diff options
Diffstat (limited to 'core/java')
46 files changed, 1139 insertions, 724 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f6883e2..9132883 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -929,7 +929,8 @@ public class Activity extends ContextThemeWrapper /** * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with - * the attribute {@link android.R.attr#persistable} set true. + * the attribute {@link android.R.attr#persistableMode} set to + * <code>persistAcrossReboots</code>. * * @param savedInstanceState if the activity is being re-initialized after * previously being shut down then this Bundle contains the data it most @@ -1012,8 +1013,9 @@ public class Activity extends ContextThemeWrapper /** * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities - * created with the attribute {@link android.R.attr#persistable}. The {@link - * android.os.PersistableBundle} passed came from the restored PersistableBundle first + * created with the attribute {@link android.R.attr#persistableMode} set to + * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed + * came from the restored PersistableBundle first * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}. * * <p>This method is called between {@link #onStart} and @@ -1111,7 +1113,8 @@ public class Activity extends ContextThemeWrapper /** * This is the same as {@link #onPostCreate(Bundle)} but is called for activities - * created with the attribute {@link android.R.attr#persistable}. + * created with the attribute {@link android.R.attr#persistableMode} set to + * <code>persistAcrossReboots</code>. * * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState} * @param persistentState The data caming from the PersistableBundle first @@ -1352,10 +1355,10 @@ public class Activity extends ContextThemeWrapper /** * This is the same as {@link #onSaveInstanceState} but is called for activities - * created with the attribute {@link android.R.attr#persistable}. The {@link - * android.os.PersistableBundle} passed in will be saved and presented in - * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity - * is restarted following the next device reboot. + * created with the attribute {@link android.R.attr#persistableMode} set to + * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed + * in will be saved and presented in {@link #onCreate(Bundle, PersistableBundle)} + * the first time that this activity is restarted following the next device reboot. * * @param outState Bundle in which to place your saved state. * @param outPersistentState State which will be saved across reboots. @@ -5320,13 +5323,15 @@ public class Activity extends ContextThemeWrapper * drawn and it is safe to make this Activity translucent again. * @param options activity options delivered to the activity below this one. The options * are retrieved using {@link #getActivityOptions}. + * @return <code>true</code> if Window was opaque and will become translucent or + * <code>false</code> if window was translucent and no change needed to be made. * * @see #convertFromTranslucent() * @see TranslucentConversionListener * * @hide */ - public void convertToTranslucent(TranslucentConversionListener callback, + public boolean convertToTranslucent(TranslucentConversionListener callback, ActivityOptions options) { boolean drawComplete; try { @@ -5343,6 +5348,7 @@ public class Activity extends ContextThemeWrapper // Window is already translucent. mTranslucentCallback.onTranslucentConversionComplete(drawComplete); } + return mChangeCanvasToTranslucent; } /** @hide */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b8f2089..f5514f8 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -319,7 +319,7 @@ public final class ActivityThread { } public boolean isPersistable() { - return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0; + return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS; } public String toString() { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index de0396e..5e17e1a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1457,10 +1457,10 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId) { + public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, + int flags) { try { - mPM.addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); + mPM.addCrossProfileIntentFilter(filter, sourceUserId, targetUserId, flags); } catch (RemoteException e) { // Should never happen! } @@ -1470,15 +1470,6 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId, - int targetUserId) { - addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); - } - - /** - * @hide - */ - @Override public void clearCrossProfileIntentFilters(int sourceUserId) { try { mPM.clearCrossProfileIntentFilters(sourceUserId); @@ -1487,14 +1478,6 @@ final class ApplicationPackageManager extends PackageManager { } } - /** - * @hide - */ - @Override - public void clearForwardingIntentFilters(int sourceUserId) { - clearCrossProfileIntentFilters(sourceUserId); - } - private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index ef4099f..5998d7a 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -1184,6 +1184,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInt(level); mRemote.transact(SCHEDULE_TRIM_MEMORY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); + data.recycle(); } public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 3e7d9b4..425a140 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -513,6 +513,9 @@ class ContextImpl extends Context { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(POWER_SERVICE); IPowerManager service = IPowerManager.Stub.asInterface(b); + if (service == null) { + Log.wtf(TAG, "Failed to get power manager service."); + } return new PowerManager(ctx.getOuterContext(), service, ctx.mMainThread.getHandler()); }}); @@ -694,8 +697,8 @@ class ContextImpl extends Context { registerService(FINGERPRINT_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { - IBinder b = ServiceManager.getService(FINGERPRINT_SERVICE); - IFingerprintService service = IFingerprintService.Stub.asInterface(b); + IBinder binder = ServiceManager.getService(FINGERPRINT_SERVICE); + IFingerprintService service = IFingerprintService.Stub.asInterface(binder); return new FingerprintManager(ctx.getOuterContext(), service); } }); diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 365cc8e..1d7a0ec 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -55,6 +55,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private boolean mIsExitTransitionComplete; private boolean mIsReadyForTransition; private Bundle mSharedElementsBundle; + private boolean mWasOpaque; public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, ArrayList<String> sharedElementNames, boolean isReturning) { @@ -191,7 +192,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { protected void prepareEnter() { mActivity.overridePendingTransition(0, 0); if (!mIsReturning) { - mActivity.convertToTranslucent(null, null); + mWasOpaque = mActivity.convertToTranslucent(null, null); Drawable background = getDecor().getBackground(); if (background != null) { getWindow().setBackgroundDrawable(null); @@ -376,7 +377,9 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private void makeOpaque() { if (!mHasStopped && mActivity != null) { - mActivity.convertFromTranslucent(); + if (mWasOpaque) { + mActivity.convertFromTranslucent(); + } mActivity = null; } } diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 45a2625..ca40436 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -166,6 +166,40 @@ public class DeviceAdminReceiver extends BroadcastReceiver { = "android.app.action.ACTION_PASSWORD_EXPIRING"; /** + * Action sent to a device administrator to notify that the device is entering + * or exiting lock task mode from an authorized package. The extra + * {@link #EXTRA_LOCK_TASK_ENTERING} will describe whether entering or exiting + * the mode. If entering, the extra {@link #EXTRA_LOCK_TASK_PACKAGE} will describe + * the authorized package using lock task mode. + * + * @see DevicePolicyManager#isLockTaskPermitted + * + * <p>The calling device admin must be the device owner or profile + * owner to receive this broadcast. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LOCK_TASK_CHANGED + = "android.app.action.ACTION_LOCK_TASK_CHANGED"; + + /** + * A boolean describing whether the device is currently entering or exiting + * lock task mode. + * + * @see #ACTION_LOCK_TASK_CHANGED + */ + public static final String EXTRA_LOCK_TASK_ENTERING = + "android.app.extra.LOCK_TASK_ENTERING"; + + /** + * A boolean describing whether the device is currently entering or exiting + * lock task mode. + * + * @see #ACTION_LOCK_TASK_CHANGED + */ + public static final String EXTRA_LOCK_TASK_PACKAGE = + "android.app.extra.LOCK_TASK_PACKAGE"; + + /** * Broadcast Action: This broadcast is sent to indicate that provisioning of a managed profile * or managed device has completed successfully. * @@ -341,6 +375,19 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } /** + * Called when a device is entering or exiting lock task mode by a package + * authorized by {@link DevicePolicyManager#isLockTaskPermitted(String)} + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param isEnteringLockTask Whether the device is entering or exiting lock task mode. + * @param pkg If entering, the authorized package using lock task mode, otherwise null. + */ + public void onLockTaskModeChanged(Context context, Intent intent, boolean isEnteringLockTask, + String pkg) { + } + + /** * Intercept standard device administrator broadcasts. Implementations * should not override this method; it is better to implement the * convenience callbacks for each action. @@ -369,6 +416,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver { onPasswordExpiring(context, intent); } else if (ACTION_PROFILE_PROVISIONING_COMPLETE.equals(action)) { onProfileProvisioningComplete(context, intent); + } else if (ACTION_LOCK_TASK_CHANGED.equals(action)) { + boolean isEntering = intent.getBooleanExtra(EXTRA_LOCK_TASK_ENTERING, false); + String pkg = intent.getStringExtra(EXTRA_LOCK_TASK_PACKAGE); + onLockTaskModeChanged(context, intent, isEntering, pkg); } } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e80c761..df6be8b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -18,6 +18,7 @@ package android.app.admin; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -1506,12 +1507,11 @@ public class DevicePolicyManager { * * @return false if the certBuffer cannot be parsed or installation is * interrupted, otherwise true - * @hide */ - public boolean installCaCert(byte[] certBuffer) { + public boolean installCaCert(ComponentName who, byte[] certBuffer) { if (mService != null) { try { - return mService.installCaCert(certBuffer); + return mService.installCaCert(who, certBuffer); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1521,13 +1521,14 @@ public class DevicePolicyManager { /** * Uninstalls the given certificate from the list of User CAs, if present. - * - * @hide */ - public void uninstallCaCert(byte[] certBuffer) { + public void uninstallCaCert(ComponentName who, byte[] certBuffer) { if (mService != null) { try { - mService.uninstallCaCert(certBuffer); + final String alias = getCaCertAlias(certBuffer); + mService.uninstallCaCert(who, alias); + } catch (CertificateException e) { + Log.w(TAG, "Unable to parse certificate", e); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1536,10 +1537,8 @@ public class DevicePolicyManager { /** * Returns whether there are any user-installed CA certificates. - * - * @hide */ - public static boolean hasAnyCaCertsInstalled() { + public boolean hasAnyCaCertsInstalled() { TrustedCertificateStore certStore = new TrustedCertificateStore(); Set<String> aliases = certStore.userAliases(); return aliases != null && !aliases.isEmpty(); @@ -1547,18 +1546,10 @@ public class DevicePolicyManager { /** * Returns whether this certificate has been installed as a User CA. - * - * @hide */ public boolean hasCaCertInstalled(byte[] certBuffer) { - TrustedCertificateStore certStore = new TrustedCertificateStore(); - String alias; - byte[] pemCert; try { - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - X509Certificate cert = (X509Certificate) certFactory.generateCertificate( - new ByteArrayInputStream(certBuffer)); - return certStore.getCertificateAlias(cert) != null; + return getCaCertAlias(certBuffer) != null; } catch (CertificateException ce) { Log.w(TAG, "Could not parse certificate", ce); } @@ -1566,6 +1557,17 @@ public class DevicePolicyManager { } /** + * Returns the alias of a given CA certificate in the certificate store, or null if it + * doesn't exist. + */ + private static String getCaCertAlias(byte[] certBuffer) throws CertificateException { + final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + final X509Certificate cert = (X509Certificate) certFactory.generateCertificate( + new ByteArrayInputStream(certBuffer)); + return new TrustedCertificateStore().getCertificateAlias(cert); + } + + /** * Called by an application that is administering the device to disable all cameras * on the device. After setting this, no applications will be able to access any cameras * on the device. @@ -1839,11 +1841,13 @@ public class DevicePolicyManager { * This function should be used cautiously as once it is called it cannot * be undone. The device owner can only be set as a part of device setup * before setup completes. + * + * @param packageName The package name of the device owner. */ - public void clearDeviceOwnerApp() { + public void clearDeviceOwnerApp(String packageName) { if (mService != null) { try { - mService.clearDeviceOwner(mContext.getPackageName()); + mService.clearDeviceOwner(packageName); } catch (RemoteException re) { Log.w(TAG, "Failed to clear device owner"); } @@ -2340,15 +2344,20 @@ public class DevicePolicyManager { } /** - * Sets which components may enter lock task mode. + * Sets which packages may enter lock task mode. + * + * <p>Any packages that shares uid with an allowed package will also be allowed + * to activate lock task. * * This function can only be called by the device owner or the profile owner. - * @param components The list of components allowed to enter lock task mode + * @param packages The list of packages allowed to enter lock task mode + * + * @see Activity#startLockTask() */ - public void setLockTaskComponents(ComponentName[] components) throws SecurityException { + public void setLockTaskPackages(String[] packages) throws SecurityException { if (mService != null) { try { - mService.setLockTaskComponents(components); + mService.setLockTaskPackages(packages); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -2356,13 +2365,13 @@ public class DevicePolicyManager { } /** - * This function returns the list of components allowed to start the lock task mode. + * This function returns the list of packages allowed to start the lock task mode. * @hide */ - public ComponentName[] getLockTaskComponents() { + public String[] getLockTaskPackages() { if (mService != null) { try { - return mService.getLockTaskComponents(); + return mService.getLockTaskPackages(); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -2373,12 +2382,12 @@ public class DevicePolicyManager { /** * This function lets the caller know whether the given component is allowed to start the * lock task mode. - * @param component The component to check + * @param pkg The package to check */ - public boolean isLockTaskPermitted(ComponentName component) { + public boolean isLockTaskPermitted(String pkg) { if (mService != null) { try { - return mService.isLockTaskPermitted(component); + return mService.isLockTaskPermitted(pkg); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index a1caa21..5333ea6 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -115,8 +115,8 @@ interface IDevicePolicyManager { String getProfileOwnerName(int userHandle); void setProfileEnabled(in ComponentName who); - boolean installCaCert(in byte[] certBuffer); - void uninstallCaCert(in byte[] certBuffer); + boolean installCaCert(in ComponentName admin, in byte[] certBuffer); + void uninstallCaCert(in ComponentName admin, in String alias); void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity); void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName); @@ -142,13 +142,15 @@ interface IDevicePolicyManager { void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled); String[] getAccountTypesWithManagementDisabled(); - void setLockTaskComponents(in ComponentName[] components); - ComponentName[] getLockTaskComponents(); - boolean isLockTaskPermitted(in ComponentName component); + void setLockTaskPackages(in String[] packages); + String[] getLockTaskPackages(); + boolean isLockTaskPermitted(in String pkg); void setGlobalSetting(in ComponentName who, in String setting, in String value); void setSecureSetting(in ComponentName who, in String setting, in String value); void setMasterVolumeMuted(in ComponentName admin, boolean on); boolean isMasterVolumeMuted(in ComponentName admin); + + void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userId); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 6e53a6f..3dfa78b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -7375,6 +7375,7 @@ public class Intent implements Parcelable, Cloneable { for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) { out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx)); } + out.endTag(null, TAG_CATEGORIES); } } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 791e5aa..abc8cde 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -104,6 +104,28 @@ public class ActivityInfo extends ComponentInfo public int documentLaunchMode; /** + * Constant corresponding to <code>persistRootOnly</code> in + * the {@link android.R.attr#persistableMode} attribute. + */ + public static final int PERSIST_ROOT_ONLY = 0; + /** + * Constant corresponding to <code>doNotPersist</code> in + * the {@link android.R.attr#persistableMode} attribute. + */ + public static final int DO_NOT_PERSIST = 1; + /** + * Constant corresponding to <code>persistAcrossReboots</code> in + * the {@link android.R.attr#persistableMode} attribute. + */ + public static final int PERSIST_ACROSS_REBOOTS = 2; + /** + * Value indicating how this activity is to be persisted across + * reboots for restoring in the Recents list. + * {@link android.R.attr#persistableMode} + */ + public int persistableMode; + + /** * The maximum number of tasks rooted at this activity that can be in the recent task list. * Refer to {@link android.R.attr#maxRecents}. */ @@ -230,12 +252,6 @@ public class ActivityInfo extends ComponentInfo */ public static final int FLAG_IMMERSIVE = 0x0800; /** - * Bit in {@link #flags} indicating that this activity is to be persisted across - * reboots for display in the Recents list. - * {@link android.R.attr#persistable} - */ - public static final int FLAG_PERSISTABLE = 0x1000; - /** * Bit in {@link #flags} indicating that tasks started with this activity are to be * removed from the recent list of tasks when the last activity in the task is finished. * {@link android.R.attr#autoRemoveFromRecents} @@ -641,13 +657,23 @@ public class ActivityInfo extends ComponentInfo return theme != 0 ? theme : applicationInfo.theme; } + private String persistableModeToString() { + switch(persistableMode) { + case PERSIST_ROOT_ONLY: return "PERSIST_ROOT_ONLY"; + case DO_NOT_PERSIST: return "DO_NOT_PERSIST"; + case PERSIST_ACROSS_REBOOTS: return "PERSIST_ACROSS_REBOOTS"; + default: return "UNKNOWN=" + persistableMode; + } + } + public void dump(Printer pw, String prefix) { super.dumpFront(pw, prefix); if (permission != null) { pw.println(prefix + "permission=" + permission); } pw.println(prefix + "taskAffinity=" + taskAffinity - + " targetActivity=" + targetActivity); + + " targetActivity=" + targetActivity + + " persistableMode=" + persistableModeToString()); if (launchMode != 0 || flags != 0 || theme != 0) { pw.println(prefix + "launchMode=" + launchMode + " flags=0x" + Integer.toHexString(flags) @@ -688,6 +714,7 @@ public class ActivityInfo extends ComponentInfo dest.writeInt(softInputMode); dest.writeInt(uiOptions); dest.writeString(parentActivityName); + dest.writeInt(persistableMode); } public static final Parcelable.Creator<ActivityInfo> CREATOR @@ -713,5 +740,6 @@ public class ActivityInfo extends ComponentInfo softInputMode = source.readInt(); uiOptions = source.readInt(); parentActivityName = source.readString(); + persistableMode = source.readInt(); } } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 06f4019..be4e864 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -325,6 +325,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_IS_GAME = 1<<25; /** + * Value for {@link #flags}: {@code true} if the application asks that only + * full-data streaming backups of its data be performed even though it defines + * a {@link android.app.backup.BackupAgent BackupAgent}, which normally + * indicates that the app will manage its backed-up data via incremental + * key/value updates. + */ + public static final int FLAG_FULL_BACKUP_ONLY = 1<<26; + + /** * Value for {@link #flags}: set to {@code true} if the application * is permitted to hold privileged permissions. * diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 70668e1..00e7918 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -248,8 +248,8 @@ interface IPackageManager { void clearPackagePersistentPreferredActivities(String packageName, int userId); - void addCrossProfileIntentFilter(in IntentFilter filter, boolean removable, int sourceUserId, - int targetUserId); + void addCrossProfileIntentFilter(in IntentFilter intentFilter, int sourceUserId, int targetUserId, + int flags); void clearCrossProfileIntentFilters(int sourceUserId); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 69fa408..6c10bb8 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -57,6 +57,65 @@ public class LauncherApps { private List<OnAppsChangedListener> mListeners = new ArrayList<OnAppsChangedListener>(); + private List<OnAppsChangedCallback> mCallbacks + = new ArrayList<OnAppsChangedCallback>(); + + /** + * Callbacks for package changes to this and related managed profiles. + */ + public static abstract class OnAppsChangedCallback { + /** + * Indicates that a package was removed from the specified profile. + * + * @param packageName The name of the package that was removed. + * @param user The UserHandle of the profile that generated the change. + */ + abstract public void onPackageRemoved(String packageName, UserHandle user); + + /** + * Indicates that a package was added to the specified profile. + * + * @param packageName The name of the package that was added. + * @param user The UserHandle of the profile that generated the change. + */ + abstract public void onPackageAdded(String packageName, UserHandle user); + + /** + * Indicates that a package was modified in the specified profile. + * + * @param packageName The name of the package that has changed. + * @param user The UserHandle of the profile that generated the change. + */ + abstract public void onPackageChanged(String packageName, UserHandle user); + + /** + * Indicates that one or more packages have become available. For + * example, this can happen when a removable storage card has + * reappeared. + * + * @param packageNames The names of the packages that have become + * available. + * @param user The UserHandle of the profile that generated the change. + * @param replacing Indicates whether these packages are replacing + * existing ones. + */ + abstract public void onPackagesAvailable(String[] packageNames, UserHandle user, + boolean replacing); + + /** + * Indicates that one or more packages have become unavailable. For + * example, this can happen when a removable storage card has been + * removed. + * + * @param packageNames The names of the packages that have become + * unavailable. + * @param user The UserHandle of the profile that generated the change. + * @param replacing Indicates whether the packages are about to be + * replaced with new versions. + */ + abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user, + boolean replacing); + } /** * Callbacks for package changes to this and related managed profiles. @@ -270,7 +329,7 @@ public class LauncherApps { synchronized (this) { if (listener != null && !mListeners.contains(listener)) { mListeners.add(listener); - if (mListeners.size() == 1) { + if (mListeners.size() == 1 && mCallbacks.size() == 0) { try { mService.addOnAppsChangedListener(mAppsChangedListener); } catch (RemoteException re) { @@ -289,7 +348,44 @@ public class LauncherApps { public void removeOnAppsChangedListener(OnAppsChangedListener listener) { synchronized (this) { mListeners.remove(listener); - if (mListeners.size() == 0) { + if (mListeners.size() == 0 && mCallbacks.size() == 0) { + try { + mService.removeOnAppsChangedListener(mAppsChangedListener); + } catch (RemoteException re) { + } + } + } + } + + /** + * Adds a callback for changes to packages in current and managed profiles. + * + * @param callback The callback to add. + */ + public void addOnAppsChangedCallback(OnAppsChangedCallback callback) { + synchronized (this) { + if (callback != null && !mCallbacks.contains(callback)) { + mCallbacks.add(callback); + if (mCallbacks.size() == 1 && mListeners.size() == 0) { + try { + mService.addOnAppsChangedListener(mAppsChangedListener); + } catch (RemoteException re) { + } + } + } + } + } + + /** + * Removes a callback that was previously added. + * + * @param callback The callback to remove. + * @see #addOnAppsChangedListener(OnAppsChangedCallback) + */ + public void removeOnAppsChangedCallback(OnAppsChangedCallback callback) { + synchronized (this) { + mListeners.remove(callback); + if (mListeners.size() == 0 && mCallbacks.size() == 0) { try { mService.removeOnAppsChangedListener(mAppsChangedListener); } catch (RemoteException re) { @@ -309,6 +405,9 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackageRemoved(user, packageName); } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackageRemoved(packageName, user); + } } } @@ -321,6 +420,9 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackageChanged(user, packageName); } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackageChanged(packageName, user); + } } } @@ -333,6 +435,9 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackageAdded(user, packageName); } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackageAdded(packageName, user); + } } } @@ -346,6 +451,9 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackagesAvailable(user, packageNames, replacing); } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackagesAvailable(packageNames, user, replacing); + } } } @@ -359,7 +467,10 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackagesUnavailable(user, packageNames, replacing); } - } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackagesUnavailable(packageNames, user, replacing); + } + } } }; } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 8d9b8d9..b5ceebe 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -196,6 +196,22 @@ public abstract class PackageManager { */ public static final int MATCH_DEFAULT_ONLY = 0x00010000; + /** + * Flag for {@link addCrossProfileIntentFilter}: if the cross-profile intent has been set by the + * profile owner. + * @hide + */ + public static final int SET_BY_PROFILE_OWNER= 0x00000001; + + /** + * Flag for {@link addCrossProfileIntentFilter}: if this flag is set: + * when resolving an intent that matches the {@link CrossProfileIntentFilter}, the current + * profile will be skipped. + * Only activities in the target user can respond to the intent. + * @hide + */ + public static final int SKIP_CURRENT_PROFILE = 0x00000002; + /** @hide */ @IntDef({PERMISSION_GRANTED, PERMISSION_DENIED}) @Retention(RetentionPolicy.SOURCE) @@ -2870,15 +2886,12 @@ public abstract class PackageManager { * */ public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) { - PackageParser packageParser = new PackageParser(archiveFilePath); - DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - final File sourceFile = new File(archiveFilePath); + final PackageParser parser = new PackageParser(); + final File apkFile = new File(archiveFilePath); try { - PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, - 0); + PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0); if ((flags & GET_SIGNATURES) != 0) { - packageParser.collectCertificates(pkg, 0); + parser.collectCertificates(pkg, 0); } PackageUserState state = new PackageUserState(); return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state); @@ -3586,30 +3599,14 @@ public abstract class PackageManager { * {@link CrossProfileIntentFilter} * @hide */ - public abstract void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId); + public abstract void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, + int targetUserId, int flags); /** - * @hide - * @deprecated - * TODO: remove it as soon as the code of ManagedProvisionning is updated - */ - public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId); - - /** - * Clearing removable {@link CrossProfileIntentFilter}s which have the specified user as their - * source + * Clearing {@link CrossProfileIntentFilter}s which have the specified user as their + * source, and have been set by the profile owner * @param sourceUserId - * be cleared. * @hide */ public abstract void clearCrossProfileIntentFilters(int sourceUserId); - - /** - * @hide - * @deprecated - * TODO: remove it as soon as the code of ManagedProvisionning is updated - */ - public abstract void clearForwardingIntentFilters(int sourceUserId); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 91895ff..546f3a5 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -16,9 +16,14 @@ package android.content.pm; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; import android.content.ComponentName; import android.content.Intent; @@ -33,6 +38,8 @@ import android.os.Bundle; import android.os.PatternMatcher; import android.os.UserHandle; import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AttributeSet; import android.util.Base64; import android.util.DisplayMetrics; @@ -41,12 +48,18 @@ import android.util.Pair; import android.util.Slog; import android.util.TypedValue; -import java.io.BufferedInputStream; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.XmlUtils; + +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.lang.ref.WeakReference; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; @@ -58,21 +71,19 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.jar.StrictJarFile; import java.util.zip.ZipEntry; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.XmlUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - /** * Package archive parsing * @@ -153,17 +164,21 @@ public class PackageParser { android.os.Build.VERSION_CODES.JELLY_BEAN) }; + /** + * @deprecated callers should move to explicitly passing around source path. + */ + @Deprecated private String mArchiveSourcePath; + private String[] mSeparateProcesses; private boolean mOnlyCoreApps; + private DisplayMetrics mMetrics; + private static final int SDK_VERSION = Build.VERSION.SDK_INT; private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; private int mParseError = PackageManager.INSTALL_SUCCEEDED; - private static final Object mSync = new Object(); - private static WeakReference<byte[]> mReadBuffer; - private static boolean sCompatibilityModeEnabled = true; private static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; @@ -247,12 +262,9 @@ public class PackageParser { private static final String TAG = "PackageParser"; - public PackageParser(String archiveSourcePath) { - mArchiveSourcePath = archiveSourcePath; - } - - public PackageParser(File archiveSource) { - this(archiveSource.getAbsolutePath()); + public PackageParser() { + mMetrics = new DisplayMetrics(); + mMetrics.setToDefaults(); } public void setSeparateProcesses(String[] procs) { @@ -263,6 +275,10 @@ public class PackageParser { mOnlyCoreApps = onlyCoreApps; } + public void setDisplayMetrics(DisplayMetrics metrics) { + mMetrics = metrics; + } + private static final boolean isPackageFilename(File file) { return isPackageFilename(file.getName()); } @@ -480,23 +496,21 @@ public class PackageParser { return pi; } - private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je, - byte[] readBuffer) { + private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry) + throws PackageParserException { + InputStream is = null; try { // We must read the stream for the JarEntry to retrieve // its certificates. - InputStream is = new BufferedInputStream(jarFile.getInputStream(je)); - while (is.read(readBuffer, 0, readBuffer.length) != -1) { - // not using - } - is.close(); - return je != null ? jarFile.getCertificateChains(je) : null; - } catch (IOException e) { - Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e); - } catch (RuntimeException e) { - Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e); + is = jarFile.getInputStream(entry); + readFullyIgnoringContents(is); + return jarFile.getCertificateChains(entry); + } catch (IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed reading " + entry.getName() + " in " + jarFile, e); + } finally { + IoUtils.closeQuietly(is); } - return null; } public final static int PARSE_IS_SYSTEM = 1<<0; @@ -508,67 +522,116 @@ public class PackageParser { public final static int PARSE_IS_SYSTEM_DIR = 1<<6; public final static int PARSE_IS_PRIVILEGED = 1<<7; public final static int PARSE_GET_SIGNATURES = 1<<8; + public final static int PARSE_TRUSTED_OVERLAY = 1<<9; + + private static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); + + /** + * Used to sort a set of APKs based on their split names, always placing the + * base APK (with {@code null} split name) first. + */ + private static class SplitNameComparator implements Comparator<String> { + @Override + public int compare(String lhs, String rhs) { + if (lhs == null) { + return -1; + } else if (rhs == null) { + return 1; + } else { + return lhs.compareTo(rhs); + } + } + } /** - * Parse all APK files under the given directory as a single package. + * Parse all APKs contained in the given directory, treating them as a + * single package. This also performs sanity checking, such as requiring + * identical package name and version codes, a single base APK, and unique + * split names. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(Package, int)}. */ - public Package parseSplitPackage(File apkDir, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { + public Package parseSplitPackage(File apkDir, int flags) throws PackageParserException { final File[] files = apkDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split"); } - File baseFile = null; + String packageName = null; + int versionCode = 0; + + final ArrayMap<String, File> apks = new ArrayMap<>(); for (File file : files) { if (file.isFile() && isPackageFilename(file)) { - ApkLite lite = parseApkLite(file.getAbsolutePath(), 0); - if (lite == null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + file); + final ApkLite lite = parseApkLite(file, 0); + + // Assert that all package names and version codes are + // consistent with the first one we encounter. + if (packageName == null) { + packageName = lite.packageName; + versionCode = lite.versionCode; + } else { + if (!packageName.equals(lite.packageName)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Inconsistent package " + lite.packageName + " in " + file + + "; expected " + packageName); + } + if (versionCode != lite.versionCode) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Inconsistent version " + lite.versionCode + " in " + file + + "; expected " + versionCode); + } } - if (TextUtils.isEmpty(lite.splitName)) { - baseFile = file; - break; + // Assert that each split is defined only once + if (apks.put(lite.splitName, file) != null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Split name " + lite.splitName + + " defined more than once; most recent was " + file); } } } + final File baseFile = apks.remove(null); if (baseFile == null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Missing base APK in " + apkDir); } - final Package pkg = parseBaseApk(baseFile, metrics, flags, trustedOverlay); + // Always apply deterministic ordering based on splitName + final int size = apks.size(); + + final String[] splitNames = apks.keySet().toArray(new String[size]); + Arrays.sort(splitNames, sSplitNameComparator); + + final File[] splitFiles = new File[size]; + for (int i = 0; i < size; i++) { + splitFiles[i] = apks.get(splitNames[i]); + } + + final Package pkg = parseBaseApk(baseFile, flags); if (pkg == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse base APK: " + baseFile); } - for (File file : files) { - if (file.isFile() && isPackageFilename(file) && !file.equals(baseFile)) { - parseSplitApk(pkg, file, metrics, flags, trustedOverlay); - } - } - - // Always use a well-defined sort order - if (pkg.splitCodePaths != null) { - Arrays.sort(pkg.splitCodePaths); + for (File splitFile : splitFiles) { + parseSplitApk(pkg, splitFile, flags); } return pkg; } - public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags) - throws PackageParserException { - return parseMonolithicPackage(apkFile, metrics, flags, false); - } - - public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { - final Package pkg = parseBaseApk(apkFile, metrics, flags, trustedOverlay); + /** + * Parse the given APK file, treating it as as a single monolithic package. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(Package, int)}. + */ + public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { + final Package pkg = parseBaseApk(apkFile, flags); if (pkg != null) { return pkg; } else { @@ -576,13 +639,15 @@ public class PackageParser { } } - private Package parseBaseApk(File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) { + private Package parseBaseApk(File apkFile, int flags) { + final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0; + mParseError = PackageManager.INSTALL_SUCCEEDED; + final String apkPath = apkFile.getAbsolutePath(); mArchiveSourcePath = apkFile.getAbsolutePath(); if (!apkFile.isFile()) { - Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath); + Slog.w(TAG, "Skipping dir: " + apkPath); mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } @@ -591,14 +656,14 @@ public class PackageParser { if ((flags&PARSE_IS_SYSTEM) == 0) { // We expect to have non-.apk files in the system dir, // so don't warn about them. - Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath); + Slog.w(TAG, "Skipping non-package file: " + apkPath); } mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } if (DEBUG_JAR) - Slog.d(TAG, "Scanning package: " + mArchiveSourcePath); + Slog.d(TAG, "Scanning package: " + apkPath); XmlResourceParser parser = null; AssetManager assmgr = null; @@ -606,19 +671,18 @@ public class PackageParser { boolean assetError = true; try { assmgr = new AssetManager(); - int cookie = assmgr.addAssetPath(mArchiveSourcePath); + int cookie = assmgr.addAssetPath(apkPath); if (cookie != 0) { - res = new Resources(assmgr, metrics, null); + res = new Resources(assmgr, mMetrics, null); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); assetError = false; } else { - Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath); + Slog.w(TAG, "Failed adding asset path:" + apkPath); } } catch (Exception e) { - Slog.w(TAG, "Unable to read AndroidManifest.xml of " - + mArchiveSourcePath, e); + Slog.w(TAG, "Unable to read AndroidManifest.xml of " + apkPath, e); } if (assetError) { if (assmgr != null) assmgr.close(); @@ -641,9 +705,9 @@ public class PackageParser { // just means to skip this app so don't make a fuss about it. if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) { if (errorException != null) { - Slog.w(TAG, mArchiveSourcePath, errorException); + Slog.w(TAG, apkPath, errorException); } else { - Slog.w(TAG, mArchiveSourcePath + " (at " + Slog.w(TAG, apkPath + " (at " + parser.getPositionDescription() + "): " + errorText[0]); } @@ -659,14 +723,16 @@ public class PackageParser { parser.close(); assmgr.close(); - pkg.codePath = mArchiveSourcePath; + pkg.codePath = apkPath; pkg.mSignatures = null; return pkg; } - private void parseSplitApk(Package pkg, File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { + private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + mArchiveSourcePath = apkFile.getAbsolutePath(); + // TODO: expand split APK parsing pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths, apkFile.getAbsolutePath()); @@ -678,8 +744,9 @@ public class PackageParser { * {@code AndroidManifest.xml}, {@code true} is returned. */ public void collectManifestDigest(Package pkg) throws PackageParserException { + // TODO: extend to gather digest for split APKs try { - final StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath); + final StrictJarFile jarFile = new StrictJarFile(pkg.codePath); try { final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { @@ -688,186 +755,127 @@ public class PackageParser { } finally { jarFile.close(); } - } catch (IOException e) { + } catch (IOException | RuntimeException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Failed to collect manifest digest"); } } + /** + * Collect certificates from all the APKs described in the given package, + * populating {@link Package#mSignatures}. This also asserts that all APK + * contents are signed correctly and consistently. + */ public void collectCertificates(Package pkg, int flags) throws PackageParserException { - if (!collectCertificatesInternal(pkg, flags)) { - throw new PackageParserException(mParseError, "Failed to collect certificates"); - } - } - - private boolean collectCertificatesInternal(Package pkg, int flags) { + pkg.mCertificates = null; pkg.mSignatures = null; + pkg.mSigningKeys = null; - WeakReference<byte[]> readBufferRef; - byte[] readBuffer = null; - synchronized (mSync) { - readBufferRef = mReadBuffer; - if (readBufferRef != null) { - mReadBuffer = null; - readBuffer = readBufferRef.get(); - } - if (readBuffer == null) { - readBuffer = new byte[8192]; - readBufferRef = new WeakReference<byte[]>(readBuffer); + collectCertificates(pkg, new File(pkg.codePath), flags); + + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + for (String splitCodePath : pkg.splitCodePaths) { + collectCertificates(pkg, new File(splitCodePath), flags); } } + } + private static void collectCertificates(Package pkg, File apkFile, int flags) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + + StrictJarFile jarFile = null; try { - StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath); - - Certificate[][] certs = null; - - if ((flags&PARSE_IS_SYSTEM) != 0) { - // If this package comes from the system image, then we - // can trust it... we'll just use the AndroidManifest.xml - // to retrieve its signatures, not validating all of the - // files. - ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); - certs = loadCertificates(jarFile, jarEntry, readBuffer); - if (certs == null) { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates at entry " - + jarEntry.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } - if (DEBUG_JAR) { - Slog.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry - + " certs=" + (certs != null ? certs.length : 0)); - if (certs != null) { - final int N = certs.length; - for (int i=0; i<N; i++) { - Slog.i(TAG, " Public key: " - + certs[i][0].getPublicKey().getEncoded() - + " " + certs[i][0].getPublicKey()); - } - } - } - } else { - Iterator<ZipEntry> entries = jarFile.iterator(); - while (entries.hasNext()) { - final ZipEntry je = entries.next(); - if (je.isDirectory()) continue; + jarFile = new StrictJarFile(apkPath); - final String name = je.getName(); + // Always verify manifest, regardless of source + final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); + if (manifestEntry == null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Package " + apkPath + " has no manifest"); + } - if (name.startsWith("META-INF/")) - continue; + final List<ZipEntry> toVerify = new ArrayList<>(); + toVerify.add(manifestEntry); - if (ANDROID_MANIFEST_FILENAME.equals(name)) { - pkg.manifestDigest = - ManifestDigest.fromInputStream(jarFile.getInputStream(je)); - } + // If we're parsing an untrusted package, verify all contents + if ((flags & PARSE_IS_SYSTEM) == 0) { + final Iterator<ZipEntry> i = jarFile.iterator(); + while (i.hasNext()) { + final ZipEntry entry = i.next(); - final Certificate[][] localCerts = loadCertificates(jarFile, je, readBuffer); - if (DEBUG_JAR) { - Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName() - + ": certs=" + certs + " (" - + (certs != null ? certs.length : 0) + ")"); - } + if (entry.isDirectory()) continue; + if (entry.getName().startsWith("META-INF/")) continue; + if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue; - if (localCerts == null) { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates at entry " - + je.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } else if (certs == null) { - certs = localCerts; - } else { - // Ensure all certificates match. - for (int i=0; i<certs.length; i++) { - boolean found = false; - for (int j=0; j<localCerts.length; j++) { - if (certs[i] != null && - certs[i].equals(localCerts[j])) { - found = true; - break; - } - } - if (!found || certs.length != localCerts.length) { - Slog.e(TAG, "Package " + pkg.packageName - + " has mismatched certificates at entry " - + je.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; - return false; - } - } - } + toVerify.add(entry); } } - jarFile.close(); - synchronized (mSync) { - mReadBuffer = readBufferRef; - } - - if (!ArrayUtils.isEmpty(certs)) { - pkg.mSignatures = convertToSignatures(certs); - } else { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates; ignoring!"); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } + // Verify that entries are signed consistently with the first entry + // we encountered. Note that for splits, certificates may have + // already been populated during an earlier parse of a base APK. + for (ZipEntry entry : toVerify) { + final Certificate[][] entryCerts = loadCertificates(jarFile, entry); + if (ArrayUtils.isEmpty(entryCerts)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "Package " + apkPath + " has no certificates at entry " + + entry.getName()); + } - // Add the signing KeySet to the system - pkg.mSigningKeys = new HashSet<PublicKey>(); - for (int i=0; i < certs.length; i++) { - pkg.mSigningKeys.add(certs[i][0].getPublicKey()); + if (pkg.mCertificates == null) { + pkg.mCertificates = entryCerts; + pkg.mSignatures = convertToSignatures(entryCerts); + pkg.mSigningKeys = new ArraySet<>(); + for (int i = 0; i < entryCerts.length; i++) { + pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey()); + } + } else { + final boolean certsMatch = (pkg.mCertificates.length == entryCerts.length) + && ArrayUtils.containsAll(pkg.mCertificates, entryCerts) + && ArrayUtils.containsAll(entryCerts, pkg.mCertificates); + if (!certsMatch) { + throw new PackageParserException( + INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath + + " has mismatched certificates at entry " + + entry.getName()); + } + } } - - } catch (CertificateEncodingException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (IOException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (SecurityException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (RuntimeException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; - return false; + } catch (GeneralSecurityException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, + "Failed to collect certificates from " + apkPath, e); + } finally { + closeQuietly(jarFile); } - - return true; } /** * Only collect certificates on the manifest; does not validate signatures * across remainder of package. */ - private static Signature[] collectCertificates(String packageFilePath) { + private static Signature[] collectManifestCertificates(File apkFile) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); try { - final StrictJarFile jarFile = new StrictJarFile(packageFilePath); + final StrictJarFile jarFile = new StrictJarFile(apkPath); try { final ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); - if (jarEntry != null) { - final Certificate[][] certs = loadCertificates(jarFile, jarEntry, null); - return convertToSignatures(certs); + if (jarEntry == null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Package " + apkPath + " has no manifest"); } + + final Certificate[][] certs = loadCertificates(jarFile, jarEntry); + return convertToSignatures(certs); + } finally { jarFile.close(); } - } catch (GeneralSecurityException e) { - Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e); - } catch (IOException e) { - Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e); + } catch (GeneralSecurityException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, + "Failed to collect certificates from " + apkPath, e); } - return null; } private static Signature[] convertToSignatures(Certificate[][] certs) @@ -879,67 +887,55 @@ public class PackageParser { return res; } - /* - * Utility method that retrieves just the package name and install - * location from the apk location at the given file path. - * @param packageFilePath file location of the apk - * @param flags Special parse flags - * @return PackageLite object with package information or null on failure. + /** + * Utility method that retrieves lightweight details about a single APK + * file, including package name, split name, and install location. + * + * @param apkFile path to a single APK + * @param flags optional parse flags, such as {@link #PARSE_GET_SIGNATURES} */ - public static ApkLite parseApkLite(String packageFilePath, int flags) { + public static ApkLite parseApkLite(File apkFile, int flags) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + AssetManager assmgr = null; - final XmlResourceParser parser; - final Resources res; + XmlResourceParser parser = null; try { assmgr = new AssetManager(); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); - int cookie = assmgr.addAssetPath(packageFilePath); + int cookie = assmgr.addAssetPath(apkPath); if (cookie == 0) { - return null; + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Failed to parse " + apkPath); } final DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); - res = new Resources(assmgr, metrics, null); + + final Resources res = new Resources(assmgr, metrics, null); parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); - } catch (Exception e) { - if (assmgr != null) assmgr.close(); - Slog.w(TAG, "Unable to read AndroidManifest.xml of " - + packageFilePath, e); - return null; - } - // Only collect certificates on the manifest; does not validate - // signatures across remainder of package. - final Signature[] signatures; - if ((flags & PARSE_GET_SIGNATURES) != 0) { - signatures = collectCertificates(packageFilePath); - } else { - signatures = null; - } + // Only collect certificates on the manifest; does not validate + // signatures across remainder of package. + final Signature[] signatures; + if ((flags & PARSE_GET_SIGNATURES) != 0) { + signatures = collectManifestCertificates(apkFile); + } else { + signatures = null; + } - final AttributeSet attrs = parser; - final String errors[] = new String[1]; - ApkLite packageLite = null; - try { - packageLite = parseApkLite(res, parser, attrs, flags, signatures, errors); - } catch (PackageParserException e) { - Slog.w(TAG, packageFilePath, e); - } catch (IOException e) { - Slog.w(TAG, packageFilePath, e); - } catch (XmlPullParserException e) { - Slog.w(TAG, packageFilePath, e); + final AttributeSet attrs = parser; + return parseApkLite(res, parser, attrs, flags, signatures); + + } catch (XmlPullParserException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to parse " + apkPath, e); } finally { if (parser != null) parser.close(); if (assmgr != null) assmgr.close(); } - if (packageLite == null) { - Slog.e(TAG, "parsePackageLite error: " + errors[0]); - return null; - } - return packageLite; } private static String validateName(String name, boolean requiresSeparator) { @@ -995,12 +991,16 @@ public class PackageParser { } } - final String splitName = attrs.getAttributeValue(null, "split"); + String splitName = attrs.getAttributeValue(null, "split"); if (splitName != null) { - final String error = validateName(splitName, true); - if (error != null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, - "Invalid manifest split: " + error); + if (splitName.length() == 0) { + splitName = null; + } else { + final String error = validateName(splitName, true); + if (error != null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + "Invalid manifest split: " + error); + } } } @@ -1009,8 +1009,8 @@ public class PackageParser { } private static ApkLite parseApkLite(Resources res, XmlPullParser parser, - AttributeSet attrs, int flags, Signature[] signatures, String[] outError) - throws IOException, XmlPullParserException, PackageParserException { + AttributeSet attrs, int flags, Signature[] signatures) throws IOException, + XmlPullParserException, PackageParserException { final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags); int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; @@ -1043,7 +1043,7 @@ public class PackageParser { } if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) { - final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags, outError); + final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags); if (verifier != null) { verifiers.add(verifier); } @@ -1793,7 +1793,7 @@ public class PackageParser { } } - owner.mKeySetMapping = new HashMap<String, Set<PublicKey>>(); + owner.mKeySetMapping = new ArrayMap<String, ArraySet<PublicKey>>(); for (Map.Entry<PublicKey, Set<String>> e : definedKeySets.entrySet()) { PublicKey key = e.getKey(); Set<String> keySetNames = e.getValue(); @@ -1801,7 +1801,7 @@ public class PackageParser { if (owner.mKeySetMapping.containsKey(alias)) { owner.mKeySetMapping.get(alias).add(key); } else { - Set<PublicKey> keys = new HashSet<PublicKey>(); + ArraySet<PublicKey> keys = new ArraySet<PublicKey>(); keys.add(key); owner.mKeySetMapping.put(alias, keys); } @@ -2088,6 +2088,11 @@ public class PackageParser { false)) { ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_fullBackupOnly, + false)) { + ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY; + } } } @@ -2607,10 +2612,9 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode, 0); - if (sa.getBoolean( - com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) { - a.info.flags |= ActivityInfo.FLAG_PERSISTABLE; - } + a.info.persistableMode = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestActivity_persistableMode, + ActivityInfo.PERSIST_ROOT_ONLY); if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded, @@ -3428,8 +3432,7 @@ public class PackageParser { } private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser, - AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException, - IOException { + AttributeSet attrs, int flags) { final TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestPackageVerifier); @@ -3672,7 +3675,10 @@ public class PackageParser { public String packageName; // TODO: work towards making these paths invariant + + /** Base APK */ public String codePath; + /** Split APKs, ordered by parsed splitName */ public String[] splitCodePaths; // For now we only support one application per package. @@ -3718,7 +3724,8 @@ public class PackageParser { public int mSharedUserLabel; // Signatures that were read from the package. - public Signature mSignatures[]; + public Signature[] mSignatures; + public Certificate[][] mCertificates; // For use by package manager service for quick lookup of // preferred up order. @@ -3780,8 +3787,8 @@ public class PackageParser { /** * Data used to feed the KeySetManager */ - public Set<PublicKey> mSigningKeys; - public Map<String, Set<PublicKey>> mKeySetMapping; + public ArraySet<PublicKey> mSigningKeys; + public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping; public Package(String packageName) { this.packageName = packageName; @@ -3789,6 +3796,15 @@ public class PackageParser { applicationInfo.uid = -1; } + public Collection<String> getAllCodePaths() { + ArrayList<String> paths = new ArrayList<>(); + paths.add(codePath); + if (!ArrayUtils.isEmpty(splitCodePaths)) { + Collections.addAll(paths, splitCodePaths); + } + return paths; + } + public void setPackageName(String newName) { packageName = newName; applicationInfo.packageName = newName; @@ -4391,6 +4407,33 @@ public class PackageParser { sCompatibilityModeEnabled = compatibilityModeEnabled; } + private static AtomicReference<byte[]> sBuffer = new AtomicReference<byte[]>(); + + public static long readFullyIgnoringContents(InputStream in) throws IOException { + byte[] buffer = sBuffer.getAndSet(null); + if (buffer == null) { + buffer = new byte[4096]; + } + + int n = 0; + int count = 0; + while ((n = in.read(buffer, 0, buffer.length)) != -1) { + count += n; + } + + sBuffer.set(buffer); + return count; + } + + public static void closeQuietly(StrictJarFile jarFile) { + if (jarFile != null) { + try { + jarFile.close(); + } catch (Exception ignored) { + } + } + } + public static class PackageParserException extends Exception { public final int error; @@ -4398,5 +4441,10 @@ public class PackageParser { super(detailMessage); this.error = error; } + + public PackageParserException(int error, String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + this.error = error; + } } } diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java index 8ad9463..d86dd5e 100644 --- a/core/java/android/hardware/hdmi/HdmiCec.java +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -185,6 +185,7 @@ public final class HdmiCec { public static final int RESULT_TARGET_NOT_AVAILABLE = 3; public static final int RESULT_ALREADY_IN_PROGRESS = 4; public static final int RESULT_EXCEPTION = 5; + public static final int RESULT_INCORRECT_MODE = 6; private static final int[] ADDRESS_TO_TYPE = { DEVICE_TV, // ADDR_TV diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 535bbe2..8142670 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -699,15 +699,15 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { break; } - try { - if (enable) { - return mPhoneService.enableApnType(apnType); - } else { - return mPhoneService.disableApnType(apnType); - } - } catch (RemoteException e) { - if (retry == 0) getPhoneService(true); - } +// try { +// if (enable) { +// return mPhoneService.enableApnType(apnType); +// } else { +// return mPhoneService.disableApnType(apnType); +// } +// } catch (RemoteException e) { +// if (retry == 0) getPhoneService(true); +// } } loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\""); diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java new file mode 100644 index 0000000..f3a95b9 --- /dev/null +++ b/core/java/android/os/BatteryManagerInternal.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 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 android.os; + +/** + * Battery manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class BatteryManagerInternal { + /** + * Returns true if the device is plugged into any of the specified plug types. + */ + public abstract boolean isPowered(int plugTypeSet); + + /** + * Returns the current plug type. + */ + public abstract int getPlugType(); + + /** + * Returns battery level as a percentage. + */ + public abstract int getBatteryLevel(); + + /** + * Returns whether we currently consider the battery level to be low. + */ + public abstract boolean getBatteryLevelLow(); + + /** + * Returns a non-zero value if an unsupported charger is attached. + */ + public abstract int getInvalidCharger(); +} diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 69b828f..08a15eb 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -16,8 +16,6 @@ package android.os; -import android.view.WindowManagerPolicy; - /** * Power manager local system service interface. * @@ -57,12 +55,9 @@ public abstract class PowerManagerInternal { public abstract boolean getLowPowerModeEnabled(); + public abstract void registerLowPowerModeObserver(LowPowerModeListener listener); + public interface LowPowerModeListener { public void onLowPowerModeChanged(boolean enabled); } - - public abstract void registerLowPowerModeObserver(LowPowerModeListener listener); - - // TODO: Remove this and retrieve as a local service instead. - public abstract void setPolicy(WindowManagerPolicy policy); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 468cfe0..96db772 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -226,14 +226,14 @@ public class UserManager { public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks"; /** - * Key for user restrictions. Specifies if a user is disallowed from configuring + * Key for user restrictions. Specifies if a user is disallowed from controlling * applications in Settings. The default value is <code>false</code>. * <p> * Type: Boolean * @see #setUserRestrictions(Bundle) * @see #getUserRestrictions() */ - public static final String DISALLOW_CONFIG_APPS = "no_config_apps"; + public static final String DISALLOW_APPS_CONTROL = "no_control_apps"; /** * Key for user restrictions. Specifies if a user is disallowed from mounting @@ -719,6 +719,26 @@ public class UserManager { /** * If the target user is a managed profile of the calling user or the caller + * is itself a managed profile, then this returns a copy of the label with + * badging for accessibility services like talkback. E.g. passing in "Email" + * and it might return "Work Email" for Email in the work profile. + * + * @param label The label to change. + * @param user The target user. + * @return A label that combines the original label and a badge as + * determined by the system. + */ + public String getBadgedLabelForUser(String label, UserHandle user) { + UserInfo userInfo = getUserIfProfile(user.getIdentifier()); + if (userInfo != null && userInfo.isManagedProfile()) { + return Resources.getSystem().getString( + R.string.managed_profile_label_badge, label); + } + return label; + } + + /** + * If the target user is a managed profile of the calling user or the caller * is itself a managed profile, then this returns a drawable to use as a small * icon to include in a view to distinguish it from the original icon. * diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 0418049..23b1e2c 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -1194,7 +1194,14 @@ public abstract class PreferenceActivity extends ListActivity implements * @param args Optional arguments to supply to the fragment. */ public void switchToHeader(String fragmentName, Bundle args) { - setSelectedHeader(null); + Header selectedHeader = null; + for (int i = 0; i < mHeaders.size(); i++) { + if (fragmentName.equals(mHeaders.get(i).fragment)) { + selectedHeader = mHeaders.get(i); + break; + } + } + setSelectedHeader(selectedHeader); switchToHeaderInner(fragmentName, args); } diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java index b6137d1..5fd597b 100644 --- a/core/java/android/service/fingerprint/FingerprintManager.java +++ b/core/java/android/service/fingerprint/FingerprintManager.java @@ -96,6 +96,9 @@ public class FingerprintManager { } }; + /** + * @hide + */ public FingerprintManager(Context context, IFingerprintService service) { mContext = context; mService = service; diff --git a/core/java/android/transition/Explode.java b/core/java/android/transition/Explode.java index fae527c..feb8efd 100644 --- a/core/java/android/transition/Explode.java +++ b/core/java/android/transition/Explode.java @@ -15,20 +15,16 @@ */ package android.transition; +import com.android.internal.R; + import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.graphics.Path; import android.graphics.Rect; import android.util.FloatMath; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; - /** * This transition tracks changes to the visibility of target views in the * start and end scenes and moves views in or out from the edges of the @@ -44,8 +40,7 @@ public class Explode extends Visibility { private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); private static final String TAG = "Explode"; - - private static final String PROPNAME_SCREEN_BOUNDS = "android:out:screenBounds"; + private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds"; private int[] mTempLoc = new int[2]; @@ -56,8 +51,8 @@ public class Explode extends Visibility { private void captureValues(TransitionValues transitionValues) { View view = transitionValues.view; view.getLocationOnScreen(mTempLoc); - int left = mTempLoc[0] + Math.round(view.getTranslationX()); - int top = mTempLoc[1] + Math.round(view.getTranslationY()); + int left = mTempLoc[0]; + int top = mTempLoc[1]; int right = left + view.getWidth(); int bottom = top + view.getHeight(); transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom)); @@ -75,27 +70,6 @@ public class Explode extends Visibility { captureValues(transitionValues); } - private Animator createAnimation(final View view, float startX, float startY, float endX, - float endY, float terminalX, float terminalY, TimeInterpolator interpolator) { - view.setTranslationX(startX); - view.setTranslationY(startY); - if (startY == endY && startX == endX) { - return null; - } - Path path = new Path(); - path.moveTo(startX, startY); - path.lineTo(endX, endY); - ObjectAnimator pathAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, - View.TRANSLATION_Y, path); - pathAnimator.setInterpolator(interpolator); - OutAnimatorListener listener = new OutAnimatorListener(view, terminalX, terminalY, - endX, endY); - pathAnimator.addListener(listener); - pathAnimator.addPauseListener(listener); - - return pathAnimator; - } - @Override public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { @@ -103,29 +77,43 @@ public class Explode extends Visibility { return null; } Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS); + float endX = view.getTranslationX(); + float endY = view.getTranslationY(); calculateOut(sceneRoot, bounds, mTempLoc); + float startX = endX + mTempLoc[0]; + float startY = endY + mTempLoc[1]; - final float endX = view.getTranslationX(); - final float startX = endX + mTempLoc[0]; - final float endY = view.getTranslationY(); - final float startY = endY + mTempLoc[1]; - - return createAnimation(view, startX, startY, endX, endY, endX, endY, sDecelerate); + return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top, + startX, startY, endX, endY, sDecelerate); } @Override public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { + if (startValues == null) { + return null; + } Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS); + int viewPosX = bounds.left; + int viewPosY = bounds.top; + float startX = view.getTranslationX(); + float startY = view.getTranslationY(); + float endX = startX; + float endY = startY; + int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transitionPosition); + if (interruptedPosition != null) { + // We want to have the end position relative to the interrupted position, not + // the position it was supposed to start at. + endX += interruptedPosition[0] - bounds.left; + endY += interruptedPosition[1] - bounds.top; + bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]); + } calculateOut(sceneRoot, bounds, mTempLoc); + endX += mTempLoc[0]; + endY += mTempLoc[1]; - final float startX = view.getTranslationX(); - final float endX = startX + mTempLoc[0]; - final float startY = view.getTranslationY(); - final float endY = startY + mTempLoc[1]; - - return createAnimation(view, startX, startY, endX, endY, startX, startY, - sAccelerate); + return TranslationAnimationCreator.createAnimation(view, startValues, + viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate); } private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) { @@ -153,8 +141,8 @@ public class Explode extends Visibility { if (xVector == 0 && yVector == 0) { // Random direction when View is centered on focal View. - xVector = (float)(Math.random() * 2) - 1; - yVector = (float)(Math.random() * 2) - 1; + xVector = (float) (Math.random() * 2) - 1; + yVector = (float) (Math.random() * 2) - 1; } float vectorSize = calculateDistance(xVector, yVector); xVector /= vectorSize; @@ -176,53 +164,4 @@ public class Explode extends Visibility { private static float calculateDistance(float x, float y) { return FloatMath.sqrt((x * x) + (y * y)); } - - private static class OutAnimatorListener extends AnimatorListenerAdapter { - private final View mView; - private boolean mCanceled = false; - private float mPausedX; - private float mPausedY; - private final float mTerminalX; - private final float mTerminalY; - private final float mEndX; - private final float mEndY; - - public OutAnimatorListener(View view, float terminalX, float terminalY, - float endX, float endY) { - mView = view; - mTerminalX = terminalX; - mTerminalY = terminalY; - mEndX = endX; - mEndY = endY; - } - - @Override - public void onAnimationCancel(Animator animator) { - mView.setTranslationX(mTerminalX); - mView.setTranslationY(mTerminalY); - mCanceled = true; - } - - @Override - public void onAnimationEnd(Animator animator) { - if (!mCanceled) { - mView.setTranslationX(mTerminalX); - mView.setTranslationY(mTerminalY); - } - } - - @Override - public void onAnimationPause(Animator animator) { - mPausedX = mView.getTranslationX(); - mPausedY = mView.getTranslationY(); - mView.setTranslationY(mEndX); - mView.setTranslationY(mEndY); - } - - @Override - public void onAnimationResume(Animator animator) { - mView.setTranslationX(mPausedX); - mView.setTranslationY(mPausedY); - } - } } diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java index 8269258..0d2e487 100644 --- a/core/java/android/transition/Slide.java +++ b/core/java/android/transition/Slide.java @@ -16,14 +16,7 @@ package android.transition; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.graphics.Rect; -import android.util.Log; -import android.util.Property; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -41,71 +34,60 @@ import android.view.animation.DecelerateInterpolator; */ public class Slide extends Visibility { private static final String TAG = "Slide"; - private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); - + private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition"; private CalculateSlide mSlideCalculator = sCalculateBottom; private interface CalculateSlide { - /** Returns the translation value for view when it out of the scene */ - float getGone(ViewGroup sceneRoot, View view); - /** Returns the translation value for view when it is in the scene */ - float getHere(View view); + /** Returns the translation value for view when it goes out of the scene */ + float getGoneX(ViewGroup sceneRoot, View view); - /** Returns the property to animate translation */ - Property<View, Float> getProperty(); + /** Returns the translation value for view when it goes out of the scene */ + float getGoneY(ViewGroup sceneRoot, View view); } private static abstract class CalculateSlideHorizontal implements CalculateSlide { - @Override - public float getHere(View view) { - return view.getTranslationX(); - } @Override - public Property<View, Float> getProperty() { - return View.TRANSLATION_X; + public float getGoneY(ViewGroup sceneRoot, View view) { + return view.getTranslationY(); } } private static abstract class CalculateSlideVertical implements CalculateSlide { - @Override - public float getHere(View view) { - return view.getTranslationY(); - } @Override - public Property<View, Float> getProperty() { - return View.TRANSLATION_Y; + public float getGoneX(ViewGroup sceneRoot, View view) { + return view.getTranslationX(); } } private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { @Override - public float getGone(ViewGroup sceneRoot, View view) { + public float getGoneX(ViewGroup sceneRoot, View view) { return view.getTranslationX() - sceneRoot.getWidth(); } }; private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { @Override - public float getGone(ViewGroup sceneRoot, View view) { + public float getGoneY(ViewGroup sceneRoot, View view) { return view.getTranslationY() - sceneRoot.getHeight(); } }; private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { @Override - public float getGone(ViewGroup sceneRoot, View view) { + public float getGoneX(ViewGroup sceneRoot, View view) { return view.getTranslationX() + sceneRoot.getWidth(); } }; private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { @Override - public float getGone(ViewGroup sceneRoot, View view) { + public float getGoneY(ViewGroup sceneRoot, View view) { return view.getTranslationY() + sceneRoot.getHeight(); } }; @@ -125,8 +107,28 @@ public class Slide extends Visibility { setSlideEdge(slideEdge); } + private void captureValues(TransitionValues transitionValues) { + View view = transitionValues.view; + int[] position = new int[2]; + view.getLocationOnScreen(position); + transitionValues.values.put(PROPNAME_SCREEN_POSITION, position); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureEndValues(transitionValues); + captureValues(transitionValues); + } + /** * Change the edge that Views appear and disappear from. + * * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP}, * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}. @@ -153,77 +155,35 @@ public class Slide extends Visibility { setPropagation(propagation); } - private Animator createAnimation(final View view, Property<View, Float> property, - float start, float end, float terminalValue, TimeInterpolator interpolator) { - view.setTranslationY(start); - if (start == end) { - return null; - } - final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end); - - SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end); - anim.addListener(listener); - anim.addPauseListener(listener); - anim.setInterpolator(interpolator); - return anim; - } - @Override public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { if (endValues == null) { return null; } - float end = mSlideCalculator.getHere(view); - float start = mSlideCalculator.getGone(sceneRoot, view); - return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate); + int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); + float endX = view.getTranslationX(); + float endY = view.getTranslationY(); + float startX = mSlideCalculator.getGoneX(sceneRoot, view); + float startY = mSlideCalculator.getGoneY(sceneRoot, view); + return TranslationAnimationCreator + .createAnimation(view, endValues, position[0], position[1], + startX, startY, endX, endY, sDecelerate); } @Override public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { - float start = mSlideCalculator.getHere(view); - float end = mSlideCalculator.getGone(sceneRoot, view); - - return createAnimation(view, mSlideCalculator.getProperty(), start, end, start, - sAccelerate); - } - - private static class SlideAnimatorListener extends AnimatorListenerAdapter { - private boolean mCanceled = false; - private float mPausedY; - private final View mView; - private final float mEndY; - private final float mTerminalY; - - public SlideAnimatorListener(View view, float terminalY, float endY) { - mView = view; - mTerminalY = terminalY; - mEndY = endY; - } - - @Override - public void onAnimationCancel(Animator animator) { - mView.setTranslationY(mTerminalY); - mCanceled = true; - } - - @Override - public void onAnimationEnd(Animator animator) { - if (!mCanceled) { - mView.setTranslationY(mTerminalY); - } - } - - @Override - public void onAnimationPause(Animator animator) { - mPausedY = mView.getTranslationY(); - mView.setTranslationY(mEndY); - } - - @Override - public void onAnimationResume(Animator animator) { - mView.setTranslationY(mPausedY); + if (startValues == null) { + return null; } + int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); + float startX = view.getTranslationX(); + float startY = view.getTranslationY(); + float endX = mSlideCalculator.getGoneX(sceneRoot, view); + float endY = mSlideCalculator.getGoneY(sceneRoot, view); + return TranslationAnimationCreator + .createAnimation(view, startValues, position[0], position[1], + startX, startY, endX, endY, sAccelerate); } } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 508769d..e936232 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -673,7 +673,7 @@ public abstract class Transition implements Cloneable { startDelays.put(mAnimators.size(), delay); minStartDelay = Math.min(delay, minStartDelay); } - AnimationInfo info = new AnimationInfo(view, getName(), + AnimationInfo info = new AnimationInfo(view, getName(), this, sceneRoot.getWindowId(), infoValues); runningAnimators.put(animator, info); mAnimators.add(animator); @@ -1587,30 +1587,10 @@ public abstract class Transition implements Cloneable { AnimationInfo oldInfo = runningAnimators.get(anim); if (oldInfo != null && oldInfo.view != null && oldInfo.view.getContext() == sceneRoot.getContext()) { - boolean cancel = false; TransitionValues oldValues = oldInfo.values; View oldView = oldInfo.view; TransitionValues newValues = mEndValues.viewValues.get(oldView); - if (oldValues != null) { - // if oldValues null, then transition didn't care to stash values, - // and won't get canceled - if (newValues != null) { - for (String key : oldValues.values.keySet()) { - Object oldValue = oldValues.values.get(key); - Object newValue = newValues.values.get(key); - if (oldValue != null && newValue != null && - !oldValue.equals(newValue)) { - cancel = true; - if (DBG) { - Log.d(LOG_TAG, "Transition.playTransition: " + - "oldValue != newValue for " + key + - ": old, new = " + oldValue + ", " + newValue); - } - break; - } - } - } - } + boolean cancel = oldInfo.transition.areValuesChanged(oldValues, newValues); if (cancel) { if (anim.isRunning() || anim.isStarted()) { if (DBG) { @@ -1632,6 +1612,29 @@ public abstract class Transition implements Cloneable { runAnimators(); } + boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) { + boolean valuesChanged = false; + // if oldValues null, then transition didn't care to stash values, + // and won't get canceled + if (oldValues != null && newValues != null) { + for (String key : oldValues.values.keySet()) { + Object oldValue = oldValues.values.get(key); + Object newValue = newValues.values.get(key); + if (oldValue != null && newValue != null && + !oldValue.equals(newValue)) { + valuesChanged = true; + if (DBG) { + Log.d(LOG_TAG, "Transition.playTransition: " + + "oldValue != newValue for " + key + + ": old, new = " + oldValue + ", " + newValue); + } + break; + } + } + } + return valuesChanged; + } + /** * This is a utility method used by subclasses to handle standard parts of * setting up and running an Animator: it sets the {@link #getDuration() @@ -2070,12 +2073,15 @@ public abstract class Transition implements Cloneable { String name; TransitionValues values; WindowId windowId; + Transition transition; - AnimationInfo(View view, String name, WindowId windowId, TransitionValues values) { + AnimationInfo(View view, String name, Transition transition, + WindowId windowId, TransitionValues values) { this.view = view; this.name = name; this.values = values; this.windowId = windowId; + this.transition = transition; } } diff --git a/core/java/android/transition/TranslationAnimationCreator.java b/core/java/android/transition/TranslationAnimationCreator.java new file mode 100644 index 0000000..de71fd7 --- /dev/null +++ b/core/java/android/transition/TranslationAnimationCreator.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014 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 android.transition; + +import com.android.internal.R; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.graphics.Path; +import android.view.View; + +/** + * This class is used by Slide and Explode to create an animator that goes from the start + * position to the end position. It takes into account the canceled position so that it + * will not blink out or shift suddenly when the transition is interrupted. + */ +class TranslationAnimationCreator { + + /** + * Creates an animator that can be used for x and/or y translations. When interrupted, + * it sets a tag to keep track of the position so that it may be continued from position. + * + * @param view The view being moved. This may be in the overlay for onDisappear. + * @param values The values containing the view in the view hierarchy. + * @param viewPosX The x screen coordinate of view + * @param viewPosY The y screen coordinate of view + * @param startX The start translation x of view + * @param startY The start translation y of view + * @param endX The end translation x of view + * @param endY The end translation y of view + * @param interpolator The interpolator to use with this animator. + * @return An animator that moves from (startX, startY) to (endX, endY) unless there was + * a previous interruption, in which case it moves from the current position to (endX, endY). + */ + static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY, + float startX, float startY, float endX, float endY, TimeInterpolator interpolator) { + float terminalX = view.getTranslationX(); + float terminalY = view.getTranslationY(); + int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition); + if (startPosition != null) { + startX = startPosition[0] - viewPosX + terminalX; + startY = startPosition[1] - viewPosY + terminalY; + } + // Initial position is at translation startX, startY, so position is offset by that amount + int startPosX = viewPosX + Math.round(startX - terminalX); + int startPosY = viewPosY + Math.round(startY - terminalY); + + view.setTranslationX(startX); + view.setTranslationY(startY); + if (startX == endX && startY == endY) { + return null; + } + Path path = new Path(); + path.moveTo(startX, startY); + path.lineTo(endX, endY); + ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y, + path); + + TransitionPositionListener listener = new TransitionPositionListener(view, values.view, + startPosX, startPosY, terminalX, terminalY); + anim.addListener(listener); + anim.addPauseListener(listener); + anim.setInterpolator(interpolator); + return anim; + } + + private static class TransitionPositionListener extends AnimatorListenerAdapter { + + private final View mViewInHierarchy; + private final View mMovingView; + private final int mStartX; + private final int mStartY; + private int[] mTransitionPosition; + private float mPausedX; + private float mPausedY; + private final float mTerminalX; + private final float mTerminalY; + + private TransitionPositionListener(View movingView, View viewInHierarchy, + int startX, int startY, float terminalX, float terminalY) { + mMovingView = movingView; + mViewInHierarchy = viewInHierarchy; + mStartX = startX - Math.round(mMovingView.getTranslationX()); + mStartY = startY - Math.round(mMovingView.getTranslationY()); + mTerminalX = terminalX; + mTerminalY = terminalY; + mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transitionPosition); + if (mTransitionPosition != null) { + mViewInHierarchy.setTagInternal(R.id.transitionPosition, null); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mTransitionPosition == null) { + mTransitionPosition = new int[2]; + } + mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX()); + mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY()); + mViewInHierarchy.setTagInternal(R.id.transitionPosition, mTransitionPosition); + } + + @Override + public void onAnimationEnd(Animator animator) { + mMovingView.setTranslationX(mTerminalX); + mMovingView.setTranslationY(mTerminalY); + } + + @Override + public void onAnimationPause(Animator animator) { + mPausedX = mMovingView.getTranslationX(); + mPausedY = mMovingView.getTranslationY(); + mMovingView.setTranslationX(mTerminalX); + mMovingView.setTranslationY(mTerminalY); + } + + @Override + public void onAnimationResume(Animator animator) { + mMovingView.setTranslationX(mPausedX); + mMovingView.setTranslationY(mPausedY); + } + } + +} diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index c6c8337..947e1a7 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -331,16 +331,6 @@ public abstract class Visibility extends Transition { public void onAnimationEnd(Animator animation) { finalSceneRoot.getOverlay().remove(finalOverlayView); } - - @Override - public void onAnimationPause(Animator animation) { - finalSceneRoot.getOverlay().remove(finalOverlayView); - } - - @Override - public void onAnimationResume(Animator animation) { - finalSceneRoot.getOverlay().add(finalOverlayView); - } }); } return animator; @@ -409,6 +399,16 @@ public abstract class Visibility extends Transition { return overlayView; } + @Override + boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) { + VisibilityInfo changeInfo = getVisibilityChangeInfo(oldValues, newValues); + if (oldValues == null && newValues == null) { + return false; + } + return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE || + changeInfo.endVisibility == View.VISIBLE); + } + /** * The default implementation of this method returns a null Animator. Subclasses should * override this method to make targets disappear with the desired transition. The diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index d7a913d..76a6f52 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -599,6 +599,40 @@ public final class Display { } /** + * Gets the app VSYNC offset, in nanoseconds. This is a positive value indicating + * the phase offset of the VSYNC events provided by Choreographer relative to the + * display refresh. For example, if Choreographer reports that the refresh occurred + * at time N, it actually occurred at (N - appVsyncOffset). + * <p> + * Apps generally do not need to be aware of this. It's only useful for fine-grained + * A/V synchronization. + */ + public long getAppVsyncOffsetNanos() { + synchronized (this) { + updateDisplayInfoLocked(); + return mDisplayInfo.appVsyncOffsetNanos; + } + } + + /** + * This is how far in advance a buffer must be queued for presentation at + * a given time. If you want a buffer to appear on the screen at + * time N, you must submit the buffer before (N - presentationDeadline). + * <p> + * The desired presentation time for GLES rendering may be set with + * {@link android.opengl.EGLExt#eglPresentationTimeANDROID}. For video decoding, use + * {@link android.media.MediaCodec#releaseOutputBuffer(int, long)}. Times are + * expressed in nanoseconds, using the system monotonic clock + * ({@link System#nanoTime}). + */ + public long getPresentationDeadlineNanos() { + synchronized (this) { + updateDisplayInfoLocked(); + return mDisplayInfo.presentationDeadlineNanos; + } + } + + /** * Gets display metrics that describe the size and density of this display. * <p> * The size is adjusted based on the current rotation of the display. diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index b0fe0fa..98696c7 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -180,6 +180,20 @@ public final class DisplayInfo implements Parcelable { public float physicalYDpi; /** + * This is a positive value indicating the phase offset of the VSYNC events provided by + * Choreographer relative to the display refresh. For example, if Choreographer reports + * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos). + */ + public long appVsyncOffsetNanos; + + /** + * This is how far in advance a buffer must be queued for presentation at + * a given time. If you want a buffer to appear on the screen at + * time N, you must submit the buffer before (N - bufferDeadlineNanos). + */ + public long presentationDeadlineNanos; + + /** * The state of the display, such as {@link android.view.Display#STATE_ON}. */ public int state; @@ -253,6 +267,8 @@ public final class DisplayInfo implements Parcelable { && logicalDensityDpi == other.logicalDensityDpi && physicalXDpi == other.physicalXDpi && physicalYDpi == other.physicalYDpi + && appVsyncOffsetNanos == other.appVsyncOffsetNanos + && presentationDeadlineNanos == other.presentationDeadlineNanos && state == other.state && ownerUid == other.ownerUid && Objects.equal(ownerPackageName, other.ownerPackageName); @@ -286,6 +302,8 @@ public final class DisplayInfo implements Parcelable { logicalDensityDpi = other.logicalDensityDpi; physicalXDpi = other.physicalXDpi; physicalYDpi = other.physicalYDpi; + appVsyncOffsetNanos = other.appVsyncOffsetNanos; + presentationDeadlineNanos = other.presentationDeadlineNanos; state = other.state; ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; @@ -314,6 +332,8 @@ public final class DisplayInfo implements Parcelable { logicalDensityDpi = source.readInt(); physicalXDpi = source.readFloat(); physicalYDpi = source.readFloat(); + appVsyncOffsetNanos = source.readLong(); + presentationDeadlineNanos = source.readLong(); state = source.readInt(); ownerUid = source.readInt(); ownerPackageName = source.readString(); @@ -343,6 +363,8 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(logicalDensityDpi); dest.writeFloat(physicalXDpi); dest.writeFloat(physicalYDpi); + dest.writeLong(appVsyncOffsetNanos); + dest.writeLong(presentationDeadlineNanos); dest.writeInt(state); dest.writeInt(ownerUid); dest.writeString(ownerPackageName); @@ -450,6 +472,10 @@ public final class DisplayInfo implements Parcelable { sb.append(physicalYDpi); sb.append(") dpi, layerStack "); sb.append(layerStack); + sb.append(", appVsyncOff "); + sb.append(appVsyncOffsetNanos); + sb.append(", presDeadline "); + sb.append(presentationDeadlineNanos); sb.append(", type "); sb.append(Display.typeToString(type)); if (address != null) { diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 446bbc9..68e2146 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -804,20 +804,12 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPicture(Picture picture) { - if (picture.createdFromStream) { - return; - } - picture.endRecording(); // TODO: Implement rendering } @Override public void drawPicture(Picture picture, Rect dst) { - if (picture.createdFromStream) { - return; - } - save(); translate(dst.left, dst.top); if (picture.getWidth() > 0 && picture.getHeight() > 0) { @@ -829,10 +821,6 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPicture(Picture picture, RectF dst) { - if (picture.createdFromStream) { - return; - } - save(); translate(dst.left, dst.top); if (picture.getWidth() > 0 && picture.getHeight() > 0) { @@ -981,22 +969,24 @@ class GLES20Canvas extends HardwareCanvas { } nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count, - long path, float hOffset, float vOffset, int bidiFlags, long nativePaint); + long path, float hOffset, float vOffset, int bidiFlags, long nativePaint, + long typeface); @Override public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) { if (text.length() == 0) return; nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } private static native void nDrawTextOnPath(long renderer, String text, int start, int end, - long path, float hOffset, float vOffset, int bidiFlags, long nativePaint); + long path, float hOffset, float vOffset, int bidiFlags, long nativePaint, + long typeface); @Override public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 79f19b5..94d8f70 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -458,6 +458,8 @@ public class SurfaceControl { public float xDpi; public float yDpi; public boolean secure; + public long appVsyncOffsetNanos; + public long presentationDeadlineNanos; public PhysicalDisplayInfo() { } @@ -479,7 +481,9 @@ public class SurfaceControl { && density == other.density && xDpi == other.xDpi && yDpi == other.yDpi - && secure == other.secure; + && secure == other.secure + && appVsyncOffsetNanos == other.appVsyncOffsetNanos + && presentationDeadlineNanos == other.presentationDeadlineNanos; } @Override @@ -495,6 +499,8 @@ public class SurfaceControl { xDpi = other.xDpi; yDpi = other.yDpi; secure = other.secure; + appVsyncOffsetNanos = other.appVsyncOffsetNanos; + presentationDeadlineNanos = other.presentationDeadlineNanos; } // For debugging purposes @@ -502,7 +508,8 @@ public class SurfaceControl { public String toString() { return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, " + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure - + "}"; + + ", appVsyncOffset " + appVsyncOffsetNanos + + ", bufferDeadline " + presentationDeadlineNanos + "}"; } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 9156216..89c2f37 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -45,7 +45,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.Shader; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManagerGlobal; @@ -4822,20 +4821,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final float x = r.exactCenterX(); final float y = r.exactCenterY(); - setDrawableHotspot(x, y); - } - - /** - * Sets the hotspot position for this View's drawables. - * - * @param x hotspot x coordinate - * @param y hotspot y coordinate - * @hide - */ - protected void setDrawableHotspot(float x, float y) { - if (mBackground != null) { - mBackground.setHotspot(x, y); - } + drawableHotspotChanged(x, y); } /** @@ -6798,7 +6784,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private void setPressed(boolean pressed, float x, float y) { if (pressed) { - setDrawableHotspot(x, y); + drawableHotspotChanged(x, y); } setPressed(pressed); @@ -9149,7 +9135,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; case MotionEvent.ACTION_MOVE: - setDrawableHotspot(x, y); + drawableHotspotChanged(x, y); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { @@ -15531,6 +15517,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * This function is called whenever the drawable hotspot changes. + * <p> + * Be sure to call through to the superclass when overriding this function. + * + * @param x hotspot x coordinate + * @param y hotspot y coordinate + */ + public void drawableHotspotChanged(float x, float y) { + if (mBackground != null) { + mBackground.setHotspot(x, y); + } + } + + /** * Call this to force a view to update its drawable state. This will cause * drawableStateChanged to be called on this view. Views that are interested * in the new state should call getDrawableState. diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index eb3f882..7e2d809 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -24,7 +24,6 @@ import android.graphics.Canvas; import android.graphics.Insets; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.PorterDuff.Mode; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -343,7 +342,10 @@ public abstract class AbsSeekBar extends ProgressBar { @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); - if (mThumb != null) mThumb.jumpToCurrentState(); + + if (mThumb != null) { + mThumb.jumpToCurrentState(); + } } @Override @@ -361,29 +363,12 @@ public abstract class AbsSeekBar extends ProgressBar { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); - final Drawable progressDrawable = getProgressDrawable(); - if (progressDrawable != null) { - progressDrawable.setHotspot(x, y); - } - - final Drawable thumb = mThumb; - if (thumb != null) { - thumb.setHotspot(x, y); - } - } - - @Override - public void invalidateDrawable(Drawable dr) { - super.invalidateDrawable(dr); - - if (dr == mThumb) { - // Handle changes to thumb width and height. - requestLayout(); + if (mThumb != null) { + mThumb.setHotspot(x, y); } } @@ -479,11 +464,11 @@ public abstract class AbsSeekBar extends ProgressBar { final Drawable background = getBackground(); if (background != null) { - final Rect bounds = mThumb.getBounds(); + final Rect bounds = thumb.getBounds(); final int offsetX = mPaddingLeft - mThumbOffset; final int offsetY = mPaddingTop; - background.setHotspotBounds(left + offsetX, bounds.top + offsetY, - right + offsetX, bounds.bottom + offsetY); + background.setHotspotBounds(left + offsetX, top + offsetY, + right + offsetX, bottom + offsetY); } // Canvas will be translated, so 0,0 is where we start drawing @@ -505,8 +490,8 @@ public abstract class AbsSeekBar extends ProgressBar { @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); - drawThumb(canvas); + } @Override diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index 7113793..4aa2300 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -307,10 +307,9 @@ public class CheckedTextView extends TextView implements Checkable { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mCheckMarkDrawable != null) { mCheckMarkDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 747d2b1..9ba0fe1 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; @@ -431,10 +430,9 @@ public abstract class CompoundButton extends Button implements Checkable { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mButtonDrawable != null) { mButtonDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 5a14929..34f333e 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -26,7 +26,6 @@ import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; @@ -223,10 +222,9 @@ public class FrameLayout extends ViewGroup { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mForeground != null) { mForeground.setHotspot(x, y); diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 399e087..5d578ca 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -32,7 +32,6 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Xfermode; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -1109,10 +1108,9 @@ public class ImageView extends View { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mDrawable != null) { mDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 62a8bec..394b255 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1623,10 +1623,9 @@ public class ProgressBar extends View { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mProgressDrawable != null) { mProgressDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java index 14d782d..23fa402 100644 --- a/core/java/android/widget/QuickContactBadge.java +++ b/core/java/android/widget/QuickContactBadge.java @@ -112,10 +112,9 @@ public class QuickContactBadge extends ImageView implements OnClickListener { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mOverlay != null) { mOverlay.setHotspot(x, y); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index f7d20b53..82637a1 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1781,7 +1781,9 @@ public class RemoteViews implements Parcelable, Filter { Parcel p = Parcel.obtain(); writeToParcel(p, 0); p.setDataPosition(0); - return new RemoteViews(p); + RemoteViews rv = new RemoteViews(p); + p.recycle(); + return rv; } public String getPackage() { diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 03193a2..cca29cf 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -962,10 +962,9 @@ public class Switch extends CompoundButton { invalidate(); } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mThumbDrawable != null) { mThumbDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 0f51e8b..d470586 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3503,10 +3503,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); final Drawables dr = mDrawables; if (dr != null) { diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl index 017801b..bc1f002 100644 --- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl +++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl @@ -23,6 +23,7 @@ interface IFaceLockInterface { void startUi(IBinder containingWindowToken, int x, int y, int width, int height, boolean useLiveliness); void stopUi(); + void startWithoutUi(); void registerCallback(IFaceLockCallback cb); void unregisterCallback(IFaceLockCallback cb); } diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 002573e..97b1634 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -45,10 +45,9 @@ public class SwipeDismissLayout extends FrameLayout { /** * Called when the layout has been swiped and the position of the window should change. * - * @param progress A number in [-1, 1] representing how far to the left - * or right the window has been swiped. Negative values are swipes - * left, and positives are right. - * @param translate A number in [-w, w], where w is the width of the + * @param progress A number in [0, 1] representing how far to the + * right the window has been swiped + * @param translate A number in [0, w], where w is the width of the * layout. This is equivalent to progress * layout.getWidth(). */ void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate); @@ -207,7 +206,7 @@ public class SwipeDismissLayout extends FrameLayout { private void setProgress(float deltaX) { mTranslationX = deltaX; - if (mProgressListener != null) { + if (mProgressListener != null && deltaX >= 0) { mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX); } } diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java index 87a50a9..fda6479 100644 --- a/core/java/com/android/server/SystemServiceManager.java +++ b/core/java/com/android/server/SystemServiceManager.java @@ -50,8 +50,19 @@ public class SystemServiceManager { * @return The service instance. */ @SuppressWarnings("unchecked") - public SystemService startService(String className) throws ClassNotFoundException { - return startService((Class<SystemService>) Class.forName(className)); + public SystemService startService(String className) { + final Class<SystemService> serviceClass; + try { + serviceClass = (Class<SystemService>)Class.forName(className); + } catch (ClassNotFoundException ex) { + Slog.i(TAG, "Starting " + className); + throw new RuntimeException("Failed to create service " + className + + ": service class not found, usually indicates that the caller should " + + "have called PackageManager.hasSystemFeature() to check whether the " + + "feature is available on this device before trying to start the " + + "services that implement it", ex); + } + return startService(serviceClass); } /** |
