summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHai Zhang <zhanghai@google.com>2019-01-10 14:14:22 -0800
committerHai Zhang <zhanghai@google.com>2019-01-10 15:10:01 -0800
commit48e51b103afbb9e70f83c277d9c75ef357346ea1 (patch)
treeb8fdbc7dd1346f7af13d535b0cc9cd2cf9054f3d
parent6fce37d8fe7409dab107e62369fec958cead27ca (diff)
downloadandroid_packages_apps_PackageInstaller-48e51b103afbb9e70f83c277d9c75ef357346ea1.tar.gz
android_packages_apps_PackageInstaller-48e51b103afbb9e70f83c277d9c75ef357346ea1.tar.bz2
android_packages_apps_PackageInstaller-48e51b103afbb9e70f83c277d9c75ef357346ea1.zip
Add confirmation message support for roles.
This change adds confirmation message support for roles, similar to what we had in default apps. It also added the direct boot unaware confirmation for dialer and SMS, and refactored dynamic behavior of roles to implement RoleBehavior. Bug: 110557011 Bug: 122270890 Test: manual Change-Id: I71fdb941f59b07274578b687fdf067e34fe89b98
-rw-r--r--proguard.flags4
-rw-r--r--res/values/strings.xml3
-rw-r--r--res/xml/roles.xml4
-rw-r--r--src/com/android/packageinstaller/role/model/DialerRoleBehavior.java (renamed from src/com/android/packageinstaller/role/model/DialerRoleAvailabilityProvider.java)19
-rw-r--r--src/com/android/packageinstaller/role/model/EncryptionUnawareConfirmationMixin.java54
-rw-r--r--src/com/android/packageinstaller/role/model/Role.java45
-rw-r--r--src/com/android/packageinstaller/role/model/RoleBehavior.java (renamed from src/com/android/packageinstaller/role/model/RoleAvailabilityProvider.java)25
-rw-r--r--src/com/android/packageinstaller/role/model/Roles.java23
-rw-r--r--src/com/android/packageinstaller/role/model/SmsRoleBehavior.java (renamed from src/com/android/packageinstaller/role/model/SmsRoleAvailabilityProvider.java)20
-rw-r--r--src/com/android/packageinstaller/role/ui/DefaultAppConfirmationDialogFragment.java108
-rw-r--r--src/com/android/packageinstaller/role/ui/DefaultAppFragment.java21
11 files changed, 273 insertions, 53 deletions
diff --git a/proguard.flags b/proguard.flags
index 05defe97..a2e58846 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -7,7 +7,7 @@
}
-dontwarn androidx.core.**
-# Keep classes that implements RoleAvailabilityProvider, which are used by reflection.
--keep class * implements com.android.packageinstaller.role.model.RoleAvailabilityProvider {
+# Keep classes that implements RoleBehavior, which are used by reflection.
+-keep class * implements com.android.packageinstaller.role.model.RoleBehavior {
*;
}
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b297783b..4fe36eea 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -537,4 +537,7 @@
<string name="role_label_gallery">Gallery app</string>
<!-- Label for the music player role. [CHAR LIMIT=30] -->
<string name="role_label_music">Music app</string>
+
+ <!-- [CHAR LIMIT=NONE] Dialog body explaining that the app just selected by the user will not work after a reboot until the user enters their credentials, such as a PIN or password. -->
+ <string name="encryption_unaware_confirmation_message">Note: If you restart your device and have a screen lock set, this app can\u2019t start until you unlock your device.</string>
</resources>
diff --git a/res/xml/roles.xml b/res/xml/roles.xml
index b73c4152..08664e08 100644
--- a/res/xml/roles.xml
+++ b/res/xml/roles.xml
@@ -149,7 +149,7 @@
<!--- @see android.telecom.DefaultDialerManager -->
<role
name="android.app.role.DIALER"
- availabilityProvider="DialerRoleAvailabilityProvider"
+ behavior="DialerRoleBehavior"
exclusive="true"
label="@string/role_label_dialer">
<required-components>
@@ -212,7 +212,7 @@
<!--- @see com.android.internal.telephony.SmsApplication -->
<role
name="android.app.role.SMS"
- availabilityProvider="SmsRoleAvailabilityProvider"
+ behavior="SmsRoleBehavior"
exclusive="true"
label="@string/role_label_sms">
<required-components>
diff --git a/src/com/android/packageinstaller/role/model/DialerRoleAvailabilityProvider.java b/src/com/android/packageinstaller/role/model/DialerRoleBehavior.java
index 9638bc8f..8d5daee5 100644
--- a/src/com/android/packageinstaller/role/model/DialerRoleAvailabilityProvider.java
+++ b/src/com/android/packageinstaller/role/model/DialerRoleBehavior.java
@@ -21,18 +21,29 @@ import android.os.UserHandle;
import android.telephony.TelephonyManager;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
- * Class for determining whether the dialer role is available.
+ * Class for behavior of the dialer role.
*
* @see com.android.settings.applications.DefaultAppSettings
- * @see com.android.settings.applications.defaultapps.DefaultPhonePreferenceController#isAvailable()
+ * @see com.android.settings.applications.defaultapps.DefaultPhonePreferenceController
+ * @see com.android.settings.applications.defaultapps.DefaultPhonePicker
*/
-public class DialerRoleAvailabilityProvider implements RoleAvailabilityProvider {
+public class DialerRoleBehavior implements RoleBehavior {
@Override
- public boolean isRoleAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) {
+ public boolean isAvailableAsUser(@NonNull UserHandle user,
+ @NonNull Context context) {
TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
return telephonyManager.isVoiceCapable();
}
+
+ @Nullable
+ @Override
+ public CharSequence getConfirmationMessage(@NonNull String packageName,
+ @NonNull Context context) {
+ return EncryptionUnawareConfirmationMixin.getConfirmationMessage(packageName,
+ context);
+ }
}
diff --git a/src/com/android/packageinstaller/role/model/EncryptionUnawareConfirmationMixin.java b/src/com/android/packageinstaller/role/model/EncryptionUnawareConfirmationMixin.java
new file mode 100644
index 00000000..c25101ae
--- /dev/null
+++ b/src/com/android/packageinstaller/role/model/EncryptionUnawareConfirmationMixin.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.role.model;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.packageinstaller.role.utils.PackageUtils;
+import com.android.permissioncontroller.R;
+
+/**
+ * Mixin for {@link RoleBehavior#getConfirmationMessage(String, Context)}
+ * that returns a confirmation message when the application is not direct boot aware.
+ */
+public class EncryptionUnawareConfirmationMixin {
+
+ private static final String LOG_TAG = EncryptionUnawareConfirmationMixin.class.getSimpleName();
+
+ /**
+ * @see RoleBehavior#getConfirmationMessage(String, Context)
+ */
+ @Nullable
+ public static CharSequence getConfirmationMessage(@NonNull String packageName,
+ @NonNull Context context) {
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context);
+ if (applicationInfo == null) {
+ Log.w(LOG_TAG, "Cannot get ApplicationInfo for application, package name: "
+ + packageName);
+ return null;
+ }
+ if (applicationInfo.isEncryptionAware()) {
+ return null;
+ }
+ return context.getString(R.string.encryption_unaware_confirmation_message);
+ }
+}
diff --git a/src/com/android/packageinstaller/role/model/Role.java b/src/com/android/packageinstaller/role/model/Role.java
index cfe627ea..7f2b0f8e 100644
--- a/src/com/android/packageinstaller/role/model/Role.java
+++ b/src/com/android/packageinstaller/role/model/Role.java
@@ -65,10 +65,10 @@ public class Role {
private final String mName;
/**
- * Whether this role is available in managed profile, i.e. work profile.
+ * The behavior of this role.
*/
@Nullable
- private final RoleAvailabilityProvider mAvailabilityProvider;
+ private final RoleBehavior mBehavior;
/**
* Whether this role is exclusive, i.e. allows at most one holder.
@@ -105,12 +105,12 @@ public class Role {
@NonNull
private final List<PreferredActivity> mPreferredActivities;
- public Role(@NonNull String name, @Nullable RoleAvailabilityProvider availabilityProvider,
- boolean exclusive, @StringRes int labelResource,
- @NonNull List<RequiredComponent> requiredComponents, @NonNull List<String> permissions,
- @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities) {
+ public Role(@NonNull String name, @Nullable RoleBehavior behavior, boolean exclusive,
+ @StringRes int labelResource, @NonNull List<RequiredComponent> requiredComponents,
+ @NonNull List<String> permissions, @NonNull List<AppOp> appOps,
+ @NonNull List<PreferredActivity> preferredActivities) {
mName = name;
- mAvailabilityProvider = availabilityProvider;
+ mBehavior = behavior;
mExclusive = exclusive;
mLabelResource = labelResource;
mRequiredComponents = requiredComponents;
@@ -125,8 +125,8 @@ public class Role {
}
@Nullable
- public RoleAvailabilityProvider getAvailabilityProvider() {
- return mAvailabilityProvider;
+ public RoleBehavior getBehavior() {
+ return mBehavior;
}
public boolean isExclusive() {
@@ -167,8 +167,8 @@ public class Role {
* @return whether this role is available.
*/
public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) {
- if (mAvailabilityProvider != null) {
- return mAvailabilityProvider.isRoleAvailableAsUser(user, context);
+ if (mBehavior != null) {
+ return mBehavior.isAvailableAsUser(user, context);
}
return true;
}
@@ -185,6 +185,23 @@ public class Role {
}
/**
+ * Get the confirmation message for adding an application as a holder of this role.
+ *
+ * @param packageName the package name of the application to get confirmation message for
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return the confirmation message, or {@code null} if no confirmation is needed.
+ */
+ @Nullable
+ public CharSequence getConfirmationMessage(@NonNull String packageName,
+ @NonNull Context context) {
+ if (mBehavior != null) {
+ return mBehavior.getConfirmationMessage(packageName, context);
+ }
+ return null;
+ }
+
+ /**
* Check whether a package is qualified for this role, i.e. whether it contains all the required
* components.
*
@@ -359,7 +376,7 @@ public class Role {
public String toString() {
return "Role{"
+ "mName='" + mName + '\''
- + ", mAvailabilityProvider=" + mAvailabilityProvider
+ + ", mBehavior=" + mBehavior
+ ", mExclusive=" + mExclusive
+ ", mLabelResource=" + mLabelResource
+ ", mRequiredComponents=" + mRequiredComponents
@@ -381,7 +398,7 @@ public class Role {
return mExclusive == role.mExclusive
&& mLabelResource == role.mLabelResource
&& Objects.equals(mName, role.mName)
- && Objects.equals(mAvailabilityProvider, role.mAvailabilityProvider)
+ && Objects.equals(mBehavior, role.mBehavior)
&& Objects.equals(mRequiredComponents, role.mRequiredComponents)
&& Objects.equals(mPermissions, role.mPermissions)
&& Objects.equals(mAppOps, role.mAppOps)
@@ -390,7 +407,7 @@ public class Role {
@Override
public int hashCode() {
- return Objects.hash(mName, mAvailabilityProvider, mExclusive, mLabelResource,
+ return Objects.hash(mName, mBehavior, mExclusive, mLabelResource,
mRequiredComponents, mPermissions, mAppOps, mPreferredActivities);
}
}
diff --git a/src/com/android/packageinstaller/role/model/RoleAvailabilityProvider.java b/src/com/android/packageinstaller/role/model/RoleBehavior.java
index 4c7207d5..867f047b 100644
--- a/src/com/android/packageinstaller/role/model/RoleAvailabilityProvider.java
+++ b/src/com/android/packageinstaller/role/model/RoleBehavior.java
@@ -20,19 +20,26 @@ import android.content.Context;
import android.os.UserHandle;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
- * Interface for determining whether a role is available.
+ * Interface for behavior of a role.
*/
-public interface RoleAvailabilityProvider {
+public interface RoleBehavior {
/**
- * Check whether a role is available
- *
- * @param user the user to check for
- * @param context the {@code Context} to retrieve system services
- *
- * @return Whether the role is available
+ * @see Role#isAvailableAsUser(UserHandle, Context)
*/
- boolean isRoleAvailableAsUser(@NonNull UserHandle user, @NonNull Context context);
+ default boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) {
+ return true;
+ }
+
+ /**
+ * @see Role#getConfirmationMessage(String, Context)
+ */
+ @Nullable
+ default CharSequence getConfirmationMessage(@NonNull String packageName,
+ @NonNull Context context) {
+ return null;
+ }
}
diff --git a/src/com/android/packageinstaller/role/model/Roles.java b/src/com/android/packageinstaller/role/model/Roles.java
index c8eafdd4..f72de890 100644
--- a/src/com/android/packageinstaller/role/model/Roles.java
+++ b/src/com/android/packageinstaller/role/model/Roles.java
@@ -71,7 +71,7 @@ public class Roles {
private static final String TAG_PREFERRED_ACTIVITIES = "preferred-activities";
private static final String TAG_PREFERRED_ACTIVITY = "preferred-activity";
private static final String ATTRIBUTE_NAME = "name";
- private static final String ATTRIBUTE_AVAILABILITY_PROVIDER = "availabilityProvider";
+ private static final String ATTRIBUTE_BEHAVIOR = "behavior";
private static final String ATTRIBUTE_EXCLUSIVE = "exclusive";
private static final String ATTRIBUTE_LABEL = "label";
private static final String ATTRIBUTE_PERMISSION = "permission";
@@ -293,23 +293,20 @@ public class Roles {
return null;
}
- String availabilityProviderClassSimpleName = getAttributeValue(parser,
- ATTRIBUTE_AVAILABILITY_PROVIDER);
- RoleAvailabilityProvider availabilityProvider;
- if (availabilityProviderClassSimpleName != null) {
- String availabilityProviderClassName = Roles.class.getPackage().getName() + '.'
- + availabilityProviderClassSimpleName;
+ String behaviorClassSimpleName = getAttributeValue(parser, ATTRIBUTE_BEHAVIOR);
+ RoleBehavior behavior;
+ if (behaviorClassSimpleName != null) {
+ String behaviorClassName = Roles.class.getPackage().getName() + '.'
+ + behaviorClassSimpleName;
try {
- availabilityProvider = (RoleAvailabilityProvider) Class.forName(
- availabilityProviderClassName).newInstance();
+ behavior = (RoleBehavior) Class.forName(behaviorClassName).newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
- throwOrLogMessage("Unable to instantiate availability provider: "
- + availabilityProviderClassName, e);
+ throwOrLogMessage("Unable to instantiate behavior: " + behaviorClassName, e);
skipCurrentTag(parser);
return null;
}
} else {
- availabilityProvider = null;
+ behavior = null;
}
Boolean exclusive = requireAttributeBooleanValue(parser, ATTRIBUTE_EXCLUSIVE, true,
@@ -385,7 +382,7 @@ public class Roles {
if (preferredActivities == null) {
preferredActivities = Collections.emptyList();
}
- return new Role(name, availabilityProvider, exclusive, labelResource, requiredComponents,
+ return new Role(name, behavior, exclusive, labelResource, requiredComponents,
permissions, appOps, preferredActivities);
}
diff --git a/src/com/android/packageinstaller/role/model/SmsRoleAvailabilityProvider.java b/src/com/android/packageinstaller/role/model/SmsRoleBehavior.java
index d4d82427..cae9d945 100644
--- a/src/com/android/packageinstaller/role/model/SmsRoleAvailabilityProvider.java
+++ b/src/com/android/packageinstaller/role/model/SmsRoleBehavior.java
@@ -22,17 +22,21 @@ import android.os.UserManager;
import android.telephony.TelephonyManager;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
- * Class for determining whether the SMS role is available.
+ * Class for behavior of the SMS role.
*
* @see com.android.settings.applications.DefaultAppSettings
- * @see com.android.settings.applications.defaultapps.DefaultSmsPreferenceController#isAvailable()
+ * @see com.android.settings.applications.defaultapps.DefaultSmsPreferenceController
+ * @see com.android.settings.applications.defaultapps.DefaultSmsPicker
+ *
*/
-public class SmsRoleAvailabilityProvider implements RoleAvailabilityProvider {
+public class SmsRoleBehavior implements RoleBehavior {
@Override
- public boolean isRoleAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) {
+ public boolean isAvailableAsUser(@NonNull UserHandle user,
+ @NonNull Context context) {
UserManager userManager = context.getSystemService(UserManager.class);
if (userManager.isManagedProfile(user.getIdentifier())) {
return false;
@@ -47,4 +51,12 @@ public class SmsRoleAvailabilityProvider implements RoleAvailabilityProvider {
}
return true;
}
+
+ @Nullable
+ @Override
+ public CharSequence getConfirmationMessage(@NonNull String packageName,
+ @NonNull Context context) {
+ return EncryptionUnawareConfirmationMixin.getConfirmationMessage(packageName,
+ context);
+ }
}
diff --git a/src/com/android/packageinstaller/role/ui/DefaultAppConfirmationDialogFragment.java b/src/com/android/packageinstaller/role/ui/DefaultAppConfirmationDialogFragment.java
new file mode 100644
index 00000000..69bd71f1
--- /dev/null
+++ b/src/com/android/packageinstaller/role/ui/DefaultAppConfirmationDialogFragment.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.role.ui;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+
+/**
+ * {@link DialogFragment} for confirmation before setting a default app.
+ */
+public class DefaultAppConfirmationDialogFragment extends DialogFragment {
+
+ private String mPackageName;
+ private CharSequence mMessage;
+
+ /**
+ * Create a new instance of this fragment.
+ *
+ * @param packageName the package name of the application
+ * @param message the confirmation message
+ *
+ * @return a new instance of this fragment
+ *
+ * @see #show(String, CharSequence, Fragment)
+ */
+ @NonNull
+ public static DefaultAppConfirmationDialogFragment newInstance(@NonNull String packageName,
+ @NonNull CharSequence message) {
+ DefaultAppConfirmationDialogFragment fragment = new DefaultAppConfirmationDialogFragment();
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ arguments.putCharSequence(Intent.EXTRA_TEXT, message);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ /**
+ * Show a new instance of this fragment.
+ *
+ * @param packageName the package name of the application
+ * @param message the confirmation message
+ * @param fragment the parent fragment
+ *
+ * @see #newInstance(String, CharSequence)
+ */
+ public static void show(@NonNull String packageName, @NonNull CharSequence message,
+ @NonNull Fragment fragment) {
+ newInstance(packageName, message).show(fragment.getChildFragmentManager(), null);
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle arguments = getArguments();
+ mPackageName = arguments.getString(Intent.EXTRA_PACKAGE_NAME);
+ mMessage = arguments.getCharSequence(Intent.EXTRA_TEXT);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ return new AlertDialog.Builder(requireContext(), getTheme())
+ .setMessage(mMessage)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> onOk())
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+ }
+
+ private void onOk() {
+ Listener listener = (Listener) getParentFragment();
+ listener.setDefaultApp(mPackageName);
+ }
+
+ /**
+ * Listener for {@link DefaultAppConfirmationDialogFragment}.
+ */
+ public interface Listener {
+
+ /**
+ * Set an application as the default app.
+ *
+ * @param packageName the package name of the application
+ */
+ void setDefaultApp(@NonNull String packageName);
+ }
+}
diff --git a/src/com/android/packageinstaller/role/ui/DefaultAppFragment.java b/src/com/android/packageinstaller/role/ui/DefaultAppFragment.java
index ddf28a30..d97ed1e9 100644
--- a/src/com/android/packageinstaller/role/ui/DefaultAppFragment.java
+++ b/src/com/android/packageinstaller/role/ui/DefaultAppFragment.java
@@ -46,7 +46,8 @@ import java.util.List;
* Fragment for a default app.
*/
public class DefaultAppFragment extends SettingsFragment
- implements Preference.OnPreferenceClickListener {
+ implements DefaultAppConfirmationDialogFragment.Listener,
+ Preference.OnPreferenceClickListener {
private static final String LOG_TAG = DefaultAppFragment.class.getSimpleName();
@@ -173,14 +174,24 @@ public class DefaultAppFragment extends SettingsFragment
@Override
public boolean onPreferenceClick(@NonNull Preference preference) {
+ String packageName = preference.getKey();
+ CharSequence confirmationMessage = mRole.getConfirmationMessage(packageName,
+ requireContext());
+ if (confirmationMessage != null) {
+ DefaultAppConfirmationDialogFragment.show(packageName, confirmationMessage, this);
+ } else {
+ setDefaultApp(packageName);
+ }
+ return true;
+ }
+
+ @Override
+ public void setDefaultApp(@NonNull String packageName) {
ManageRoleHolderStateLiveData liveData = mViewModel.getManageRoleHolderStateLiveData();
if (liveData.getValue() != ManageRoleHolderStateLiveData.STATE_IDLE) {
Log.i(LOG_TAG, "Trying to set default app while another request is on-going");
- return true;
+ return;
}
-
- String packageName = preference.getKey();
liveData.manageRoleHolderAsUser(mRoleName, packageName, mUser, true, requireContext());
- return true;
}
}