diff options
| author | 2013-01-23 22:43:11 +0000 | |
|---|---|---|
| committer | 2013-01-23 22:43:11 +0000 | |
| commit | 846dda3fa7a194b57acdb977e443c93c7cddcea1 (patch) | |
| tree | d924a9451f3be63d2b8bd5012933b4fef4419f36 | |
| parent | 00dcbf8f58c36e3e8803336aa947a333411f43d0 (diff) | |
| parent | fd7adedebf88427162a3ce27fcc9cfd3893c869d (diff) | |
Merge "Add new disabled state for "optional" built-in apps."
11 files changed, 154 insertions, 51 deletions
diff --git a/api/current.txt b/api/current.txt index d367a00bc347..199541604d84 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6627,6 +6627,7 @@ package android.content.pm { 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 @@ package android.content.pm { 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 39539b4ec3ca..f0e337017ed2 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 @@ public final class Pm { 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 @@ public final class Pm { 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 @@ public final class Pm { 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 89b1bbd2ee4e..b5349fdc95fc 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1588,7 +1588,7 @@ public class Intent implements Parcelable, Cloneable { * <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 @@ public class Intent implements Parcelable, Cloneable { /** * 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 a69f2202ca28..d80598c2a602 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -175,6 +175,14 @@ public abstract class PackageManager { 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 abstract class PackageManager { 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 3e8c2a8572d1..e1887bc64452 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3527,29 +3527,45 @@ public class PackageParser { 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 @@ public class PackageParser { 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 20ecaceade13..424c19be6ec7 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -153,8 +153,33 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { 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 abstract class PackageMonitor extends android.content.BroadcastReceiver { */ 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 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { 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 4d41e4284999..3d7e1ffc1277 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -536,7 +536,7 @@ public class InputMethodUtils { } } - 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 593b9bf99920..0f14265e1334 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -382,6 +382,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private boolean mInputBoundToKeyguard; class SettingsObserver extends ContentObserver { + String mLastEnabled = ""; + SettingsObserver(Handler handler) { super(handler); ContentResolver resolver = mContext.getContentResolver(); @@ -395,7 +397,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (changed) { - updateFromSettingsLocked(); + updateFromSettingsLocked(false); } } } @@ -674,7 +682,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 47987f167eb1..5462ecce3193 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.GRANT_REVOKE_PERMISSIONS; 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 06f11bc9a4da..e33652442c27 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -18,6 +18,7 @@ package com.android.server.pm; 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 @@ final class Settings { 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 4b2bbfe0006b..f9aaa17ba116 100644 --- a/services/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/java/com/android/server/usb/UsbSettingsManager.java @@ -364,8 +364,9 @@ class UsbSettingsManager { } @Override - public void onPackageChanged(String packageName, int uid, String[] components) { + public boolean onPackageChanged(String packageName, int uid, String[] components) { handlePackageUpdate(packageName); + return false; } @Override |