Merge "Add new disabled state for "optional" built-in apps."
diff --git a/api/current.txt b/api/current.txt
index d367a00..1995416 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6627,6 +6627,7 @@
method public abstract void verifyPendingInstall(int, int);
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
+ field public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4; // 0x4
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
@@ -6673,6 +6674,7 @@
field public static final int GET_ACTIVITIES = 1; // 0x1
field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
+ field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
field public static final int GET_INTENT_FILTERS = 32; // 0x20
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 39539b4..f0e3370 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -135,6 +135,11 @@
return;
}
+ if ("disable-until-used".equals(op)) {
+ runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+ return;
+ }
+
if ("grant".equals(op)) {
runGrantRevokePermission(true);
return;
@@ -1178,6 +1183,8 @@
return "disabled";
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
return "disabled-user";
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ return "disabled-until-used";
}
return "unknown";
}
@@ -1459,6 +1466,7 @@
System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
+ System.err.println(" pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm grant PACKAGE PERMISSION");
System.err.println(" pm revoke PACKAGE PERMISSION");
System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]");
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 89b1bbd..b5349fd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1588,7 +1588,7 @@
* <ul>
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
* <li> {@link #EXTRA_CHANGED_COMPONENT_NAME_LIST} containing the class name
- * of the changed components.
+ * of the changed components (or the package name itself).
* <li> {@link #EXTRA_DONT_KILL_APP} containing boolean field to override the
* default action of restarting the application.
* </ul>
@@ -2969,7 +2969,9 @@
/**
* This field is part of {@link android.content.Intent#ACTION_PACKAGE_CHANGED},
- * and contains a string array of all of the components that have changed.
+ * and contains a string array of all of the components that have changed. If
+ * the state of the overall package has changed, then it will contain an entry
+ * with the package name itself.
*/
public static final String EXTRA_CHANGED_COMPONENT_NAME_LIST =
"android.intent.extra.changed_component_name_list";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a69f220..d80598c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -175,6 +175,14 @@
public static final int GET_CONFIGURATIONS = 0x00004000;
/**
+ * {@link PackageInfo} flag: include disabled components which are in
+ * that state only because of {@link #COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED}
+ * in the returned info. Note that if you set this flag, applications
+ * that are in this disabled state will be reported as enabled.
+ */
+ public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
+
+ /**
* Resolution and querying flag: if set, only filters that support the
* {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for
* matching. This is a synonym for including the CATEGORY_DEFAULT in your
@@ -265,6 +273,19 @@
public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3;
/**
+ * Flag for {@link #setApplicationEnabledSetting(String, int, int)} only: This
+ * application should be considered, until the point where the user actually
+ * wants to use it. This means that it will not normally show up to the user
+ * (such as in the launcher), but various parts of the user interface can
+ * use {@link #GET_DISABLED_UNTIL_USED_COMPONENTS} to still see it and allow
+ * the user to select it (as for example an IME, device admin, etc). Such code,
+ * once the user has selected the app, should at that point also make it enabled.
+ * This option currently <strong>can not</strong> be used with
+ * {@link #setComponentEnabledSetting(ComponentName, int, int)}.
+ */
+ public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4;
+
+ /**
* Flag parameter for {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} to
* indicate that this package should be installed as forward locked, i.e. only the app itself
* should have access to its code and non-resource assets.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3e8c2a8..e1887bc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3527,29 +3527,45 @@
return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId());
}
+ private static void updateApplicationInfo(ApplicationInfo ai, int flags,
+ PackageUserState state) {
+ // CompatibilityMode is global state.
+ if (!sCompatibilityModeEnabled) {
+ ai.disableCompatibilityMode();
+ }
+ if (state.installed) {
+ ai.flags |= ApplicationInfo.FLAG_INSTALLED;
+ } else {
+ ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+ }
+ if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ ai.enabled = true;
+ } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ ai.enabled = (flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
+ } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ ai.enabled = false;
+ }
+ ai.enabledSetting = state.enabled;
+ }
+
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
PackageUserState state, int userId) {
if (p == null) return null;
if (!checkUseInstalled(flags, state)) {
return null;
}
- if (!copyNeeded(flags, p, state, null, userId)) {
- // CompatibilityMode is global state. It's safe to modify the instance
- // of the package.
- if (!sCompatibilityModeEnabled) {
- p.applicationInfo.disableCompatibilityMode();
- }
- // Make sure we report as installed. Also safe to do, since the
- // default state should be installed (we will always copy if we
- // need to report it is not installed).
- p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
- if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- p.applicationInfo.enabled = true;
- } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
- p.applicationInfo.enabled = false;
- }
- p.applicationInfo.enabledSetting = state.enabled;
+ if (!copyNeeded(flags, p, state, null, userId)
+ && ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) == 0
+ || state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+ // In this case it is safe to directly modify the internal ApplicationInfo state:
+ // - CompatibilityMode is global state, so will be the same for every call.
+ // - We only come in to here if the app should reported as installed; this is the
+ // default state, and we will do a copy otherwise.
+ // - The enable state will always be reported the same for the application across
+ // calls; the only exception is for the UNTIL_USED mode, and in that case we will
+ // be doing a copy.
+ updateApplicationInfo(p.applicationInfo, flags, state);
return p.applicationInfo;
}
@@ -3565,26 +3581,12 @@
if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
ai.sharedLibraryFiles = p.usesLibraryFiles;
}
- if (!sCompatibilityModeEnabled) {
- ai.disableCompatibilityMode();
- }
if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (state.installed) {
- ai.flags |= ApplicationInfo.FLAG_INSTALLED;
- } else {
- ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
- }
- if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- ai.enabled = true;
- } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
- ai.enabled = false;
- }
- ai.enabledSetting = state.enabled;
+ updateApplicationInfo(ai, flags, state);
return ai;
}
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 20ecace..424c19b 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -153,8 +153,33 @@
public void onPackageUpdateFinished(String packageName, int uid) {
}
-
- public void onPackageChanged(String packageName, int uid, String[] components) {
+
+ /**
+ * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED
+ * Intent.ACTION_PACKAGE_CHANGED} being received, informing you of
+ * changes to the enabled/disabled state of components in a package
+ * and/or of the overall package.
+ *
+ * @param packageName The name of the package that is changing.
+ * @param uid The user ID the package runs under.
+ * @param components Any components in the package that are changing. If
+ * the overall package is changing, this will contain an entry of the
+ * package name itself.
+ * @return Return true to indicate you care about this change, which will
+ * result in {@link #onSomePackagesChanged()} being called later. If you
+ * return false, no further callbacks will happen about this change. The
+ * default implementation returns true if this is a change to the entire
+ * package.
+ */
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
+ if (components != null) {
+ for (String name : components) {
+ if (packageName.equals(name)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
@@ -189,7 +214,10 @@
*/
public void onPackageAppeared(String packageName, int reason) {
}
-
+
+ /**
+ * Called when an existing package is updated or its disabled state changes.
+ */
public void onPackageModified(String packageName) {
}
@@ -328,9 +356,10 @@
if (pkg != null) {
mModifiedPackages = mTempArray;
mTempArray[0] = pkg;
- onPackageChanged(pkg, uid, components);
- // XXX Don't want this to always cause mSomePackagesChanged,
- // since it can happen a fair amount.
+ mChangeType = PACKAGE_PERMANENT_CHANGE;
+ if (onPackageChanged(pkg, uid, components)) {
+ mSomePackagesChanged = true;
+ }
onPackageModified(pkg);
}
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 4d41e42..3d7e1ff 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -536,7 +536,7 @@
}
}
- private String getEnabledInputMethodsStr() {
+ public String getEnabledInputMethodsStr() {
mEnabledInputMethodsStrCache = Settings.Secure.getStringForUser(
mResolver, Settings.Secure.ENABLED_INPUT_METHODS, mCurrentUserId);
if (DEBUG) {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 593b9bf..0f14265 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -382,6 +382,8 @@
private boolean mInputBoundToKeyguard;
class SettingsObserver extends ContentObserver {
+ String mLastEnabled = "";
+
SettingsObserver(Handler handler) {
super(handler);
ContentResolver resolver = mContext.getContentResolver();
@@ -395,7 +397,13 @@
@Override public void onChange(boolean selfChange) {
synchronized (mMethodMap) {
- updateFromSettingsLocked();
+ boolean enabledChanged = false;
+ String newEnabled = mSettings.getEnabledInputMethodsStr();
+ if (!mLastEnabled.equals(newEnabled)) {
+ mLastEnabled = newEnabled;
+ enabledChanged = true;
+ }
+ updateFromSettingsLocked(enabledChanged);
}
}
}
@@ -539,7 +547,7 @@
}
if (changed) {
- updateFromSettingsLocked();
+ updateFromSettingsLocked(false);
}
}
}
@@ -674,7 +682,7 @@
}
mSettingsObserver = new SettingsObserver(mHandler);
- updateFromSettingsLocked();
+ updateFromSettingsLocked(true);
// IMMS wants to receive Intent.ACTION_LOCALE_CHANGED in order to update the current IME
// according to the new system locale.
@@ -748,7 +756,7 @@
// If the locale is changed, needs to reset the default ime
resetDefaultImeLocked(mContext);
}
- updateFromSettingsLocked();
+ updateFromSettingsLocked(true);
mLastSystemLocale = newLocale;
if (!updateOnlyWhenLocaleChanged) {
try {
@@ -1533,7 +1541,27 @@
return false;
}
- void updateFromSettingsLocked() {
+ void updateFromSettingsLocked(boolean enabledMayChange) {
+ if (enabledMayChange) {
+ List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
+ for (int i=0; i<enabled.size(); i++) {
+ // We allow the user to select "disabled until used" apps, so if they
+ // are enabling one of those here we now need to make it enabled.
+ InputMethodInfo imm = enabled.get(i);
+ try {
+ ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(),
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+ mSettings.getCurrentUserId());
+ if (ai.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId());
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ }
// We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
// ENABLED_INPUT_METHODS is taking care of keeping them correctly in
// sync, so we will never have a DEFAULT_INPUT_METHOD that is not
@@ -2383,7 +2411,8 @@
final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
- PackageManager.GET_META_DATA, mSettings.getCurrentUserId());
+ PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+ mSettings.getCurrentUserId());
final HashMap<String, List<InputMethodSubtype>> additionalSubtypes =
mFileManager.getAllAdditionalInputMethodSubtypes();
@@ -2429,7 +2458,7 @@
if (!map.containsKey(defaultImiId)) {
Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
if (chooseNewDefaultIMELocked()) {
- updateFromSettingsLocked();
+ updateFromSettingsLocked(true);
}
} else {
// Double check that the default IME is certainly enabled.
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 47987f1..5462ecc 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static com.android.internal.util.ArrayUtils.appendInt;
@@ -8906,13 +8907,14 @@
if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
|| newState == COMPONENT_ENABLED_STATE_ENABLED
|| newState == COMPONENT_ENABLED_STATE_DISABLED
- || newState == COMPONENT_ENABLED_STATE_DISABLED_USER)) {
+ || newState == COMPONENT_ENABLED_STATE_DISABLED_USER
+ || newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
throw new IllegalArgumentException("Invalid new component state: "
+ newState);
}
PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
- final int permission = mContext.checkCallingPermission(
+ final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
enforceCrossUserPermission(uid, userId, false, "set enabled");
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 06f11bc..e3365244 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
@@ -2412,8 +2413,14 @@
return false;
}
PackageUserState ustate = packageSettings.readUserState(userId);
+ if ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0) {
+ if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ return true;
+ }
+ }
if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
|| ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
+ || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
|| (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
&& ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
return false;
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 4b2bbfe..f9aaa17 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -364,8 +364,9 @@
}
@Override
- public void onPackageChanged(String packageName, int uid, String[] components) {
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
handlePackageUpdate(packageName);
+ return false;
}
@Override