diff options
162 files changed, 3707 insertions, 1358 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 2fe5cbecfcf9..cee8fdb9a856 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -240,6 +240,7 @@ $(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framewo $(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IRemoteControlDisplay.*) $(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/APPS/FeatureSplit1_intermediates/src/com/android/test/split/feature/R.java) $(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/APPS/FeatureSplit2_intermediates/src/com/android/test/split/feature/R.java) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/hardware) # ****************************************************************** # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER diff --git a/api/current.txt b/api/current.txt index 07505b35ccae..2152d6faa922 100644 --- a/api/current.txt +++ b/api/current.txt @@ -20935,6 +20935,7 @@ package android.media { field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height"; field public static final java.lang.String KEY_STRIDE = "stride"; field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema"; + field public static final java.lang.String KEY_TRACK_ID = "track-id"; field public static final java.lang.String KEY_WIDTH = "width"; field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3"; @@ -29047,6 +29048,8 @@ package android.os { ctor public Process(); method public static final long getElapsedCpuTime(); method public static final int getGidForName(java.lang.String); + method public static final long getStartElapsedRealtime(); + method public static final long getStartUptimeMillis(); method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException; method public static final int getUidForName(java.lang.String); method public static final boolean is64Bit(); @@ -34234,7 +34237,8 @@ package android.service.carrier { ctor public CarrierMessagingService(); method public android.os.IBinder onBind(android.content.Intent); method public void onDownloadMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>); - method public void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>); + method public deprecated void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>); + method public void onReceiveTextSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>); method public deprecated void onSendDataSms(byte[], int, java.lang.String, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>); method public void onSendDataSms(byte[], int, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>); method public void onSendMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendMmsResult>); @@ -34245,6 +34249,9 @@ package android.service.carrier { field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2 field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0 field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1 + field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0 + field public static final int RECEIVE_OPTIONS_DROP = 1; // 0x1 + field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE = 2; // 0x2 field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1 field public static final int SEND_STATUS_ERROR = 2; // 0x2 field public static final int SEND_STATUS_OK = 0; // 0x0 diff --git a/api/system-current.txt b/api/system-current.txt index 089ccd367c3b..b5a8513853ba 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -10186,6 +10186,8 @@ package android.content.pm { field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99 field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a field public static final int INSTALL_SUCCEEDED = 1; // 0x1 + field public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff + field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1 field public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ALL = 131072; // 0x20000 field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000 @@ -22425,6 +22427,7 @@ package android.media { field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height"; field public static final java.lang.String KEY_STRIDE = "stride"; field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema"; + field public static final java.lang.String KEY_TRACK_ID = "track-id"; field public static final java.lang.String KEY_WIDTH = "width"; field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3"; @@ -26662,7 +26665,6 @@ package android.net.wifi { method public boolean reconnect(); method public boolean removeNetwork(int); method public boolean saveConfiguration(); - method public boolean setMetered(int, boolean); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); @@ -31339,6 +31341,8 @@ package android.os { ctor public Process(); method public static final long getElapsedCpuTime(); method public static final int getGidForName(java.lang.String); + method public static final long getStartElapsedRealtime(); + method public static final long getStartUptimeMillis(); method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException; method public static final int getUidForName(java.lang.String); method public static final boolean is64Bit(); @@ -36723,7 +36727,8 @@ package android.service.carrier { ctor public CarrierMessagingService(); method public android.os.IBinder onBind(android.content.Intent); method public void onDownloadMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>); - method public void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>); + method public deprecated void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>); + method public void onReceiveTextSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>); method public deprecated void onSendDataSms(byte[], int, java.lang.String, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>); method public void onSendDataSms(byte[], int, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>); method public void onSendMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendMmsResult>); @@ -36734,6 +36739,9 @@ package android.service.carrier { field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2 field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0 field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1 + field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0 + field public static final int RECEIVE_OPTIONS_DROP = 1; // 0x1 + field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE = 2; // 0x2 field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1 field public static final int SEND_STATUS_ERROR = 2; // 0x2 field public static final int SEND_STATUS_OK = 0; // 0x0 diff --git a/api/test-current.txt b/api/test-current.txt index 21a9db03d4c2..98a000e0ade7 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -20946,6 +20946,7 @@ package android.media { field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height"; field public static final java.lang.String KEY_STRIDE = "stride"; field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema"; + field public static final java.lang.String KEY_TRACK_ID = "track-id"; field public static final java.lang.String KEY_WIDTH = "width"; field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3"; @@ -29058,6 +29059,8 @@ package android.os { ctor public Process(); method public static final long getElapsedCpuTime(); method public static final int getGidForName(java.lang.String); + method public static final long getStartElapsedRealtime(); + method public static final long getStartUptimeMillis(); method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException; method public static final int getUidForName(java.lang.String); method public static final boolean is64Bit(); @@ -34251,7 +34254,8 @@ package android.service.carrier { ctor public CarrierMessagingService(); method public android.os.IBinder onBind(android.content.Intent); method public void onDownloadMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>); - method public void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>); + method public deprecated void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>); + method public void onReceiveTextSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>); method public deprecated void onSendDataSms(byte[], int, java.lang.String, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>); method public void onSendDataSms(byte[], int, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>); method public void onSendMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendMmsResult>); @@ -34262,6 +34266,9 @@ package android.service.carrier { field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2 field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0 field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1 + field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0 + field public static final int RECEIVE_OPTIONS_DROP = 1; // 0x1 + field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE = 2; // 0x2 field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1 field public static final int SEND_STATUS_ERROR = 2; // 0x2 field public static final int SEND_STATUS_OK = 0; // 0x0 diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index e721de96540f..5ab2c1d491d7 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -1029,8 +1029,16 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio @Override public void resume() { - if (mPaused) { + if (Looper.myLooper() == null) { + throw new AndroidRuntimeException("Animators may only be resumed from the same " + + "thread that the animator was started on"); + } + if (mPaused && !mResumed) { mResumed = true; + if (mPauseTime > 0) { + AnimationHandler handler = AnimationHandler.getInstance(); + handler.addAnimationFrameCallback(this, 0); + } } super.resume(); } @@ -1235,9 +1243,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio } mLastFrameTime = frameTime; if (mPaused) { - if (mPauseTime < 0) { - mPauseTime = frameTime; - } + mPauseTime = frameTime; + handler.removeCallback(this); return; } else if (mResumed) { mResumed = false; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index be89b20c91c5..b87e9fa2d879 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6599,7 +6599,8 @@ public class Activity extends ContextThemeWrapper if (isAppDebuggable || isDlwarningEnabled) { String dlwarning = getDlWarning(); if (dlwarning != null) { - String appName = getString(mApplication.getApplicationInfo().labelRes); + String appName = getApplicationInfo().loadLabel(getPackageManager()) + .toString(); String warning = "Detected problems with app native libraries\n" + "(please consult log for detail):\n" + dlwarning; if (isAppDebuggable) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4676cc4620f0..a4e5b9049e9d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -577,6 +577,16 @@ public class ActivityManager { } /** + * Return whether a stackId is a stack containing floating windows. Floating windows + * are laid out differently as they are allowed to extend past the display bounds + * without overscan insets. + */ + public static boolean tasksAreFloating(int stackId) { + return stackId == FREEFORM_WORKSPACE_STACK_ID + || stackId == PINNED_STACK_ID; + } + + /** * Returns true if animation specs should be constructed for app transition that moves * the task to the specified stack. */ diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 9d8dca608c43..ff7f70dbd838 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -823,7 +823,21 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } - + case RESIZE_PINNED_STACK_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final boolean hasBounds = data.readInt() != 0; + Rect bounds = null; + if (hasBounds) { + bounds = Rect.CREATOR.createFromParcel(data); + } + final boolean hasTempPinnedTaskBounds = data.readInt() != 0; + Rect tempPinnedTaskBounds = null; + if (hasTempPinnedTaskBounds) { + tempPinnedTaskBounds = Rect.CREATOR.createFromParcel(data); + } + resizePinnedStack(bounds, tempPinnedTaskBounds); + return true; + } case RESIZE_DOCKED_STACK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); final boolean hasBounds = data.readInt() != 0; @@ -3914,6 +3928,31 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + + @Override + public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + if (pinnedBounds != null) { + data.writeInt(1); + pinnedBounds.writeToParcel(data, 0); + } else { + data.writeInt(0); + } + if (tempPinnedTaskBounds != null) { + data.writeInt(1); + tempPinnedTaskBounds.writeToParcel(data, 0); + } else { + data.writeInt(0); + } + mRemote.transact(RESIZE_PINNED_STACK_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + @Override public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1e95c983a84f..1bc33b81ed35 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1788,7 +1788,8 @@ public final class ActivityThread { } /** - * Creates the top level resources for the given package. + * Creates the top level resources for the given package. Will return an existing + * Resources if one has already been created. */ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, String[] libDirs, int displayId, Configuration overrideConfiguration, @@ -1798,6 +1799,19 @@ public final class ActivityThread { pkgInfo.getClassLoader()); } + /** + * Creates a new top level resources for the given package. Will always create a new + * Resources, regardless if one has already been created. + */ + Resources getNewTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, + String[] libDirs, int displayId, Configuration overrideConfiguration, + LoadedApk pkgInfo) { + mResourcesManager.removeTopLevelResources( + resDir, displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo()); + return getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs, + displayId, overrideConfiguration, pkgInfo); + } + final Handler getHandler() { return mH; } @@ -4749,29 +4763,87 @@ public final class ActivityThread { final void handleDispatchPackageBroadcast(int cmd, String[] packages) { boolean hasPkgInfo = false; - if (packages != null) { - synchronized (mResourcesManager) { - for (int i=packages.length-1; i>=0; i--) { - //Slog.i(TAG, "Cleaning old package: " + packages[i]); - if (!hasPkgInfo) { - WeakReference<LoadedApk> ref; - ref = mPackages.get(packages[i]); - if (ref != null && ref.get() != null) { + switch (cmd) { + case IApplicationThread.PACKAGE_REMOVED: + case IApplicationThread.PACKAGE_REMOVED_DONT_KILL: + { + final boolean killApp = cmd == IApplicationThread.PACKAGE_REMOVED; + if (packages == null) { + break; + } + synchronized (mResourcesManager) { + for (int i = packages.length - 1; i >= 0; i--) { + if (!hasPkgInfo) { + WeakReference<LoadedApk> ref = mPackages.get(packages[i]); + if (ref != null && ref.get() != null) { + hasPkgInfo = true; + } else { + ref = mResourcePackages.get(packages[i]); + if (ref != null && ref.get() != null) { + hasPkgInfo = true; + } + } + } + if (killApp) { + mPackages.remove(packages[i]); + mResourcePackages.remove(packages[i]); + } + } + } + break; + } + case IApplicationThread.PACKAGE_REPLACED: + { + if (packages == null) { + break; + } + synchronized (mResourcesManager) { + for (int i = packages.length - 1; i >= 0; i--) { + WeakReference<LoadedApk> ref = mPackages.get(packages[i]); + LoadedApk pkgInfo = ref != null ? ref.get() : null; + if (pkgInfo != null) { hasPkgInfo = true; } else { ref = mResourcePackages.get(packages[i]); - if (ref != null && ref.get() != null) { + pkgInfo = ref != null ? ref.get() : null; + if (pkgInfo != null) { hasPkgInfo = true; } } + // If the package is being replaced, yet it still has a valid + // LoadedApk object, the package was updated with _DONT_KILL. + // Adjust it's internal references to the application info and + // resources. + if (pkgInfo != null) { + try { + final String packageName = packages[i]; + final ApplicationInfo aInfo = + sPackageManager.getApplicationInfo( + packageName, + 0 /*flags*/, + UserHandle.myUserId()); + + if (mActivities.size() > 0) { + for (ActivityClientRecord ar : mActivities.values()) { + if (ar.activityInfo.applicationInfo.packageName + .equals(packageName)) { + ar.activityInfo.applicationInfo = aInfo; + ar.packageInfo = pkgInfo; + } + } + } + final List<String> oldPaths = + sPackageManager.getPreviousCodePaths(packageName); + pkgInfo.updateApplicationInfo(aInfo, oldPaths); + } catch (RemoteException e) { + } + } } - mPackages.remove(packages[i]); - mResourcePackages.remove(packages[i]); } + break; } } - ApplicationPackageManager.handlePackageBroadcast(cmd, packages, - hasPkgInfo); + ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo); } final void handleLowMemory() { @@ -4909,6 +4981,9 @@ public final class ActivityThread { DdmVmInternal.enableRecentAllocations(true); } + // Note when this process has started. + Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); + mBoundApplication = data; mConfiguration = new Configuration(data.config); mCompatConfiguration = new Configuration(data.config); diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index b20c09125733..0fc097e2e706 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -86,6 +86,18 @@ class ApplicationLoaders { String libraryPermittedPath, boolean isShared); + /** + * Adds a new path the classpath of the given loader. + * @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}. + */ + void addPath(ClassLoader classLoader, String dexPath) { + if (!(classLoader instanceof PathClassLoader)) { + throw new IllegalStateException("class loader is not a PathClassLoader"); + } + final PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader; + baseDexClassLoader.addDexPath(dexPath); + } + private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>(); private static final ApplicationLoaders gApplicationLoaders diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index c8411119430f..38f32f7165ed 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1543,9 +1543,9 @@ public class ApplicationPackageManager extends PackageManager { } @Override - public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) { + public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) { try { - mPM.verifyIntentFilter(id, verificationCode, outFailedDomains); + mPM.verifyIntentFilter(id, verificationCode, failedDomains); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index d78f59f06754..70bff800383d 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -171,6 +171,16 @@ public interface IActivityManager extends IInterface { public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) throws RemoteException; + /** + * Resizes the pinned stack. + * + * @param pinnedBounds The bounds for the pinned stack. + * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which + * might be different from the stack bounds to allow more + * flexibility while resizing, or {@code null} if they should be the + * same as the stack bounds. + */ + public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) throws RemoteException; public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException; public List<StackInfo> getAllStackInfos() throws RemoteException; public StackInfo getStackInfo(int stackId) throws RemoteException; @@ -982,4 +992,5 @@ public interface IActivityManager extends IInterface { int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 367; int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368; int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369; + int RESIZE_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 370; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index a3c95916bb98..628bde0891ef 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -123,8 +123,13 @@ public interface IApplicationThread extends IInterface { void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) throws RemoteException; void setSchedulingGroup(int group) throws RemoteException; + // the package has been removed, clean up internal references static final int PACKAGE_REMOVED = 0; static final int EXTERNAL_STORAGE_UNAVAILABLE = 1; + // the package is being modified in-place, don't kill it and retain references to it + static final int PACKAGE_REMOVED_DONT_KILL = 2; + // a previously removed package was replaced with a new version [eg. upgrade, split added, ...] + static final int PACKAGE_REPLACED = 3; void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException; void scheduleCrash(String msg) throws RemoteException; void dumpActivity(FileDescriptor fd, IBinder servicetoken, String prefix, String[] args) diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index b65faa98cdca..cd170780450d 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -58,6 +58,7 @@ import java.lang.reflect.Method; import java.net.URL; import java.util.List; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.Objects; @@ -83,24 +84,25 @@ public final class LoadedApk { private static final String TAG = "LoadedApk"; private final ActivityThread mActivityThread; - private ApplicationInfo mApplicationInfo; final String mPackageName; - private final String mAppDir; - private final String mResDir; - private final String[] mSplitAppDirs; - private final String[] mSplitResDirs; - private final String[] mOverlayDirs; - private final String[] mSharedLibraries; - private final String mDataDir; - private final String mLibDir; - private final File mDataDirFile; - private final File mDeviceEncryptedDataDirFile; - private final File mCredentialEncryptedDataDirFile; + private ApplicationInfo mApplicationInfo; + private String mAppDir; + private String mResDir; + private String[] mSplitAppDirs; + private String[] mSplitResDirs; + private String[] mOverlayDirs; + private String[] mSharedLibraries; + private String mDataDir; + private String mLibDir; + private File mDataDirFile; + private File mDeviceEncryptedDataDirFile; + private File mCredentialEncryptedDataDirFile; private final ClassLoader mBaseClassLoader; private final boolean mSecurityViolation; private final boolean mIncludeCode; private final boolean mRegisterPackage; private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments(); + /** WARNING: This may change. Don't hold external references to it. */ Resources mResources; private ClassLoader mClassLoader; private Application mApplication; @@ -129,23 +131,10 @@ public final class LoadedApk { public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode, boolean registerPackage) { - final int myUid = Process.myUid(); - aInfo = adjustNativeLibraryPaths(aInfo); mActivityThread = activityThread; - mApplicationInfo = aInfo; + setApplicationInfo(aInfo); mPackageName = aInfo.packageName; - mAppDir = aInfo.sourceDir; - mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; - mSplitAppDirs = aInfo.splitSourceDirs; - mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs; - mOverlayDirs = aInfo.resourceDirs; - mSharedLibraries = aInfo.sharedLibraryFiles; - mDataDir = aInfo.dataDir; - mDataDirFile = FileUtils.newFileOrNull(mDataDir); - mDeviceEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceEncryptedDataDir); - mCredentialEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialEncryptedDataDir); - mLibDir = aInfo.nativeLibraryDir; mBaseClassLoader = baseLoader; mSecurityViolation = securityViolation; mIncludeCode = includeCode; @@ -266,130 +255,189 @@ public final class LoadedApk { return ai.sharedLibraryFiles; } - public ClassLoader getClassLoader() { + public void updateApplicationInfo(ApplicationInfo aInfo, List<String> oldPaths) { + setApplicationInfo(aInfo); + + final List<String> newPaths = new ArrayList<>(); + makePaths(mActivityThread, aInfo, newPaths, null /*libPaths*/); + final List<String> addedPaths = new ArrayList<>(newPaths.size()); + + if (oldPaths != null) { + for (String path : newPaths) { + final String apkName = path.substring(path.lastIndexOf(File.separator)); + boolean match = false; + for (String oldPath : oldPaths) { + final String oldApkName = oldPath.substring(path.lastIndexOf(File.separator)); + if (apkName.equals(oldApkName)) { + match = true; + break; + } + } + if (!match) { + addedPaths.add(path); + } + } + } else { + addedPaths.addAll(newPaths); + } synchronized (this) { - if (mClassLoader != null) { - return mClassLoader; + mClassLoader = createOrUpdateClassLoaderLocked(addedPaths); + if (mResources != null) { + mResources = mActivityThread.getNewTopLevelResources(mResDir, mSplitResDirs, + mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, + null /*overrideConfiguration*/, this); } + } + } - if (mPackageName.equals("android")) { - if (mBaseClassLoader == null) { - mClassLoader = ClassLoader.getSystemClassLoader(); - } else { - mClassLoader = mBaseClassLoader; - } - return mClassLoader; - } + private void setApplicationInfo(ApplicationInfo aInfo) { + final int myUid = Process.myUid(); + aInfo = adjustNativeLibraryPaths(aInfo); + mApplicationInfo = aInfo; + mAppDir = aInfo.sourceDir; + mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; + mSplitAppDirs = aInfo.splitSourceDirs; + mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs; + mOverlayDirs = aInfo.resourceDirs; + mSharedLibraries = aInfo.sharedLibraryFiles; + mDataDir = aInfo.dataDir; + mLibDir = aInfo.nativeLibraryDir; + mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir); + mDeviceEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceEncryptedDataDir); + mCredentialEncryptedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialEncryptedDataDir); + } - // Avoid the binder call when the package is the current application package. - // The activity manager will perform ensure that dexopt is performed before - // spinning up the process. - if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) { - final String isa = VMRuntime.getRuntime().vmInstructionSet(); - try { - ActivityThread.getPackageManager().notifyPackageUse(mPackageName); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } + public static void makePaths(ActivityThread activityThread, ApplicationInfo aInfo, + List<String> outZipPaths, List<String> outLibPaths) { + final String appDir = aInfo.sourceDir; + final String[] splitAppDirs = aInfo.splitSourceDirs; + final String libDir = aInfo.nativeLibraryDir; + final String[] sharedLibraries = aInfo.sharedLibraryFiles; + + outZipPaths.clear(); + outZipPaths.add(appDir); + if (splitAppDirs != null) { + Collections.addAll(outZipPaths, splitAppDirs); + } - final List<String> zipPaths = new ArrayList<>(); - final List<String> apkPaths = new ArrayList<>(); - final List<String> libPaths = new ArrayList<>(); + if (outLibPaths != null) { + outLibPaths.clear(); + } - if (mRegisterPackage) { - try { - ActivityManagerNative.getDefault().addPackageDependency(mPackageName); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + /* + * The following is a bit of a hack to inject + * instrumentation into the system: If the app + * being started matches one of the instrumentation names, + * then we combine both the "instrumentation" and + * "instrumented" app into the path, along with the + * concatenation of both apps' shared library lists. + */ + + String instrumentationPackageName = activityThread.mInstrumentationPackageName; + String instrumentationAppDir = activityThread.mInstrumentationAppDir; + String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs; + String instrumentationLibDir = activityThread.mInstrumentationLibDir; + + String instrumentedAppDir = activityThread.mInstrumentedAppDir; + String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs; + String instrumentedLibDir = activityThread.mInstrumentedLibDir; + String[] instrumentationLibs = null; + + if (appDir.equals(instrumentationAppDir) + || appDir.equals(instrumentedAppDir)) { + outZipPaths.clear(); + outZipPaths.add(instrumentationAppDir); + if (instrumentationSplitAppDirs != null) { + Collections.addAll(outZipPaths, instrumentationSplitAppDirs); + } + outZipPaths.add(instrumentedAppDir); + if (instrumentedSplitAppDirs != null) { + Collections.addAll(outZipPaths, instrumentedSplitAppDirs); } - zipPaths.add(mAppDir); - if (mSplitAppDirs != null) { - Collections.addAll(zipPaths, mSplitAppDirs); + if (outLibPaths != null) { + outLibPaths.add(instrumentationLibDir); + outLibPaths.add(instrumentedLibDir); } - libPaths.add(mLibDir); + if (!instrumentedAppDir.equals(instrumentationAppDir)) { + instrumentationLibs = getLibrariesFor(instrumentationPackageName); + } + } - /* - * The following is a bit of a hack to inject - * instrumentation into the system: If the app - * being started matches one of the instrumentation names, - * then we combine both the "instrumentation" and - * "instrumented" app into the path, along with the - * concatenation of both apps' shared library lists. - */ + if (outLibPaths != null) { + if (outLibPaths.isEmpty()) { + outLibPaths.add(libDir); + } - String instrumentationPackageName = mActivityThread.mInstrumentationPackageName; - String instrumentationAppDir = mActivityThread.mInstrumentationAppDir; - String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs; - String instrumentationLibDir = mActivityThread.mInstrumentationLibDir; - - String instrumentedAppDir = mActivityThread.mInstrumentedAppDir; - String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs; - String instrumentedLibDir = mActivityThread.mInstrumentedLibDir; - String[] instrumentationLibs = null; - - if (mAppDir.equals(instrumentationAppDir) - || mAppDir.equals(instrumentedAppDir)) { - zipPaths.clear(); - zipPaths.add(instrumentationAppDir); - if (instrumentationSplitAppDirs != null) { - Collections.addAll(zipPaths, instrumentationSplitAppDirs); - } - zipPaths.add(instrumentedAppDir); - if (instrumentedSplitAppDirs != null) { - Collections.addAll(zipPaths, instrumentedSplitAppDirs); + // Add path to libraries in apk for current abi. Do this now because more entries + // will be added to zipPaths that shouldn't be part of the library path. + if (aInfo.primaryCpuAbi != null) { + for (String apk : outZipPaths) { + outLibPaths.add(apk + "!/lib/" + aInfo.primaryCpuAbi); } + } - libPaths.clear(); - libPaths.add(instrumentationLibDir); - libPaths.add(instrumentedLibDir); + if (aInfo.isSystemApp() && !aInfo.isUpdatedSystemApp()) { + // Add path to system libraries to libPaths; + // Access to system libs should be limited + // to bundled applications; this is why updated + // system apps are not included. + outLibPaths.add(System.getProperty("java.library.path")); + } + } - if (!instrumentedAppDir.equals(instrumentationAppDir)) { - instrumentationLibs = getLibrariesFor(instrumentationPackageName); + if (sharedLibraries != null) { + for (String lib : sharedLibraries) { + if (!outZipPaths.contains(lib)) { + outZipPaths.add(0, lib); } } + } - apkPaths.addAll(zipPaths); - - if (mSharedLibraries != null) { - for (String lib : mSharedLibraries) { - if (!zipPaths.contains(lib)) { - zipPaths.add(0, lib); - } + if (instrumentationLibs != null) { + for (String lib : instrumentationLibs) { + if (!outZipPaths.contains(lib)) { + outZipPaths.add(0, lib); } } + } - if (instrumentationLibs != null) { - for (String lib : instrumentationLibs) { - if (!zipPaths.contains(lib)) { - zipPaths.add(0, lib); - } + final String zip = TextUtils.join(File.pathSeparator, outZipPaths); + } + + private ClassLoader createOrUpdateClassLoaderLocked(List<String> addedPaths) { + final ClassLoader classLoader; + if (mIncludeCode && !mPackageName.equals("android")) { + // Avoid the binder call when the package is the current application package. + // The activity manager will perform ensure that dexopt is performed before + // spinning up the process. + if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) { + VMRuntime.getRuntime().vmInstructionSet(); + try { + ActivityThread.getPackageManager().notifyPackageUse(mPackageName); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); } } - final String zip = mIncludeCode ? TextUtils.join(File.pathSeparator, zipPaths) : ""; + final List<String> zipPaths = new ArrayList<>(); + final List<String> libPaths = new ArrayList<>(); - // Add path to libraries in apk for current abi - if (mApplicationInfo.primaryCpuAbi != null) { - for (String apk : apkPaths) { - libPaths.add(apk + "!/lib/" + mApplicationInfo.primaryCpuAbi); + if (mRegisterPackage) { + try { + ActivityManagerNative.getDefault().addPackageDependency(mPackageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } + makePaths(mActivityThread, mApplicationInfo, zipPaths, libPaths); + final String zip = TextUtils.join(File.pathSeparator, zipPaths); + final boolean isBundledApp = mApplicationInfo.isSystemApp() + && !mApplicationInfo.isUpdatedSystemApp(); String libraryPermittedPath = mDataDir; - boolean isBundledApp = false; - - if (mApplicationInfo.isSystemApp() && !mApplicationInfo.isUpdatedSystemApp()) { - isBundledApp = true; - // Add path to system libraries to libPaths; - // Access to system libs should be limited - // to bundled applications; this is why updated - // system apps are not included. - libPaths.add(System.getProperty("java.library.path")); - + if (isBundledApp) { // This is necessary to grant bundled apps access to // libraries located in subdirectories of /system/lib libraryPermittedPath += File.pathSeparator + @@ -414,15 +462,42 @@ public final class LoadedApk { Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + librarySearchPath); - // Temporarily disable logging of disk reads on the Looper thread - // as this is early and necessary. - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + if (mClassLoader == null) { + // Temporarily disable logging of disk reads on the Looper thread + // as this is early and necessary. + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + + classLoader = ApplicationLoaders.getDefault().getClassLoader(zip, + mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, + libraryPermittedPath, mBaseClassLoader); - mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, - mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, - libraryPermittedPath, mBaseClassLoader); + StrictMode.setThreadPolicy(oldPolicy); + } else if (addedPaths != null && addedPaths.size() > 0) { + final String add = TextUtils.join(File.pathSeparator, addedPaths); + ApplicationLoaders.getDefault().addPath(mClassLoader, add); + classLoader = mClassLoader; + } else { + classLoader = mClassLoader; + } + } else { + if (mClassLoader == null) { + if (mBaseClassLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } else { + classLoader = mBaseClassLoader; + } + } else { + classLoader = mClassLoader; + } + } + return classLoader; + } - StrictMode.setThreadPolicy(oldPolicy); + public ClassLoader getClassLoader() { + synchronized (this) { + if (mClassLoader == null) { + mClassLoader = createOrUpdateClassLoaderLocked(null /*addedPaths*/); + } return mClassLoader; } } @@ -1103,7 +1178,6 @@ public final class LoadedApk { private RuntimeException mUnbindLocation; - private boolean mDied; private boolean mForgotten; private static class ConnectionInfo { @@ -1202,7 +1276,6 @@ public final class LoadedApk { ServiceDispatcher.ConnectionInfo old; synchronized (this) { - mDied = true; old = mActiveConnections.remove(name); if (old == null || old.binder != service) { // Death for someone different than who we last @@ -1237,7 +1310,6 @@ public final class LoadedApk { if (service != null) { // A new service is being connected... set it all up. - mDied = false; info = new ConnectionInfo(); info.binder = service; info.deathMonitor = new DeathMonitor(name, service); diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 94e584eeaa98..a6612f67e955 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -288,6 +288,19 @@ public class ResourcesManager { } } + /** + * Removes the top level Resources for applications with the given compatibility info. + * @see #getTopLevelResources(String, String[], String[], String[], int, Configuration, CompatibilityInfo, ClassLoader) + */ + void removeTopLevelResources(String resDir, int displayId, Configuration overrideConfiguration, + CompatibilityInfo compatInfo) { + final float scale = compatInfo.applicationScale; + final Configuration overrideConfigCopy = (overrideConfiguration != null) + ? new Configuration(overrideConfiguration) : null; + final ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale); + mActiveResources.remove(key); + } + /* package */ void setDefaultLocalesLocked(LocaleList locales) { final int bestLocale; if (mHasNonSystemLocales) { diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 9959f2749609..0389085ae0ee 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -530,4 +530,7 @@ interface IPackageManager { String getServicesSystemSharedLibraryPackageName(); boolean isPackageDeviceAdminOnAnyUser(String packageName); + + List<String> getPreviousCodePaths(in String packageName); + } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 2cbb7825ec80..700a40db1f5f 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1042,6 +1042,11 @@ public class PackageInstaller { } /** {@hide} */ + public void setInstallFlagsDontKillApp() { + installFlags |= PackageManager.INSTALL_DONT_KILL_APP; + } + + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); pw.printHexPair("installFlags", installFlags); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 27056a3fc5a5..0dc856c5da4f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -538,6 +538,7 @@ public abstract class PackageManager { INSTALL_FORCE_VOLUME_UUID, INSTALL_FORCE_PERMISSION_PROMPT, INSTALL_EPHEMERAL, + INSTALL_DONT_KILL_APP, }) @Retention(RetentionPolicy.SOURCE) public @interface InstallFlags {} @@ -640,6 +641,15 @@ public abstract class PackageManager { public static final int INSTALL_EPHEMERAL = 0x00000800; /** + * Flag parameter for {@link #installPackage} to indicate that this package contains + * a feature split to an existing application and the existing application should not + * be killed during the installation process. + * + * @hide + */ + public static final int INSTALL_DONT_KILL_APP = 0x00001000; + + /** * Flag parameter for * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate * that you don't want to kill the app containing the component. Be careful when you set this @@ -1088,6 +1098,7 @@ public abstract class PackageManager { DELETE_KEEP_DATA, DELETE_ALL_USERS, DELETE_SYSTEM_APP, + DELETE_DONT_KILL_APP, }) @Retention(RetentionPolicy.SOURCE) public @interface DeleteFlags {} @@ -1120,6 +1131,15 @@ public abstract class PackageManager { public static final int DELETE_SYSTEM_APP = 0x00000004; /** + * Flag parameter for {@link #deletePackage} to indicate that, if you are calling + * uninstall on a package that is replaced to provide new feature splits, the + * existing application should not be killed during the removal process. + * + * @hide + */ + public static final int DELETE_DONT_KILL_APP = 0x00000008; + + /** * Return code for when package deletion succeeds. This is passed to the * {@link IPackageDeleteObserver} if the system succeeded in deleting the * package. @@ -1292,6 +1312,7 @@ public abstract class PackageManager { * * @hide */ + @SystemApi public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; /** @@ -1301,6 +1322,7 @@ public abstract class PackageManager { * * @hide */ + @SystemApi public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; /** @@ -4711,8 +4733,8 @@ public abstract class PackageManager { /** * Allows a package listening to the - * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION intent filter verification - * broadcast} to respond to the package manager. The response must include + * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION} intent filter verification + * broadcast to respond to the package manager. The response must include * the {@code verificationCode} which is one of * {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS} or * {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}. @@ -4721,7 +4743,7 @@ public abstract class PackageManager { * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra. * @param verificationCode either {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS} * or {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}. - * @param outFailedDomains a list of failed domains if the verificationCode is + * @param failedDomains a list of failed domains if the verificationCode is * {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}, otherwise null; * @throws SecurityException if the caller does not have the * INTENT_FILTER_VERIFICATION_AGENT permission. @@ -4730,7 +4752,7 @@ public abstract class PackageManager { */ @SystemApi public abstract void verifyIntentFilter(int verificationId, int verificationCode, - List<String> outFailedDomains); + List<String> failedDomains); /** * Get the status of a Domain Verification Result for an IntentFilter. This is diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 7fe7f84e0ec9..89f2fc4334a4 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -16,6 +16,8 @@ package android.content.pm; +import android.content.pm.PackageManager.NameNotFoundException; + import java.util.List; /** @@ -125,4 +127,17 @@ public abstract class PackageManagerInternal { * @return True a permissions review is required. */ public abstract boolean isPermissionsReviewRequired(String packageName, int userId); + + /** + * Gets all of the information we know about a particular package. + * + * @param packageName The package name to find. + * @param userId The user under which to check. + * + * @return An {@link ApplicationInfo} containing information about the + * package. + * @throws NameNotFoundException if a package with the given name cannot be + * found on the system. + */ + public abstract ApplicationInfo getApplicationInfo(String packageName, int userId); } diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 20c216826531..9e360e11bf8b 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -200,6 +200,14 @@ public abstract class NetworkAgent extends Handler { */ public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15; + /** + * Sent by ConnectivityService to the NetworkAgent to install an APF program in the network + * chipset for use to filter packets. + * + * obj = byte[] containing the APF program bytecode. + */ + public static final int CMD_PUSH_APF_PROGRAM = BASE + 16; + public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score) { this(looper, context, logTag, ni, nc, lp, score, null); @@ -319,6 +327,10 @@ public abstract class NetworkAgent extends Handler { preventAutomaticReconnect(); break; } + case CMD_PUSH_APF_PROGRAM: { + installPacketFilter((byte[]) msg.obj); + break; + } } } @@ -494,6 +506,15 @@ public abstract class NetworkAgent extends Handler { protected void preventAutomaticReconnect() { } + /** + * Install a packet filter. + * @param filter an APF program to filter incoming packets. + * @return {@code true} if filter successfully installed, {@code false} otherwise. + */ + protected boolean installPacketFilter(byte[] filter) { + return false; + } + protected void log(String s) { Log.d(LOG_TAG, "NetworkAgent: " + s); } diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java index 5511a248b6aa..748699eff4cf 100644 --- a/core/java/android/net/NetworkMisc.java +++ b/core/java/android/net/NetworkMisc.java @@ -56,6 +56,22 @@ public class NetworkMisc implements Parcelable { */ public String subscriberId; + /** + * Version of APF instruction set supported for packet filtering. 0 indicates no support for + * packet filtering using APF programs. + */ + public int apfVersionSupported; + + /** + * Maximum size of APF program allowed. + */ + public int maximumApfProgramSize; + + /** + * Format of packets passed to APF filter. Should be one of ARPHRD_* + */ + public int apfPacketFormat; + public NetworkMisc() { } @@ -65,6 +81,9 @@ public class NetworkMisc implements Parcelable { explicitlySelected = nm.explicitlySelected; acceptUnvalidated = nm.acceptUnvalidated; subscriberId = nm.subscriberId; + apfVersionSupported = nm.apfVersionSupported; + maximumApfProgramSize = nm.maximumApfProgramSize; + apfPacketFormat = nm.apfPacketFormat; } } @@ -79,6 +98,9 @@ public class NetworkMisc implements Parcelable { out.writeInt(explicitlySelected ? 1 : 0); out.writeInt(acceptUnvalidated ? 1 : 0); out.writeString(subscriberId); + out.writeInt(apfVersionSupported); + out.writeInt(maximumApfProgramSize); + out.writeInt(apfPacketFormat); } public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() { @@ -89,6 +111,9 @@ public class NetworkMisc implements Parcelable { networkMisc.explicitlySelected = in.readInt() != 0; networkMisc.acceptUnvalidated = in.readInt() != 0; networkMisc.subscriberId = in.readString(); + networkMisc.apfVersionSupported = in.readInt(); + networkMisc.maximumApfProgramSize = in.readInt(); + networkMisc.apfPacketFormat = in.readInt(); return networkMisc; } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 94de93329294..8738424e1a50 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -68,10 +68,12 @@ public class NetworkPolicyManager { public static final int FIREWALL_CHAIN_NONE = 0; public static final int FIREWALL_CHAIN_DOZABLE = 1; public static final int FIREWALL_CHAIN_STANDBY = 2; + public static final int FIREWALL_CHAIN_POWERSAVE = 3; public static final String FIREWALL_CHAIN_NAME_NONE = "none"; public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable"; public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby"; + public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave"; private static final boolean ALLOW_PLATFORM_APP_POLICY = true; diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index c6d919f4d77e..555032d522bf 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -62,6 +62,13 @@ public class NetworkUtils { public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException; /** + * Attaches a socket filter that accepts ICMP6 router advertisement packets to the given socket. + * @param fd the socket's {@link FileDescriptor}. + * @param packetType the hardware address type, one of ARPHRD_*. + */ + public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException; + + /** * Binds the current process to the network designated by {@code netId}. All sockets created * in the future (and not explicitly bound via a bound {@link SocketFactory} (see * {@link Network#getSocketFactory}) will be bound to this network. Note that if this diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 9984755d316f..8fd3b0c0035f 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -383,6 +383,9 @@ public class Process { public static final int SIGNAL_KILL = 9; public static final int SIGNAL_USR1 = 10; + private static long sStartElapsedRealtime; + private static long sStartUptimeMillis; + /** * State for communicating with the zygote process. * @@ -772,6 +775,26 @@ public class Process { public static final native long getElapsedCpuTime(); /** + * Return the {@link SystemClock#elapsedRealtime()} at which this process was started. + */ + public static final long getStartElapsedRealtime() { + return sStartElapsedRealtime; + } + + /** + * Return the {@link SystemClock#uptimeMillis()} at which this process was started. + */ + public static final long getStartUptimeMillis() { + return sStartUptimeMillis; + } + + /** @hide */ + public static final void setStartTimes(long elapsedRealtime, long uptimeMillis) { + sStartElapsedRealtime = elapsedRealtime; + sStartUptimeMillis = uptimeMillis; + } + + /** * Returns true if the current process is a 64-bit runtime. */ public static final boolean is64Bit() { diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 3788c746826d..03a1ca60ecf2 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -568,6 +568,22 @@ public final class DocumentsContract { * @hide */ public static final int FLAG_HAS_SETTINGS = 1 << 17; + + /** + * Flag indicating that this root is on removable SD card storage. + * + * @see #COLUMN_FLAGS + * @hide + */ + public static final int FLAG_REMOVABLE_SD = 1 << 18; + + /** + * Flag indicating that this root is on removable USB storage. + * + * @see #COLUMN_FLAGS + * @hide + */ + public static final int FLAG_REMOVABLE_USB = 1 << 19; } /** diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java index f5396a347cf9..140341cfaece 100644 --- a/core/java/android/service/carrier/CarrierMessagingService.java +++ b/core/java/android/service/carrier/CarrierMessagingService.java @@ -51,6 +51,30 @@ public abstract class CarrierMessagingService extends Service { = "android.service.carrier.CarrierMessagingService"; /** + * The default bitmask value passed to the callback of {@link #onReceiveTextSms} with all + * {@code RECEIVE_OPTIONS_x} flags cleared to indicate that the message should be kept and a + * new message notification should be shown. + * + * @see #RECEIVE_OPTIONS_DROP + * @see #RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE + */ + public static final int RECEIVE_OPTIONS_DEFAULT = 0; + + /** + * Used to set the flag in the bitmask passed to the callback of {@link #onReceiveTextSms} to + * indicate that the inbound SMS should be dropped. + */ + public static final int RECEIVE_OPTIONS_DROP = 0x1; + + /** + * Used to set the flag in the bitmask passed to the callback of {@link #onReceiveTextSms} to + * indicate that a new message notification should not be shown to the user when the + * credential-encrypted storage of the device is not available before the user unlocks the + * phone. It is only applicable to devices that support file-based encryption. + */ + public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE = 0x2; + + /** * Indicates that an SMS or MMS message was successfully sent. */ public static final int SEND_STATUS_OK = 0; @@ -96,7 +120,9 @@ public abstract class CarrierMessagingService extends Service { * @param subId SMS subscription ID of the SIM * @param callback result callback. Call with {@code true} to keep an inbound SMS message and * deliver to SMS apps, and {@code false} to drop the message. + * @deprecated Use {@link #onReceiveTextSms} instead. */ + @Deprecated public void onFilterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort, int subId, @NonNull ResultCallback<Boolean> callback) { // optional @@ -107,6 +133,36 @@ public abstract class CarrierMessagingService extends Service { } /** + * Override this method to filter inbound SMS messages. + * + * <p>This method will be called once for every incoming text SMS. You can invoke the callback + * with a bitmask to tell the platform how to handle the SMS. For a SMS received on a + * file-based encryption capable device while the credential-encrypted storage is not available, + * this method will be called for the second time when the credential-encrypted storage becomes + * available after the user unlocks the phone, if the bit {@link #RECEIVE_OPTIONS_DROP} is not + * set when invoking the callback. + * + * @param pdu the PDUs of the message + * @param format the format of the PDUs, typically "3gpp" or "3gpp2" + * @param destPort the destination port of a binary SMS, this will be -1 for text SMS + * @param subId SMS subscription ID of the SIM + * @param callback result callback. Call with a bitmask integer to indicate how the incoming + * text SMS should be handled by the platform. Use {@link #RECEIVE_OPTIONS_DROP} and + * {@link #RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE} + * to set the flags in the bitmask. + */ + public void onReceiveTextSms(@NonNull MessagePdu pdu, @NonNull String format, + int destPort, int subId, @NonNull final ResultCallback<Integer> callback) { + onFilterSms(pdu, format, destPort, subId, new ResultCallback<Boolean>() { + @Override + public void onReceiveResult(Boolean result) throws RemoteException { + callback.onReceiveResult(result ? RECEIVE_OPTIONS_DEFAULT : RECEIVE_OPTIONS_DROP + | RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_ENCRYPTED_STORAGE_UNAVAILABLE); + } + }); + } + + /** * Override this method to intercept text SMSs sent from the device. * @deprecated Override {@link #onSendTextSms} below instead. * @@ -408,10 +464,11 @@ public abstract class CarrierMessagingService extends Service { @Override public void filterSms(MessagePdu pdu, String format, int destPort, int subId, final ICarrierMessagingCallback callback) { - onFilterSms(pdu, format, destPort, subId, new ResultCallback<Boolean>() { + onReceiveTextSms(pdu, format, destPort, subId, + new ResultCallback<Integer>() { @Override - public void onReceiveResult(final Boolean result) throws RemoteException { - callback.onFilterComplete(result); + public void onReceiveResult(Integer options) throws RemoteException { + callback.onFilterComplete(options); } }); } diff --git a/core/java/android/service/carrier/ICarrierMessagingCallback.aidl b/core/java/android/service/carrier/ICarrierMessagingCallback.aidl index 6118a20ca006..2753669fae84 100644 --- a/core/java/android/service/carrier/ICarrierMessagingCallback.aidl +++ b/core/java/android/service/carrier/ICarrierMessagingCallback.aidl @@ -22,7 +22,7 @@ package android.service.carrier; * @hide */ oneway interface ICarrierMessagingCallback { - void onFilterComplete(boolean keepMessage); + void onFilterComplete(int result); void onSendSmsComplete(int result, int messageRef); void onSendMultipartSmsComplete(int result, in int[] messageRefs); void onSendMmsComplete(int result, in byte[] sendConfPdu); diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index fbd992466f5f..bd376ead2334 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -224,7 +224,7 @@ public class Linkify { } if ((mask & EMAIL_ADDRESSES) != 0) { - gatherLinks(links, text, Patterns.EMAIL_ADDRESS, + gatherLinks(links, text, Patterns.AUTOLINK_EMAIL_ADDRESS, new String[] { "mailto:" }, null, null); } diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java index 9f2bcfd3a136..9ed485018c2d 100644 --- a/core/java/android/util/Patterns.java +++ b/core/java/android/util/Patterns.java @@ -394,6 +394,36 @@ public class Patterns { public static final Pattern AUTOLINK_WEB_URL = Pattern.compile( "(" + WEB_URL_WITH_PROTOCOL + "|" + WEB_URL_WITHOUT_PROTOCOL + ")"); + /** + * Regular expression for valid email characters. Does not include some of the valid characters + * defined in RFC5321: #&~!^`{}/=$*?| + */ + private static final String EMAIL_CHAR = LABEL_CHAR + "\\+\\-_%'"; + + /** + * Regular expression for local part of an email address. RFC5321 section 4.5.3.1.1 limits + * the local part to be at most 64 octets. + */ + private static final String EMAIL_ADDRESS_LOCAL_PART = + "[" + EMAIL_CHAR + "]" + "(?:[" + EMAIL_CHAR + "\\.]{1,62}[" + EMAIL_CHAR + "])?"; + + /** + * Regular expression for the domain part of an email address. RFC5321 section 4.5.3.1.2 limits + * the domain to be at most 255 octets. + */ + private static final String EMAIL_ADDRESS_DOMAIN = + "(?=.{1,255}(?:\\s|$|^))" + HOST_NAME; + + /** + * Regular expression pattern to match email addresses. It excludes double quoted local parts + * and the special characters #&~!^`{}/=$*?| that are included in RFC5321. + * @hide + */ + public static final Pattern AUTOLINK_EMAIL_ADDRESS = Pattern.compile("(" + WORD_BOUNDARY + + "(?:" + EMAIL_ADDRESS_LOCAL_PART + "@" + EMAIL_ADDRESS_DOMAIN + ")" + + WORD_BOUNDARY + ")" + ); + public static final Pattern EMAIL_ADDRESS = Pattern.compile( "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 1d242d38ab58..434e3eb8237e 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -117,6 +117,16 @@ public class EditText extends TextView { Selection.extendSelection(getText(), index); } + /** + * Causes words in the text that are longer than the view's width to be ellipsized instead of + * broken in the middle. {@link TextUtils.TruncateAt#MARQUEE + * TextUtils.TruncateAt#MARQUEE} is not supported. + * + * @param ellipsis Type of ellipsis to be applied. + * @throws IllegalArgumentException When the value of <code>ellipsis</code> parameter is + * {@link TextUtils.TruncateAt#MARQUEE}. + * @see TextView#setEllipsize(TextUtils.TruncateAt) + */ @Override public void setEllipsize(TextUtils.TruncateAt ellipsis) { if (ellipsis == TextUtils.TruncateAt.MARQUEE) { diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 881e5cd62794..e949c52fc58b 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3160,6 +3160,7 @@ public class Editor { mTextView.getContext(), mTextView.mTextEditSuggestionHighlightStyle); private TextView mAddToDictionaryButton; private TextView mDeleteButton; + private ListView mSuggestionListView; private SuggestionSpan mMisspelledSpan; private int mContainerMarginWidth; private int mContainerMarginTop; @@ -3177,7 +3178,7 @@ public class Editor { ((Spannable) mTextView.getText()).removeSpan(mSuggestionRangeSpan); mTextView.setCursorVisible(mCursorWasVisibleBeforeSuggestions); - if (hasInsertionController()) { + if (hasInsertionController() && !extractedTextModeWillBeStarted()) { getInsertionController().show(); } } @@ -3213,12 +3214,12 @@ public class Editor { mClippingLimitLeft = lp.leftMargin; mClippingLimitRight = lp.rightMargin; - final ListView suggestionListView = (ListView) relativeLayout.findViewById( + mSuggestionListView = (ListView) relativeLayout.findViewById( com.android.internal.R.id.suggestionContainer); mSuggestionsAdapter = new SuggestionAdapter(); - suggestionListView.setAdapter(mSuggestionsAdapter); - suggestionListView.setOnItemClickListener(this); + mSuggestionListView.setAdapter(mSuggestionsAdapter); + mSuggestionListView.setOnItemClickListener(this); // Inflate the suggestion items once and for all. mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS]; @@ -3327,6 +3328,9 @@ public class Editor { @Override public void show() { if (!(mTextView.getText() instanceof Editable)) return; + if (extractedTextModeWillBeStarted()) { + return; + } if (updateSuggestions()) { mCursorWasVisibleBeforeSuggestions = mCursorVisible; @@ -3374,6 +3378,7 @@ public class Editor { popupBackground.getPadding(mTempRect); width += mTempRect.left + mTempRect.right; } + mSuggestionListView.getLayoutParams().width = width; mPopupWindow.setWidth(width); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f19bf02a238f..18f1ae5ae592 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -20,6 +20,7 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; import android.R; import android.annotation.ColorInt; import android.annotation.DrawableRes; +import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; @@ -5835,8 +5836,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int layoutDirection = getLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - if (mEllipsize == TextUtils.TruncateAt.MARQUEE && - mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { + if (isMarqueeFadeEnabled()) { if (!mSingleLine && getLineCount() == 1 && canMarquee() && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) { final int width = mRight - mLeft; @@ -7888,7 +7888,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Causes words in the text that are longer than the view is wide + * Causes words in the text that are longer than the view's width * to be ellipsized instead of broken in the middle. You may also * want to {@link #setSingleLine} or {@link #setHorizontallyScrolling} * to constrain the text to a single line. Use <code>null</code> @@ -8616,78 +8616,59 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected float getLeftFadingEdgeStrength() { - if (mEllipsize == TextUtils.TruncateAt.MARQUEE && - mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { - if (mMarquee != null && !mMarquee.isStopped()) { - final Marquee marquee = mMarquee; - if (marquee.shouldDrawLeftFade()) { - final float scroll = marquee.getScroll(); - return scroll / getHorizontalFadingEdgeLength(); - } else { - return 0.0f; - } - } else if (getLineCount() == 1) { - final int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - return 0.0f; - case Gravity.RIGHT: - return (mLayout.getLineRight(0) - (mRight - mLeft) - - getCompoundPaddingLeft() - getCompoundPaddingRight() - - mLayout.getLineLeft(0)) / getHorizontalFadingEdgeLength(); - case Gravity.CENTER_HORIZONTAL: - case Gravity.FILL_HORIZONTAL: - final int textDirection = mLayout.getParagraphDirection(0); - if (textDirection == Layout.DIR_LEFT_TO_RIGHT) { - return 0.0f; - } else { - return (mLayout.getLineRight(0) - (mRight - mLeft) - - getCompoundPaddingLeft() - getCompoundPaddingRight() - - mLayout.getLineLeft(0)) / getHorizontalFadingEdgeLength(); - } - } + if (isMarqueeFadeEnabled() && mMarquee != null && !mMarquee.isStopped()) { + final Marquee marquee = mMarquee; + if (marquee.shouldDrawLeftFade()) { + return getHorizontalFadingEdgeStrength(marquee.getScroll(), 0.0f); + } else { + return 0.0f; } + } else if (getLineCount() == 1) { + final float lineLeft = getLayout().getLineLeft(0); + if(lineLeft > mScrollX) return 0.0f; + return getHorizontalFadingEdgeStrength(mScrollX, lineLeft); } return super.getLeftFadingEdgeStrength(); } @Override protected float getRightFadingEdgeStrength() { - if (mEllipsize == TextUtils.TruncateAt.MARQUEE && - mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { - if (mMarquee != null && !mMarquee.isStopped()) { - final Marquee marquee = mMarquee; - final float maxFadeScroll = marquee.getMaxFadeScroll(); - final float scroll = marquee.getScroll(); - return (maxFadeScroll - scroll) / getHorizontalFadingEdgeLength(); - } else if (getLineCount() == 1) { - final int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() - - getCompoundPaddingRight(); - final float lineWidth = mLayout.getLineWidth(0); - return (lineWidth - textWidth) / getHorizontalFadingEdgeLength(); - case Gravity.RIGHT: - return 0.0f; - case Gravity.CENTER_HORIZONTAL: - case Gravity.FILL_HORIZONTAL: - final int textDirection = mLayout.getParagraphDirection(0); - if (textDirection == Layout.DIR_RIGHT_TO_LEFT) { - return 0.0f; - } else { - return (mLayout.getLineWidth(0) - ((mRight - mLeft) - - getCompoundPaddingLeft() - getCompoundPaddingRight())) / - getHorizontalFadingEdgeLength(); - } - } - } + if (isMarqueeFadeEnabled() && mMarquee != null && !mMarquee.isStopped()) { + final Marquee marquee = mMarquee; + return getHorizontalFadingEdgeStrength(marquee.getMaxFadeScroll(), marquee.getScroll()); + } else if (getLineCount() == 1) { + final float rightEdge = mScrollX + (getWidth() - getCompoundPaddingLeft() - + getCompoundPaddingRight()); + final float lineRight = getLayout().getLineRight(0); + if(lineRight < rightEdge) return 0.0f; + return getHorizontalFadingEdgeStrength(rightEdge, lineRight); } return super.getRightFadingEdgeStrength(); } + /** + * Calculates the fading edge strength as the ratio of the distance between two + * horizontal positions to {@link View#getHorizontalFadingEdgeLength()}. Uses the absolute + * value for the distance calculation. + * + * @param position1 A horizontal position. + * @param position2 A horizontal position. + * @return Fading edge strength between [0.0f, 1.0f]. + */ + @FloatRange(from=0.0, to=1.0) + private final float getHorizontalFadingEdgeStrength(float position1, float position2) { + final int horizontalFadingEdgeLength = getHorizontalFadingEdgeLength(); + if(horizontalFadingEdgeLength == 0) return 0.0f; + final float diff = Math.abs(position1 - position2); + if(diff > horizontalFadingEdgeLength) return 1.0f; + return diff / horizontalFadingEdgeLength; + } + + private final boolean isMarqueeFadeEnabled() { + return mEllipsize == TextUtils.TruncateAt.MARQUEE && + mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS; + } + @Override protected int computeHorizontalScrollRange() { if (mLayout != null) { diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java index aca93abddf85..d8d6e56ac69a 100644 --- a/core/java/com/android/internal/app/LocaleHelper.java +++ b/core/java/com/android/internal/app/LocaleHelper.java @@ -180,14 +180,16 @@ public class LocaleHelper { */ public static final class LocaleInfoComparator implements Comparator<LocaleStore.LocaleInfo> { private final Collator mCollator; + private final boolean mCountryMode; /** * Constructor. * * @param sortLocale the locale to be used for sorting. */ - public LocaleInfoComparator(Locale sortLocale) { + public LocaleInfoComparator(Locale sortLocale, boolean countryMode) { mCollator = Collator.getInstance(sortLocale); + mCountryMode = countryMode; } /** @@ -202,9 +204,9 @@ public class LocaleHelper { public int compare(LocaleStore.LocaleInfo lhs, LocaleStore.LocaleInfo rhs) { // We don't care about the various suggestion types, just "suggested" (!= 0) // and "all others" (== 0) - if (lhs.isSuggested() == rhs.isSuggested()) { + if (mCountryMode || (lhs.isSuggested() == rhs.isSuggested())) { // They are in the same "bucket" (suggested / others), so we compare the text - return mCollator.compare(lhs.getLabel(), rhs.getLabel()); + return mCollator.compare(lhs.getLabel(mCountryMode), rhs.getLabel(mCountryMode)); } else { // One locale is suggested and one is not, so we put them in different "buckets" return lhs.isSuggested() ? -1 : 1; diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index 956ee8c9242d..2ea225f99068 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -50,7 +50,6 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O private Set<LocaleStore.LocaleInfo> mLocaleList; private LocaleStore.LocaleInfo mParentLocale; private boolean mTranslatedOnly = false; - private boolean mCountryMode = false; /** * Other classes can register to be notified when a locale was selected. @@ -70,15 +69,14 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O boolean translatedOnly) { LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); boolean shouldShowTheList = localePicker.setListener(context, listener, parent, - true /* country mode */, translatedOnly); + translatedOnly); return shouldShowTheList ? localePicker : null; } public static LocalePickerWithRegion createLanguagePicker(Context context, LocaleSelectedListener listener, boolean translatedOnly) { LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); - localePicker.setListener(context, listener, null, - false /* language mode */, translatedOnly); + localePicker.setListener(context, listener, /* parent */ null, translatedOnly); return localePicker; } @@ -96,14 +94,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O * "pretending" it was selected, and return false.</p> */ private boolean setListener(Context context, LocaleSelectedListener listener, - LocaleStore.LocaleInfo parent, boolean countryMode, boolean translatedOnly) { - if (countryMode && (parent == null || parent.getLocale() == null)) { - // The list of countries is determined as all the countries where the parent language - // is used. - throw new IllegalArgumentException("The country selection list needs a parent."); - } - - this.mCountryMode = countryMode; + LocaleStore.LocaleInfo parent, boolean translatedOnly) { this.mParentLocale = parent; this.mListener = listener; this.mTranslatedOnly = translatedOnly; @@ -116,7 +107,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O Collections.addAll(langTagsToIgnore, langTags); } - if (countryMode) { + if (parent != null) { mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore, parent, translatedOnly); if (mLocaleList.size() <= 1) { @@ -138,13 +129,11 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O super.onCreate(savedInstanceState); setHasOptionsMenu(true); - final Locale sortingLocale = (mCountryMode && mParentLocale != null) - ? mParentLocale.getLocale() - : Locale.getDefault(); - - mAdapter = new SuggestedLocaleAdapter(mLocaleList, mCountryMode); + final boolean countryMode = mParentLocale != null; + final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault(); + mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode); final LocaleHelper.LocaleInfoComparator comp = - new LocaleHelper.LocaleInfoComparator(sortingLocale); + new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode); mAdapter.sort(comp); setListAdapter(mAdapter); } @@ -164,12 +153,8 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O public void onResume() { super.onResume(); - if (mCountryMode) { - if (mParentLocale == null) { - this.getActivity().setTitle(R.string.country_selection_title); - } else { - this.getActivity().setTitle(mParentLocale.getFullNameNative()); - } + if (mParentLocale != null) { + this.getActivity().setTitle(mParentLocale.getFullNameNative()); } else { this.getActivity().setTitle(R.string.language_selection_title); } @@ -182,7 +167,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O final LocaleStore.LocaleInfo locale = (LocaleStore.LocaleInfo) getListAdapter().getItem(position); - if (mCountryMode || locale.getParent() != null) { + if (locale.getParent() != null) { if (mListener != null) { mListener.onLocaleSelected(locale); } @@ -205,7 +190,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (!mCountryMode) { + if (mParentLocale == null) { inflater.inflate(R.menu.language_selection_list, menu); MenuItem mSearchMenuItem = menu.findItem(R.id.locale_search_menu); diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index 465c4d833aa1..c4e6675c2c92 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -145,11 +145,11 @@ public class LocaleStore { return mLangScriptKey; } - String getLabel() { - if (getParent() == null || this.isSuggestionOfType(SUGGESTION_TYPE_SIM)) { - return getFullNameNative(); - } else { + String getLabel(boolean countryMode) { + if (countryMode) { return getFullCountryNameNative(); + } else { + return getFullNameNative(); } } @@ -311,9 +311,7 @@ public class LocaleStore { if (level == 2) { if (parent != null) { // region selection if (parentId.equals(li.getParent().toLanguageTag())) { - if (!li.isSuggestionOfType(LocaleInfo.SUGGESTION_TYPE_SIM)) { - result.add(li); - } + result.add(li); } } else { // language selection if (li.isSuggestionOfType(LocaleInfo.SUGGESTION_TYPE_SIM)) { diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 0d4a5aac2c0a..98102ea8627c 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -156,7 +156,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { TextView text = (TextView) convertView.findViewById(R.id.locale); LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position); - text.setText(item.getLabel()); + text.setText(item.getLabel(mCountryMode)); text.setTextLocale(item.getLocale()); if (mCountryMode) { int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent()); @@ -171,6 +171,9 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { } private boolean showHeaders() { + if (mCountryMode) { // never show suggestions in country mode + return false; + } return mSuggestionCount != 0 && mSuggestionCount != mLocaleOptions.size(); } diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java index c4ed2e10cffe..78c5e34ba108 100644 --- a/core/java/com/android/internal/widget/ImageFloatingTextView.java +++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java @@ -65,6 +65,8 @@ public class ImageFloatingTextView extends TextView { .setTextDirection(getTextDirectionHeuristic()) .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier()) .setIncludePad(getIncludeFontPadding()) + .setEllipsize(shouldEllipsize ? effectiveEllipsize : null) + .setEllipsizedWidth(ellipsisWidth) .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); // we set the endmargin on the first 2 lines. this works just in our case but that's diff --git a/core/jni/android_app_Activity.cpp b/core/jni/android_app_Activity.cpp index b1d7e82c73ae..56f4f01f8c80 100644 --- a/core/jni/android_app_Activity.cpp +++ b/core/jni/android_app_Activity.cpp @@ -15,16 +15,25 @@ */ #include <poll.h> -#include <android/dlext.h> + +#include <string> #include "core_jni_helpers.h" +extern "C" void android_dlwarning(void*, void (*)(void*, const char*)); + namespace android { static jstring getDlWarning_native(JNIEnv* env, jobject) { - const char* text = android_dlwarning(); - return text == nullptr ? nullptr : env->NewStringUTF(text); + std::string msg; + android_dlwarning(&msg, [](void* obj, const char* msg) { + if (msg != nullptr) { + *reinterpret_cast<std::string*>(obj) = msg; + } + }); + + return msg.empty() ? nullptr : env->NewStringUTF(msg.c_str()); } static const JNINativeMethod g_methods[] = { diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index afccfcfe5cef..f37fd78f6bdd 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -918,15 +918,14 @@ static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject t sp<VendorTagDescriptor> desc = new VendorTagDescriptor(); binder::Status res = cameraService->getCameraVendorTagDescriptor(/*out*/desc.get()); - if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DEPRECATED_HAL) { - ALOGW("%s: Camera HAL too old; does not support vendor tags", __FUNCTION__); + if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DISCONNECTED) { + // No camera module available, not an error on devices with no cameras VendorTagDescriptor::clearGlobalVendorTagDescriptor(); - return OK; } else if (!res.isOk()) { - ALOGE("%s: Failed to setup vendor tag descriptors: %s: %s", - __FUNCTION__, res.serviceSpecificErrorCode(), - res.toString8().string()); + VendorTagDescriptor::clearGlobalVendorTagDescriptor(); + ALOGE("%s: Failed to setup vendor tag descriptors: %s", + __FUNCTION__, res.toString8().string()); return res.serviceSpecificErrorCode(); } diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index defb88a9712f..880a79cc4f6d 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -26,10 +26,13 @@ #include <net/if.h> #include <linux/filter.h> #include <linux/if.h> +#include <linux/if_arp.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <net/if_ether.h> +#include <netinet/icmp6.h> #include <netinet/ip.h> +#include <netinet/ip6.h> #include <netinet/udp.h> #include <cutils/properties.h> @@ -64,10 +67,9 @@ static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); uint32_t ip_offset = sizeof(ether_header); uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol); - uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off); + uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off); uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest); struct sock_filter filter_code[] = { // Check the protocol is UDP. @@ -94,6 +96,45 @@ static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobje filter_code, }; + int fd = jniGetFDFromFileDescriptor(env, javaFd); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); + } +} + +static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd, + jint hardwareAddressType) +{ + if (hardwareAddressType != ARPHRD_ETHER) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "attachRaFilter only supports ARPHRD_ETHER"); + return; + } + + uint32_t ipv6_offset = sizeof(ether_header); + uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt); + uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr); + uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type); + struct sock_filter filter_code[] = { + // Check IPv6 Next Header is ICMPv6. + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, ipv6_next_header_offset), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), + + // Check ICMPv6 type is Router Advertisement. + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, icmp6_type_offset), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1), + + // Accept or reject. + BPF_STMT(BPF_RET | BPF_K, 0xffff), + BPF_STMT(BPF_RET | BPF_K, 0) + }; + struct sock_fprog filter = { + sizeof(filter_code) / sizeof(filter_code[0]), + filter_code, + }; + + int fd = jniGetFDFromFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); @@ -148,6 +189,7 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn }, { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter }, + { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter }, }; int register_android_net_NetworkUtils(JNIEnv* env) diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 3df0876532cb..f870a891b094 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -262,7 +262,13 @@ static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss) } name = line + name_pos; nameLen = strlen(name); - + // Trim the end of the line if it is " (deleted)". + const char* deleted_str = " (deleted)"; + if (nameLen > (int)strlen(deleted_str) && + strcmp(name+nameLen-strlen(deleted_str), deleted_str) == 0) { + nameLen -= strlen(deleted_str); + name[nameLen] = '\0'; + } if ((strstr(name, "[heap]") == name)) { whichHeap = HEAP_NATIVE; } else if (strncmp(name, "[anon:libc_malloc]", 18) == 0) { diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml index 9a4b28c1298c..3c59b4e83a05 100644 --- a/core/res/res/layout/notification_template_material_big_text.xml +++ b/core/res/res/layout/notification_template_material_big_text.xml @@ -39,7 +39,7 @@ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text" android:layout_width="match_parent" android:layout_height="0dp" - android:layout_marginTop="1.5dp" + android:layout_marginTop="1dp" android:paddingBottom="@dimen/notification_content_margin_bottom" android:textAppearance="@style/TextAppearance.Material.Notification" android:singleLine="false" diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml index 38470cd52c8b..47b30ec99299 100644 --- a/core/res/res/layout/notification_template_text.xml +++ b/core/res/res/layout/notification_template_text.xml @@ -14,12 +14,12 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<TextView xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" - android:layout_marginTop="1.5dp" + android:layout_marginTop="1dp" android:ellipsize="marquee" android:fadingEdge="horizontal" android:gravity="top" diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java index 998c72a9e9c8..c92863d93fda 100644 --- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java +++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java @@ -328,7 +328,12 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn // Only a1's pause listener should be called. assertTrue(l1.pauseCalled); assertFalse(l1.resumeCalled); - a1.resume(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + a1.resume(); + } + }); Thread.sleep(a1.getTotalDuration()); diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java index d3837756e495..348f8fd43867 100644 --- a/core/tests/coretests/src/android/util/PatternsTest.java +++ b/core/tests/coretests/src/android/util/PatternsTest.java @@ -25,7 +25,7 @@ import junit.framework.TestCase; public class PatternsTest extends TestCase { - //Tests for Patterns.TOP_LEVEL_DOMAIN + // Tests for Patterns.TOP_LEVEL_DOMAIN @SmallTest public void testTldPattern() throws Exception { @@ -56,7 +56,7 @@ public class PatternsTest extends TestCase { assertFalse("Matched invalid TLD!", t); } - //Tests for Patterns.IANA_TOP_LEVEL_DOMAINS + // Tests for Patterns.IANA_TOP_LEVEL_DOMAINS @SmallTest public void testIanaTopLevelDomains_matchesValidTld() throws Exception { @@ -94,7 +94,7 @@ public class PatternsTest extends TestCase { assertFalse("Should not match invalid Punycode TLD", pattern.matcher("xn").matches()); } - //Tests for Patterns.WEB_URL + // Tests for Patterns.WEB_URL @SmallTest public void testWebUrl_matchesValidUrlWithSchemeAndHostname() throws Exception { @@ -208,7 +208,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } - //Tests for Patterns.AUTOLINK_WEB_URL + // Tests for Patterns.AUTOLINK_WEB_URL @SmallTest public void testAutoLinkWebUrl_matchesValidUrlWithSchemeAndHostname() throws Exception { @@ -419,7 +419,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } - //Tests for Patterns.IP_ADDRESS + // Tests for Patterns.IP_ADDRESS @SmallTest public void testIpPattern() throws Exception { @@ -432,7 +432,7 @@ public class PatternsTest extends TestCase { assertFalse("Invalid IP", t); } - //Tests for Patterns.DOMAIN_NAME + // Tests for Patterns.DOMAIN_NAME @SmallTest public void testDomain_matchesPunycodeTld() throws Exception { @@ -508,7 +508,227 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } - //Tests for Patterns.PHONE + // Tests for Patterns.AUTOLINK_EMAIL_ADDRESS + + public void testAutoLinkEmailAddress_matchesShortValidEmail() throws Exception { + String email = "a@a.co"; + assertTrue("Should match short valid email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesRegularEmail() throws Exception { + String email = "email@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesEmailWithMultipleSubdomains() throws Exception { + String email = "email@e.somelongdomainnameforandroid.abc.uk"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartWithDot() throws Exception { + String email = "e.mail@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartWithPlus() throws Exception { + String email = "e+mail@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartWithUnderscore() throws Exception { + String email = "e_mail@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartWithDash() throws Exception { + String email = "e-mail@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartWithApostrophe() throws Exception { + String email = "e'mail@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartWithDigits() throws Exception { + String email = "123@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesUnicodeLocalPart() throws Exception { + String email = "\uD604\uAE08\uC601\uC218\uC99D@android.kr"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartWithEmoji() throws Exception { + String email = "smiley\u263A@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartWithSurrogatePairs() throws Exception { + String email = "\uD83C\uDF38@android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesDomainWithDash() throws Exception { + String email = "email@an-droid.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesUnicodeDomain() throws Exception { + String email = "email@\uD604\uAE08\uC601\uC218\uC99D.kr"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesUnicodeLocalPartAndDomain() throws Exception { + String email = "\uD604\uAE08\uC601\uC218\uC99D@\uD604\uAE08\uC601\uC218\uC99D.kr"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesDomainWithEmoji() throws Exception { + String email = "smiley@\u263Aandroid.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesDomainWithSurrogatePairs() throws Exception { + String email = "email@\uD83C\uDF38android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartAndDomainWithSurrogatePairs() + throws Exception { + String email = "\uD83C\uDF38@\uD83C\uDF38android.com"; + assertTrue("Should match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchStringWithoutAtSign() throws Exception { + String email = "android.com"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchPlainString() throws Exception { + String email = "email"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchStringWithMultipleAtSigns() throws Exception { + String email = "email@android@android.com"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchEmailWithoutTld() throws Exception { + String email = "email@android"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchLocalPartEndingWithDot() throws Exception { + String email = "email.@android.com"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchLocalPartStartingWithDot() throws Exception { + String email = ".email@android.com"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchDomainStartingWithDash() throws Exception { + String email = "email@-android.com"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchDomainWithConsecutiveDots() throws Exception { + String email = "email@android..com"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchEmailWithIpAsDomain() throws Exception { + String email = "email@127.0.0.1"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_doesNotMatchEmailWithInvalidTld() throws Exception { + String email = "email@android.c"; + assertFalse("Should not match email: " + email, + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesLocalPartUpTo64Chars() throws Exception { + String localPart = ""; + for (int i = 0; i < 64; i++) { + localPart += "a"; + } + String email = localPart + "@android.com"; + + assertTrue("Should match local part of length: " + localPart.length(), + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + + email = localPart + "a@android.com"; + assertFalse("Should not match local part of length: " + localPart.length(), + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesSubdomainUpTo63Chars() throws Exception { + String subdomain = ""; + for (int i = 0; i < 63; i++) { + subdomain += "a"; + } + String email = "email@" + subdomain + ".com"; + + assertTrue("Should match subdomain of length: " + subdomain.length(), + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + + subdomain += "a"; + email = "email@" + subdomain + ".com"; + assertFalse("Should not match local part of length: " + subdomain.length(), + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + public void testAutoLinkEmailAddress_matchesDomainUpTo255Chars() throws Exception { + String longDomain = ""; + while (longDomain.length() <= 250) { + longDomain += "d."; + } + longDomain += "com"; + assertEquals(255, longDomain.length()); + String email = "a@" + longDomain; + + assertTrue("Should match domain of length: " + longDomain.length(), + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + + email = email + "m"; + assertEquals(258, email.length()); + assertFalse("Should not match domain of length: " + longDomain.length(), + Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); + } + + // Tests for Patterns.PHONE @SmallTest public void testPhonePattern() throws Exception { diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 7adad8a4b86e..0886487f70cf 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -547,11 +547,8 @@ public final class KeyChain { Intent intent = new Intent(IKeyChainService.class.getName()); ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); intent.setComponent(comp); - boolean isBound = context.bindServiceAsUser(intent, - keyChainServiceConnection, - Context.BIND_AUTO_CREATE, - user); - if (!isBound) { + if (comp == null || !context.bindServiceAsUser( + intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user)) { throw new AssertionError("could not bind to KeyChainService"); } return new KeyChainConnection(context, keyChainServiceConnection, q.take()); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 646ab4ef1804..a0e2481d4fdc 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -644,6 +644,21 @@ public final class MediaFormat { /** Full range. Y, Cr and Cb component values range from 0 to 255 for 8-bit content. */ public static final int COLOR_RANGE_FULL = 1; + /** + * A key describing a unique ID for the content of a media track. + * + * <p>This key is used by {@link MediaExtractor}. Some extractors provide multiple encodings + * of the same track (e.g. float audio tracks for FLAC and WAV may be expressed as two + * tracks via MediaExtractor: a normal PCM track for backward compatibility, and a float PCM + * track for added fidelity. Similarly, Dolby Vision extractor may provide a baseline SDR + * version of a DV track.) This key can be used to identify which MediaExtractor tracks refer + * to the same underlying content. + * </p> + * + * The associated value is an integer. + */ + public static final String KEY_TRACK_ID = "track-id"; + /* package private */ MediaFormat(Map<String, Object> map) { mMap = map; } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index adf85517c344..b78869eb4790 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -2845,13 +2845,17 @@ public class MediaPlayer implements SubtitleController.Listener MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null); sendMessage(msg2); } - if (mOnPreparedListener != null) - mOnPreparedListener.onPrepared(mMediaPlayer); + OnPreparedListener onPreparedListener = mOnPreparedListener; + if (onPreparedListener != null) + onPreparedListener.onPrepared(mMediaPlayer); return; case MEDIA_PLAYBACK_COMPLETE: - if (mOnCompletionListener != null) - mOnCompletionListener.onCompletion(mMediaPlayer); + { + OnCompletionListener onCompletionListener = mOnCompletionListener; + if (onCompletionListener != null) + onCompletionListener.onCompletion(mMediaPlayer); + } stayAwake(false); return; @@ -2875,13 +2879,15 @@ public class MediaPlayer implements SubtitleController.Listener break; case MEDIA_BUFFERING_UPDATE: - if (mOnBufferingUpdateListener != null) - mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1); + OnBufferingUpdateListener onBufferingUpdateListener = mOnBufferingUpdateListener; + if (onBufferingUpdateListener != null) + onBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1); return; case MEDIA_SEEK_COMPLETE: - if (mOnSeekCompleteListener != null) { - mOnSeekCompleteListener.onSeekComplete(mMediaPlayer); + OnSeekCompleteListener onSeekCompleteListener = mOnSeekCompleteListener; + if (onSeekCompleteListener != null) { + onSeekCompleteListener.onSeekComplete(mMediaPlayer); } // fall through @@ -2895,8 +2901,9 @@ public class MediaPlayer implements SubtitleController.Listener return; case MEDIA_SET_VIDEO_SIZE: - if (mOnVideoSizeChangedListener != null) { - mOnVideoSizeChangedListener.onVideoSizeChanged( + OnVideoSizeChangedListener onVideoSizeChangedListener = mOnVideoSizeChangedListener; + if (onVideoSizeChangedListener != null) { + onVideoSizeChangedListener.onVideoSizeChanged( mMediaPlayer, msg.arg1, msg.arg2); } return; @@ -2904,11 +2911,15 @@ public class MediaPlayer implements SubtitleController.Listener case MEDIA_ERROR: Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")"); boolean error_was_handled = false; - if (mOnErrorListener != null) { - error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2); + OnErrorListener onErrorListener = mOnErrorListener; + if (onErrorListener != null) { + error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2); } - if (mOnCompletionListener != null && ! error_was_handled) { - mOnCompletionListener.onCompletion(mMediaPlayer); + { + OnCompletionListener onCompletionListener = mOnCompletionListener; + if (onCompletionListener != null && ! error_was_handled) { + onCompletionListener.onCompletion(mMediaPlayer); + } } stayAwake(false); return; @@ -2944,47 +2955,52 @@ public class MediaPlayer implements SubtitleController.Listener break; } - if (mOnInfoListener != null) { - mOnInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2); + OnInfoListener onInfoListener = mOnInfoListener; + if (onInfoListener != null) { + onInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2); } // No real default action so far. return; case MEDIA_TIMED_TEXT: - if (mOnTimedTextListener == null) + OnTimedTextListener onTimedTextListener = mOnTimedTextListener; + if (onTimedTextListener == null) return; if (msg.obj == null) { - mOnTimedTextListener.onTimedText(mMediaPlayer, null); + onTimedTextListener.onTimedText(mMediaPlayer, null); } else { if (msg.obj instanceof Parcel) { Parcel parcel = (Parcel)msg.obj; TimedText text = new TimedText(parcel); parcel.recycle(); - mOnTimedTextListener.onTimedText(mMediaPlayer, text); + onTimedTextListener.onTimedText(mMediaPlayer, text); } } return; case MEDIA_SUBTITLE_DATA: - if (mOnSubtitleDataListener == null) { + OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener; + if (onSubtitleDataListener == null) { return; } if (msg.obj instanceof Parcel) { Parcel parcel = (Parcel) msg.obj; SubtitleData data = new SubtitleData(parcel); parcel.recycle(); - mOnSubtitleDataListener.onSubtitleData(mMediaPlayer, data); + onSubtitleDataListener.onSubtitleData(mMediaPlayer, data); } return; case MEDIA_META_DATA: - if (mOnTimedMetaDataAvailableListener == null) { + OnTimedMetaDataAvailableListener onTimedMetaDataAvailableListener = + mOnTimedMetaDataAvailableListener; + if (onTimedMetaDataAvailableListener == null) { return; } if (msg.obj instanceof Parcel) { Parcel parcel = (Parcel) msg.obj; TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel); parcel.recycle(); - mOnTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data); + onTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data); } return; diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp index 3b892cb3e4ab..537b56d77b58 100644 --- a/media/jni/android_media_MediaDataSource.cpp +++ b/media/jni/android_media_MediaDataSource.cpp @@ -116,7 +116,8 @@ status_t JMediaDataSource::getSize(off64_t* size) { return UNKNOWN_ERROR; } if (mSizeIsCached) { - return mCachedSize; + *size = mCachedSize; + return OK; } JNIEnv* env = AndroidRuntime::getJNIEnv(); diff --git a/packages/DocumentsUI/res/drawable/ic_sd_storage.xml b/packages/DocumentsUI/res/drawable/ic_sd_storage.xml index b0f3cc395bcd..5aeebbb6fc90 100644 --- a/packages/DocumentsUI/res/drawable/ic_sd_storage.xml +++ b/packages/DocumentsUI/res/drawable/ic_sd_storage.xml @@ -14,8 +14,8 @@ Copyright (C) 2015 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="48dp" - android:height="48dp" + android:width="24dp" + android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path diff --git a/packages/SystemUI/res/drawable/ic_info.xml b/packages/DocumentsUI/res/drawable/ic_usb_storage.xml index 65e7bf5fa41d..2a8d024f3eec 100644 --- a/packages/SystemUI/res/drawable/ic_info.xml +++ b/packages/DocumentsUI/res/drawable/ic_usb_storage.xml @@ -1,5 +1,5 @@ <!-- -Copyright (C) 2014 The Android Open Source Project +Copyright (C) 2015 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. @@ -14,11 +14,11 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24.0dp" - android:height="24.0dp" + android:width="24dp" + android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:pathData="M12.000000,2.000000C6.500000,2.000000 2.000000,6.500000 2.000000,12.000000s4.500000,10.000000 10.000000,10.000000c5.500000,0.000000 10.000000,-4.500000 10.000000,-10.000000S17.500000,2.000000 12.000000,2.000000zM13.000000,17.000000l-2.000000,0.000000l0.000000,-6.000000l2.000000,0.000000L13.000000,17.000000zM13.000000,9.000000l-2.000000,0.000000L11.000000,7.000000l2.000000,0.000000L13.000000,9.000000z" - android:fillColor="#FFFFFF"/> + android:fillColor="#FF000000" + android:pathData="M15 7v4h1v2h-3V5h2l-3,-4,-3 4h2v8H8v-2.07c.7,-.37 1.2,-1.08 1.2,-1.93 0,-1.21,-.99,-2.2,-2.2,-2.2,-1.21 0,-2.2.99,-2.2 2.2 0 .85.5 1.56 1.2 1.93V13c0 1.11.89 2 2 2h3v3.05c-.71.37,-1.2 1.1,-1.2 1.95 0 1.22.99 2.2 2.2 2.2 1.21 0 2.2,-.98 2.2,-2.2 0,-.85,-.49,-1.58,-1.2,-1.95V15h3c1.11 0 2,-.89 2,-2v-2h1V7h-4z"/> </vector> diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java index 29273a36b112..3eaf10a18a0f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java @@ -16,6 +16,7 @@ package com.android.documentsui.model; +import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.Shared.compareToIgnoreCaseNullable; import static com.android.documentsui.model.DocumentInfo.getCursorInt; import static com.android.documentsui.model.DocumentInfo.getCursorLong; @@ -31,6 +32,7 @@ import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Root; import android.text.TextUtils; +import android.util.Log; import com.android.documentsui.IconUtils; import com.android.documentsui.R; @@ -47,6 +49,8 @@ import java.util.Objects; * Representation of a {@link Root}. */ public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> { + + private static final String TAG = "RootInfo"; private static final int VERSION_INIT = 1; private static final int VERSION_DROP_TYPE = 2; @@ -59,6 +63,8 @@ public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> { TYPE_DOWNLOADS, TYPE_LOCAL, TYPE_MTP, + TYPE_SD, + TYPE_USB, TYPE_OTHER }) @Retention(RetentionPolicy.SOURCE) @@ -70,7 +76,9 @@ public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> { public static final int TYPE_DOWNLOADS = 5; public static final int TYPE_LOCAL = 6; public static final int TYPE_MTP = 7; - public static final int TYPE_OTHER = 8; + public static final int TYPE_SD = 8; + public static final int TYPE_USB = 9; + public static final int TYPE_OTHER = 10; public String authority; public String rootId; @@ -185,33 +193,40 @@ public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> { private void deriveFields() { derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null; - // TODO: remove these special case icons if (isHome()) { - derivedIcon = R.drawable.ic_root_documents; derivedType = TYPE_LOCAL; + derivedIcon = R.drawable.ic_root_documents; + } else if (isMtp()) { + derivedType = TYPE_MTP; + derivedIcon = R.drawable.ic_usb_storage; + } else if (isUsb()) { + derivedType = TYPE_USB; + derivedIcon = R.drawable.ic_usb_storage; + } else if (isSd()) { + derivedType = TYPE_SD; + derivedIcon = R.drawable.ic_sd_storage; } else if (isExternalStorage()) { - derivedIcon = R.drawable.ic_root_smartphone; derivedType = TYPE_LOCAL; - // TODO: Apply SD card icon to SD devices. + derivedIcon = R.drawable.ic_root_smartphone; } else if (isDownloads()) { - derivedIcon = R.drawable.ic_root_download; derivedType = TYPE_DOWNLOADS; + derivedIcon = R.drawable.ic_root_download; } else if (isImages()) { - derivedIcon = R.drawable.ic_doc_image; derivedType = TYPE_IMAGES; + derivedIcon = R.drawable.ic_doc_image; } else if (isVideos()) { - derivedIcon = R.drawable.ic_doc_video; derivedType = TYPE_VIDEO; + derivedIcon = R.drawable.ic_doc_video; } else if (isAudio()) { - derivedIcon = R.drawable.ic_doc_audio; derivedType = TYPE_AUDIO; + derivedIcon = R.drawable.ic_doc_audio; } else if (isRecents()) { derivedType = TYPE_RECENTS; - } else if (isMtp()) { - derivedType = TYPE_MTP; } else { derivedType = TYPE_OTHER; } + + if (DEBUG) Log.d(TAG, "Finished deriving fields: " + this); } public Uri getUri() { @@ -291,6 +306,14 @@ public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> { return (flags & Root.FLAG_EMPTY) != 0; } + public boolean isSd() { + return (flags & Root.FLAG_REMOVABLE_SD) != 0; + } + + public boolean isUsb() { + return (flags & Root.FLAG_REMOVABLE_USB) != 0; + } + public Drawable loadIcon(Context context) { if (derivedIcon != 0) { return context.getDrawable(derivedIcon); @@ -358,7 +381,14 @@ public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> { @Override public String toString() { - return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}"; + return "Root{" + + "authority=" + authority + + ", rootId=" + rootId + + ", title=" + title + + ", isUsb=" + isUsb() + + ", isSd=" + isSd() + + ", isMtp=" + isMtp() + + "}"; } public String getDirectoryString() { diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 3eda8ec1ee35..9a51b057d135 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -34,6 +34,7 @@ import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.OnCloseListener; import android.os.UserHandle; +import android.os.storage.DiskInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.provider.DocumentsContract; @@ -184,6 +185,14 @@ public class ExternalStorageProvider extends DocumentsProvider { root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD; + final DiskInfo disk = volume.getDisk(); + if (DEBUG) Log.d(TAG, "Disk for root " + rootId + " is " + disk); + if (disk != null && disk.isSd()) { + root.flags |= Root.FLAG_REMOVABLE_SD; + } else if (disk != null && disk.isUsb()) { + root.flags |= Root.FLAG_REMOVABLE_USB; + } + if (volume.isPrimary()) { // save off the primary volume for subsequent "Home" dir initialization. primaryVolume = volume; diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 64f5cc62201e..4b62f2475456 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -731,7 +731,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // The activity is a component name, therefore it is one or none. if (resolvedActivities.get(0).activityInfo.exported) { - intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, mPrintJob); + PrintJobInfo.Builder printJobBuilder = new PrintJobInfo.Builder(mPrintJob); + printJobBuilder.setPages(mSelectedPages); + + intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, printJobBuilder.build()); intent.putExtra(PrintService.EXTRA_PRINTER_INFO, printer); intent.putExtra(PrintService.EXTRA_PRINT_DOCUMENT_INFO, mPrintedDocument.getDocumentInfo().info); @@ -759,10 +762,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // Take the advanced options without interpretation. mPrintJob.setAdvancedOptions(printJobInfo.getAdvancedOptions()); - // Take copies without interpretation as the advanced print dialog - // cannot create a print job info with invalid copies. - mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies())); - mPrintJob.setCopies(printJobInfo.getCopies()); + if (printJobInfo.getCopies() < 1) { + Log.w(LOG_TAG, "Cannot apply return value from advanced options activity. Copies " + + "must be 1 or more. Actual value is: " + printJobInfo.getCopies() + ". " + + "Ignoring."); + } else { + mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies())); + mPrintJob.setCopies(printJobInfo.getCopies()); + } PrintAttributes currAttributes = mPrintJob.getAttributes(); PrintAttributes newAttributes = printJobInfo.getAttributes(); @@ -771,7 +778,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // Take the media size only if the current printer supports is. MediaSize oldMediaSize = currAttributes.getMediaSize(); MediaSize newMediaSize = newAttributes.getMediaSize(); - if (!oldMediaSize.equals(newMediaSize)) { + if (newMediaSize != null && !oldMediaSize.equals(newMediaSize)) { final int mediaSizeCount = mMediaSizeSpinnerAdapter.getCount(); MediaSize newMediaSizePortrait = newAttributes.getMediaSize().asPortrait(); for (int i = 0; i < mediaSizeCount; i++) { diff --git a/packages/SettingsLib/res/drawable/ic_info.xml b/packages/SettingsLib/res/drawable/ic_info.xml new file mode 100644 index 000000000000..afe7e6b782ca --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_info.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2016 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/colorAccent"> + <path + android:fillColor="@android:color/white" + android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml deleted file mode 100644 index b3d7cf925ef2..000000000000 --- a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2016 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. ---> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="21dp" - android:height="21dp" - android:viewportWidth="21.0" - android:viewportHeight="21.0" - android:tint="?android:attr/colorAccent"> - <path - android:fillColor="@android:color/white" - android:pathData="M8,16c1.1,0,2-0.9,2-2s-0.9-2-2-2s-2,0.9-2,2S6.9,16,8,16zM14,7h-1V5c0-2.8-2.2-5-5-5S3,2.2,3,5v2H2C0.9,7,0,7.9,0,9v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V9C16,7.9,15.1,7,14,7z M4.9,5c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H4.9V5z M14,19H2V9h12V19z" /> -</vector> diff --git a/packages/SettingsLib/res/layout/restricted_icon.xml b/packages/SettingsLib/res/layout/restricted_icon.xml new file mode 100644 index 000000000000..d57fb80c14b6 --- /dev/null +++ b/packages/SettingsLib/res/layout/restricted_icon.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 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. +--> +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/restricted_icon" + android:layout_width="@dimen/restricted_icon_size" + android:layout_height="@dimen/restricted_icon_size" + android:src="@drawable/ic_info" + android:gravity="end|center_vertical" />
\ No newline at end of file diff --git a/packages/SettingsLib/res/layout/restricted_switch_widget.xml b/packages/SettingsLib/res/layout/restricted_switch_widget.xml new file mode 100644 index 000000000000..618381203d05 --- /dev/null +++ b/packages/SettingsLib/res/layout/restricted_switch_widget.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/restricted_icon" + android:layout_width="@dimen/restricted_icon_size" + android:layout_height="@dimen/restricted_icon_size" + android:src="@drawable/ic_info" + android:gravity="end|center_vertical" /> + <!-- Based off frameworks/base/core/res/res/layout/preference_widget_switch.xml --> + <Switch xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+android:id/switch_widget" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:focusable="false" + android:clickable="false" + android:background="@null" /> +</merge>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index 9f78e87d1231..c3b3cfc47bfd 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -33,8 +33,8 @@ <dimen name="user_spinner_item_height">56dp</dimen> <!-- Lock icon for preferences locked by admin --> - <dimen name="restricted_lock_icon_size">16dp</dimen> - <dimen name="restricted_lock_icon_padding">4dp</dimen> + <dimen name="restricted_icon_size">16dp</dimen> + <dimen name="restricted_icon_padding">4dp</dimen> <dimen name="wifi_preference_badge_padding">8dip</dimen> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 72fa9397e898..ae2c6e71bfc3 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -773,6 +773,11 @@ <!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] --> <string name="disabled_by_admin_summary_text">Controlled by admin</string> + <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] --> + <string name="enabled_by_admin">Enabled by administrator</string> + <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] --> + <string name="disabled_by_admin">Disabled by administrator</string> + <!-- Option in navigation drawer that leads to Settings main screen [CHAR LIMIT=30] --> <string name="home">Home</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java index c2f885dabe60..4c0450e38fa9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java @@ -49,7 +49,7 @@ public class RestrictedDropDownPreference extends DropDownPreference { mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(context); mRestrictedPadlockPadding = context.getResources().getDimensionPixelSize( - R.dimen.restricted_lock_icon_padding); + R.dimen.restricted_icon_padding); } private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() { diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java index e63130de6f4e..360a34c959f2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java @@ -36,7 +36,7 @@ public class RestrictedLockImageSpan extends ImageSpan { mContext = context; mExtraPadding = mContext.getResources().getDimensionPixelSize( - R.dimen.restricted_lock_icon_padding); + R.dimen.restricted_icon_padding); mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext); } @@ -53,7 +53,7 @@ public class RestrictedLockImageSpan extends ImageSpan { // Add extra padding before the padlock. float transX = x + mExtraPadding; - float transY = bottom - drawable.getBounds().bottom - paint.getFontMetricsInt().descent; + float transY = (bottom - drawable.getBounds().bottom) / 2.0f; canvas.translate(transX, transY); drawable.draw(canvas); diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java index 6d29c5f1a98e..d0c249f94740 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java @@ -49,9 +49,9 @@ public class RestrictedLockUtils { * @return drawables for displaying with settings that are locked by a device admin. */ public static Drawable getRestrictedPadlock(Context context) { - Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline); + Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_info); final int iconSize = context.getResources().getDimensionPixelSize( - R.dimen.restricted_lock_icon_size); + R.dimen.restricted_icon_size); restrictedPadlock.setBounds(0, 0, iconSize, iconSize); return restrictedPadlock; } diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java index 810f6eb28a56..e69497a92034 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java @@ -23,6 +23,7 @@ import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceViewHolder; import android.util.AttributeSet; +import android.view.View; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; @@ -36,6 +37,7 @@ public class RestrictedPreference extends Preference { public RestrictedPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + setWidgetLayoutResource(R.layout.restricted_icon); mHelper = new RestrictedPreferenceHelper(context, this, attrs); } @@ -56,6 +58,10 @@ public class RestrictedPreference extends Preference { public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); mHelper.onBindViewHolder(holder); + final View restrictedIcon = holder.findViewById(R.id.restricted_icon); + if (restrictedIcon != null) { + restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE); + } } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index 227b1e8e1b4d..0c0af243ff7b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -54,7 +54,7 @@ public class RestrictedPreferenceHelper { mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext); mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize( - R.dimen.restricted_lock_icon_padding); + R.dimen.restricted_icon_padding); if (attrs != null) { final TypedArray attributes = context.obtainStyledAttributes(attrs, @@ -91,12 +91,8 @@ public class RestrictedPreferenceHelper { * Modify PreferenceViewHolder to add padlock if restriction is disabled. */ public void onBindViewHolder(PreferenceViewHolder holder) { - final TextView titleView = (TextView) holder.findViewById(android.R.id.title); - if (titleView != null) { - RestrictedLockUtils.setTextViewPadlock(mContext, titleView, mDisabledByAdmin); - if (mDisabledByAdmin) { - holder.itemView.setEnabled(true); - } + if (mDisabledByAdmin) { + holder.itemView.setEnabled(true); } if (mUseAdminDisabledSummary) { final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java index 6cae8aa70847..f381286c0697 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java @@ -23,6 +23,8 @@ import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceViewHolder; import android.support.v14.preference.SwitchPreference; import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; @@ -36,6 +38,7 @@ public class RestrictedSwitchPreference extends SwitchPreference { public RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + setWidgetLayoutResource(R.layout.restricted_switch_widget); mHelper = new RestrictedPreferenceHelper(context, this, attrs); } @@ -56,6 +59,20 @@ public class RestrictedSwitchPreference extends SwitchPreference { public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); mHelper.onBindViewHolder(holder); + final View restrictedIcon = holder.findViewById(R.id.restricted_icon); + final View switchWidget = holder.findViewById(android.R.id.switch_widget); + if (restrictedIcon != null) { + restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE); + } + if (switchWidget != null) { + switchWidget.setVisibility(isDisabledByAdmin() ? View.GONE : View.VISIBLE); + } + final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); + if (summaryView != null && isDisabledByAdmin()) { + summaryView.setText( + isChecked() ? R.string.enabled_by_admin : R.string.disabled_by_admin); + summaryView.setVisibility(View.VISIBLE); + } } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java index a57805512a7e..7a1c741eb490 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java @@ -69,16 +69,18 @@ public class SettingsDrawerActivity extends Activity { long startTime = System.currentTimeMillis(); - getWindow().addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - getWindow().addFlags(LayoutParams.FLAG_TRANSLUCENT_STATUS); - requestWindowFeature(Window.FEATURE_NO_TITLE); + TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme); + if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) { + getWindow().addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + getWindow().addFlags(LayoutParams.FLAG_TRANSLUCENT_STATUS); + requestWindowFeature(Window.FEATURE_NO_TITLE); + } super.setContentView(R.layout.settings_with_drawer); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (mDrawerLayout == null) { return; } Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar); - TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme); if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) { toolbar.setVisibility(View.GONE); mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index b5b7bcd743d5..637551c68a74 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -245,7 +245,7 @@ android:stateNotNeeded="true" android:resumeWhilePausing="true" android:screenOrientation="behind" - android:theme="@style/RecentsTheme.Wallpaper"> + android:theme="@style/RecentsTvTheme.Wallpaper"> <intent-filter> <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" /> </intent-filter> diff --git a/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml new file mode 100644 index 000000000000..e98d43f595ae --- /dev/null +++ b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <gradient + android:startColor="#99000000" + android:endColor="#E6000000" + android:angle="90"/> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml index 603ebbfc8ffb..b0dca9a66ddb 100644 --- a/packages/SystemUI/res/layout/qs_tile_label.xml +++ b/packages/SystemUI/res/layout/qs_tile_label.xml @@ -20,22 +20,22 @@ android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/tile_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@color/qs_tile_text" - android:gravity="center_horizontal" - android:minLines="2" - android:padding="0dp" - android:fontFamily="sans-serif-condensed" - android:textStyle="normal" - android:textSize="@dimen/qs_tile_text_size" - android:clickable="false" /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/qs_tile_text" + android:gravity="center_horizontal" + android:minLines="2" + android:padding="0dp" + android:fontFamily="sans-serif-condensed" + android:textStyle="normal" + android:textSize="@dimen/qs_tile_text_size" + android:clickable="false" /> <ImageView android:id="@+id/restricted_padlock" - android:layout_width="@dimen/qs_tile_text_size" - android:layout_height="@dimen/qs_tile_text_size" - android:src="@drawable/ic_settings_lock_outline" - android:layout_marginLeft="@dimen/restricted_padlock_pading" - android:baselineAlignBottom="true" - android:scaleType="centerInside" - android:visibility="gone" /> + android:layout_width="@dimen/qs_tile_text_size" + android:layout_height="match_parent" + android:paddingBottom="@dimen/qs_tile_text_size" + android:src="@drawable/ic_info" + android:layout_marginLeft="@dimen/restricted_padlock_pading" + android:scaleType="centerInside" + android:visibility="gone" /> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml index a22c3603632e..661d74a468e4 100644 --- a/packages/SystemUI/res/layout/qs_user_detail_item.xml +++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml @@ -53,10 +53,10 @@ <ImageView android:id="@+id/restricted_padlock" android:layout_width="@dimen/qs_detail_item_secondary_text_size" - android:layout_height="@dimen/qs_detail_item_secondary_text_size" - android:src="@drawable/ic_settings_lock_outline" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:src="@drawable/ic_info" android:layout_marginLeft="@dimen/restricted_padlock_pading" - android:baselineAlignBottom="true" android:scaleType="centerInside" android:visibility="gone" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml index 94b099e5f446..3a7c1d10e642 100644 --- a/packages/SystemUI/res/layout/recents_on_tv.xml +++ b/packages/SystemUI/res/layout/recents_on_tv.xml @@ -18,9 +18,10 @@ android:id="@+id/recents_view" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@drawable/recents_tv_background_gradient" android:clipChildren="false" - android:clipToPadding="false" > - + android:clipToPadding="false" + android:layoutDirection="rtl"> <com.android.systemui.recents.tv.views.TaskStackHorizontalGridView android:id="@+id/task_list" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/layout/recents_task_card_view.xml b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml index fa1daadd2cee..c5b1a7a51782 100644 --- a/packages/SystemUI/res/layout/recents_task_card_view.xml +++ b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml @@ -20,28 +20,28 @@ android:focusable="true" android:focusableInTouchMode="true" android:layout_gravity="center" - android:layout_centerInParent="true"> + android:layout_centerInParent="true" + android:layoutDirection="ltr"> - <RelativeLayout + <LinearLayout android:layout_width="@dimen/recents_tv_card_width" android:layout_height="wrap_content" android:layout_centerInParent="true" - android:layout_gravity="center"> - <ImageView - android:id="@+id/card_view_thumbnail" - android:layout_width="match_parent" - android:layout_height="@dimen/recents_tv_card_height" - android:scaleType="centerCrop" - android:gravity="center" - android:layout_alignParentTop="true" - android:layout_centerHorizontal="true"/> - - <RelativeLayout + android:layout_gravity="center" + android:orientation="vertical" > + <LinearLayout android:id="@+id/card_info_field" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/card_view_thumbnail" - android:background="@color/recents_tv_card_background_color" > + android:layout_height="wrap_content"> + <ImageView + android:id="@+id/card_extra_badge" + android:layout_width="@dimen/recents_tv_card_extra_badge_size" + android:layout_height="@dimen/recents_tv_card_extra_badge_size" + android:layout_marginBottom="@dimen/recents_tv_icon_padding_bottom" + android:layout_marginEnd="@dimen/recents_tv_icon_padding_end" + android:scaleType="fitCenter" + android:layout_centerVertical="true" + android:layout_alignParentRight="true" /> <TextView android:id="@+id/card_title_text" android:layout_width="match_parent" @@ -49,29 +49,21 @@ android:layout_alignParentTop="false" android:includeFontPadding="true" android:minLines="1" - android:maxLines="2" + android:maxLines="1" android:textColor="@color/recents_tv_card_title_text_color" - android:ellipsize="end" /> - <TextView - android:id="@+id/card_content_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_below="@id/card_title_text" - android:includeFontPadding="true" - android:minLines="1" - android:maxLines="2" - android:textColor="@color/recents_tv_card_content_text_color" - android:ellipsize="end" /> - <ImageView - android:id="@+id/card_extra_badge" - android:layout_width="@dimen/recents_tv_card_extra_badge_size" - android:layout_height="@dimen/recents_tv_card_extra_badge_size" - android:scaleType="fitCenter" - android:background="@android:color/transparent" - android:contentDescription="@null" - android:layout_centerVertical="true" - android:layout_alignParentRight="true"/> - </RelativeLayout> - </RelativeLayout> + android:fontFamily="@string/font_roboto_regular" + android:textSize="@dimen/recents_tv_title_text_size" + android:layout_marginBottom="@dimen/recents_tv_text_padding_bottom" + android:ellipsize="end"/> + </LinearLayout> + <ImageView + android:id="@+id/card_view_thumbnail" + android:layout_width="match_parent" + android:layout_height="@dimen/recents_tv_card_height" + android:scaleType="centerCrop" + android:gravity="center" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_below="@id/card_title_text" /> + </LinearLayout> </com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml index 818df3b46fd7..75195c4ffbcc 100644 --- a/packages/SystemUI/res/layout/remote_input.xml +++ b/packages/SystemUI/res/layout/remote_input.xml @@ -43,7 +43,7 @@ android:singleLine="true" android:ellipsize="start" android:inputType="textShortMessage|textAutoCorrect|textCapSentences" - android:imeOptions="actionSend" /> + android:imeOptions="actionSend|flagNoExtractUi" /> <FrameLayout android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml index 6f4c983586a4..af99aaecf62a 100644 --- a/packages/SystemUI/res/values/colors_tv.xml +++ b/packages/SystemUI/res/values/colors_tv.xml @@ -18,7 +18,5 @@ --> <resources> <color name="recents_tv_card_background_color">#FF37474F</color> - <color name="recents_tv_card_title_text_color">#FFEEEEEE</color> - <color name="recents_tv_card_content_text_color">#99EEEEEE</color> - <color name="recents_tv_card_source_text_color">#99EEEEEE</color> + <color name="recents_tv_card_title_text_color">#CCEEEEEE</color> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8a7f90bf4a14..12c3a5d8cb50 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -292,6 +292,9 @@ <!-- The amount to allow the stack to overscroll. --> <dimen name="recents_stack_overscroll">24dp</dimen> + <!-- The size of the initial peek area at the top of the stack (below the status bar). --> + <dimen name="recents_initial_top_peek_size">8dp</dimen> + <!-- The size of the peek area at the top of the stack (below the status bar). --> <dimen name="recents_layout_focused_top_peek_size">@dimen/recents_history_button_height</dimen> diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml index bf32cc7505b7..b5891107297e 100644 --- a/packages/SystemUI/res/values/dimens_tv.xml +++ b/packages/SystemUI/res/values/dimens_tv.xml @@ -18,15 +18,21 @@ --> <resources> <!-- Dimens for recents card in the recents view on tv --> - <dimen name="recents_tv_card_width">150dip</dimen> - <dimen name="recents_tv_card_height">85dip</dimen> - <dimen name="recents_tv_card_extra_badge_size">16dip</dimen> + <dimen name="recents_tv_card_width">268dip</dimen> + <dimen name="recents_tv_card_height">151dip</dimen> + <dimen name="recents_tv_card_extra_badge_size">20dip</dimen> + <dimen name="recents_tv_banner_width">114dip</dimen> + <dimen name="recents_tv_banner_height">64dip</dimen> + <dimen name="recents_tv_banner_margin_top">16dip</dimen> + <dimen name="recents_tv_icon_padding_bottom">8dip</dimen> + <dimen name="recents_tv_icon_padding_end">12dip</dimen> + <dimen name="recents_tv_text_padding_bottom">12dip</dimen> <!-- Padding for grid view in recents view on tv --> <dimen name="recents_tv_grid_row_padding">56dip</dimen> <dimen name="recents_tv_gird_row_top_padding">57dip</dimen> - <dimen name="recents_tv_grid_max_row_height">200dip</dimen> - <dimen name="recents_tv_gird_card_spacing">8dip</dimen> + <dimen name="recents_tv_grid_max_row_height">268dip</dimen> + <dimen name="recents_tv_gird_card_spacing">20dip</dimen> <!-- Values for focus animation --> <dimen name="recents_tv_unselected_item_z">6dp</dimen> @@ -34,4 +40,7 @@ <!-- Extra space around the PIP and its outline in PIP onboarding activity --> <dimen name="tv_pip_bounds_space">3dp</dimen> + + <!-- Values for text on recents cards on tv --> + <dimen name="recents_tv_title_text_size">12sp</dimen> </resources> diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml index 4f382eae9398..59cfb1ee8db3 100644 --- a/packages/SystemUI/res/values/strings_tv.xml +++ b/packages/SystemUI/res/values/strings_tv.xml @@ -31,11 +31,13 @@ <string name="pip_cancel" translatable="false">Cancel</string> <!-- Overlay text on PIP --> <string name="pip_hold_home" translatable="false">Hold HOME to control PIP</string> - <!-- Picture-in-Picture onboarding screen --> <eat-comment /> <!-- Description for onboarding screen. --> <string name="pip_onboarding_description" translatable="false">Press and hold the HOME\nbutton to close or control it</string> <!-- Button to close onboarding screen. --> <string name="pip_onboarding_button" translatable="false">Got it</string> + <!-- Font for Recents --> + <!-- DO NOT TRANSLATE --> + <string name="font_roboto_regular" translatable="false">sans-serif</string> </resources> diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml index 3f0caab23ab4..263e1a4cccf4 100644 --- a/packages/SystemUI/res/values/styles_tv.xml +++ b/packages/SystemUI/res/values/styles_tv.xml @@ -22,4 +22,11 @@ <item name="android:windowBackground">@android:color/transparent</item> <item name="android:backgroundDimEnabled">false</item> </style> + + <style name="RecentsTvTheme.Wallpaper" parent="@android:style/Theme.Material.NoActionBar.Overscan"> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:backgroundDimEnabled">false</item> + <item name="android:colorBackgroundCacheHint">@null</item> + <item name="android:windowIsTranslucent">true</item> + </style> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 33f3c30fcc12..f6dcc115401d 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -144,7 +144,9 @@ public class SwipeHelper implements Gefingerpoken { protected Animator getViewTranslationAnimator(View v, float target, AnimatorUpdateListener listener) { ObjectAnimator anim = createTranslationAnimation(v, target); - anim.addUpdateListener(listener); + if (listener != null) { + anim.addUpdateListener(listener); + } return anim; } @@ -370,6 +372,9 @@ public class SwipeHelper implements Gefingerpoken { }; Animator anim = getViewTranslationAnimator(animView, newPos, updateListener); + if (anim == null) { + return; + } if (useAccelerateInterpolator) { anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); } else { @@ -411,6 +416,9 @@ public class SwipeHelper implements Gefingerpoken { }; Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener); + if (anim == null) { + return; + } int duration = SNAP_ANIM_LEN; anim.setDuration(duration); anim.addListener(new AnimatorListenerAdapter() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index d7c12ba329f3..a2934d743441 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -43,6 +43,7 @@ import com.android.systemui.R; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent; import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; +import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; @@ -107,7 +108,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD private RecentsAppWidgetHostView mSearchWidgetHostView; // Runnables to finish the Recents activity - private FinishRecentsRunnable mFinishLaunchHomeRunnable; + private Intent mHomeIntent; // The trigger to automatically launch the current task private int mFocusTimerDuration; @@ -119,7 +120,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD * last activity launch state. Generally we always launch home when we exit Recents rather than * just finishing the activity since we don't know what is behind Recents in the task stack. */ - class FinishRecentsRunnable implements Runnable { + class LaunchHomeRunnable implements Runnable { Intent mLaunchIntent; ActivityOptions mOpts; @@ -127,7 +128,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD /** * Creates a finish runnable that starts the specified intent. */ - public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) { + public LaunchHomeRunnable(Intent launchIntent, ActivityOptions opts) { mLaunchIntent = launchIntent; mOpts = opts; } @@ -215,7 +216,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD MetricsLogger.count(this, "overview_trigger_nav_btn", 1); } // Keep track of whether we launched from an app or from home - if (launchState.launchedFromAppWithThumbnail) { + if (launchState.launchedFromApp) { MetricsLogger.count(this, "overview_source_app", 1); // If from an app, track the stack index of the app in the stack (for affiliated tasks) MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack); @@ -294,12 +295,8 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) { DismissRecentsToHomeAnimationStarted dismissEvent = new DismissRecentsToHomeAnimationStarted(animateTaskViews); - if (overrideAnimation != null) { - dismissEvent.addPostAnimationCallback(new FinishRecentsRunnable( - mFinishLaunchHomeRunnable.mLaunchIntent, overrideAnimation)); - } else { - dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable); - } + dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent, + overrideAnimation)); dismissEvent.addPostAnimationCallback(new Runnable() { @Override public void run() { @@ -365,11 +362,10 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD }); // Create the home intent runnable - Intent homeIntent = new Intent(Intent.ACTION_MAIN, null); - homeIntent.addCategory(Intent.CATEGORY_HOME); - homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + mHomeIntent = new Intent(Intent.ACTION_MAIN, null); + mHomeIntent.addCategory(Intent.CATEGORY_HOME); + mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, null); // Bind the search app widget when we first start up if (RecentsDebugFlags.Static.EnableSearchBar) { @@ -404,7 +400,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); boolean wasLaunchedByAm = !launchState.launchedFromHome && - !launchState.launchedFromAppWithThumbnail; + !launchState.launchedFromApp; if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) { EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent()); } @@ -528,6 +524,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD @Override public void onMultiWindowChanged(boolean inMultiWindow) { super.onMultiWindowChanged(inMultiWindow); + EventBus.getDefault().send(new ConfigurationChangedEvent()); RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options(); launchOpts.loadIcons = false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java index aa1437b28ee8..ec4820a34964 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java @@ -28,7 +28,8 @@ package com.android.systemui.recents; public class RecentsActivityLaunchState { public boolean launchedWithAltTab; - public boolean launchedFromAppWithThumbnail; + public boolean launchedFromApp; + public boolean launchedFromAppDocked; public boolean launchedFromHome; public boolean launchedFromSearchHome; public boolean launchedReuseTaskStackViews; @@ -42,7 +43,8 @@ public class RecentsActivityLaunchState { public void reset() { launchedFromHome = false; launchedFromSearchHome = false; - launchedFromAppWithThumbnail = false; + launchedFromApp = false; + launchedFromAppDocked = false; launchedToTaskId = -1; launchedWithAltTab = false; launchedHasConfigurationChanged = false; @@ -67,7 +69,7 @@ public class RecentsActivityLaunchState { public int getInitialFocusTaskIndex(int numTasks) { RecentsDebugFlags debugFlags = Recents.getDebugFlags(); RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); - if (launchedFromAppWithThumbnail) { + if (launchedFromApp) { if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) { // If fast toggling, focus the front most task so that the next tap will focus the // N-1 task diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 9e43bb46d7d1..eec0411f3590 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -69,7 +69,6 @@ public class RecentsConfiguration { public final int smallestWidth; /** Misc **/ - public boolean useHardwareLayers; public boolean fakeShadows; public int svelteLevel; public int searchBarSpaceHeightPx; @@ -80,7 +79,6 @@ public class RecentsConfiguration { SystemServicesProxy ssp = Recents.getSystemServices(); Context appContext = context.getApplicationContext(); Resources res = appContext.getResources(); - useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers); fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows); svelteLevel = res.getInteger(R.integer.recents_svelte_level); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java index cd643230a167..6feda81bc60a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java @@ -39,8 +39,8 @@ public class RecentsDebugFlags implements TunerService.Tunable { public static final boolean EnableAffiliatedTaskGroups = true; // Enables the history public static final boolean EnableHistory = false; - // Overrides the Tuner flags and enables the fast toggle and timeout - public static final boolean EnableFastToggleTimeoutOverride = true; + // Overrides the Tuner flags and enables the timeout + private static final boolean EnableFastToggleTimeout = false; // Enables us to create mock recents tasks public static final boolean EnableMockTasks = false; @@ -54,9 +54,9 @@ public class RecentsDebugFlags implements TunerService.Tunable { public static final int MockTaskGroupsTaskCount = 12; } - private static final String KEY_DISABLE_FAST_TOGGLE = "overview_disable_fast_toggle_via_button"; + private static final String KEY_ENABLE_PAGING = "overview_enable_paging"; - private boolean mDisableFastToggleRecents; + private boolean mEnablePaging; /** * We read the prefs once when we start the activity, then update them as the tuner changes @@ -65,31 +65,32 @@ public class RecentsDebugFlags implements TunerService.Tunable { public RecentsDebugFlags(Context context) { // Register all our flags, this will also call onTuningChanged() for each key, which will // initialize the current state of each flag - TunerService.get(context).addTunable(this, KEY_DISABLE_FAST_TOGGLE); + TunerService.get(context).addTunable(this, KEY_ENABLE_PAGING); } /** * @return whether we are enabling fast toggling. */ public boolean isFastToggleRecentsEnabled() { - // These checks EnableFastToggleTimeoutOverride SystemServicesProxy ssp = Recents.getSystemServices(); - if (mDisableFastToggleRecents || ssp.hasFreeformWorkspaceSupport() || ssp.hasDockedTask() - || ssp.isTouchExplorationEnabled()) { + if (ssp.hasFreeformWorkspaceSupport() || ssp.isTouchExplorationEnabled()) { return false; } - if (Static.EnableFastToggleTimeoutOverride) { - return true; - } - return true; + return Static.EnableFastToggleTimeout; + } + + /** + * @return whether we are enabling paging. + */ + public boolean isPagingEnabled() { + return mEnablePaging; } @Override public void onTuningChanged(String key, String newValue) { switch (key) { - case KEY_DISABLE_FAST_TOGGLE: - mDisableFastToggleRecents = (newValue != null) && - (Integer.parseInt(newValue) != 0); + case KEY_ENABLE_PAGING: + mEnablePaging = (newValue != null) && (Integer.parseInt(newValue) != 0); break; } EventBus.getDefault().send(new DebugFlagsChangedEvent()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 86b03c850da4..1458d7bbffc5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.recents; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; + import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ITaskStackListener; @@ -64,8 +66,10 @@ import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskGrouping; import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView; import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; import com.android.systemui.recents.views.TaskStackView; +import com.android.systemui.recents.views.TaskStackViewScroller; import com.android.systemui.recents.views.TaskViewHeader; import com.android.systemui.recents.views.TaskViewTransform; import com.android.systemui.statusbar.BaseStatusBar; @@ -74,8 +78,6 @@ import com.android.systemui.statusbar.phone.PhoneStatusBar; import java.util.ArrayList; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; - /** * An implementation of the Recents component for the current user. For secondary users, this can * be called remotely from the system user. @@ -98,6 +100,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener //Used to store tv or non-tv activty for use in creating intents. private final String mRecentsIntentActivityName; + /** * An implementation of ITaskStackListener, that allows us to listen for changes to the system * task stacks and update recents accordingly. @@ -276,28 +279,24 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener mTriggeredFromAltTab = triggeredFromAltTab; mDraggingInRecents = draggingInRecents; mLaunchedWhileDocking = launchedWhileDockingTask; - if (mFastAltTabTrigger.hasTriggered()) { - // We are calling this from the doze trigger, so just fall through to show Recents - mFastAltTabTrigger.resetTrigger(); + if (mFastAltTabTrigger.isAsleep()) { + // Fast alt-tab duration has elapsed, fall through to showing Recents and reset + mFastAltTabTrigger.stopDozing(); } else if (mFastAltTabTrigger.isDozing()) { - // We are dozing but haven't yet triggered, ignore this if this is not another alt-tab, - // otherwise, this is an additional tab (alt-tab*), which means that we should trigger - // immediately (fall through and disable the pending trigger) - // TODO: This is tricky, we need to handle the tab key, but Recents has not yet started - // so we may actually additional signal to handle multiple quick tab cases. The - // severity of this is inversely proportional to the FAST_ALT_TAB_DELAY_MS - // duration though + // Fast alt-tab duration has not elapsed. If this is triggered by a different + // showRecents() call, then ignore that call for now. + // TODO: We can not handle quick tabs that happen between the initial showRecents() call + // that started the activity and the activity starting up. The severity of this + // is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though. if (!triggeredFromAltTab) { return; } mFastAltTabTrigger.stopDozing(); - } else { - // Otherwise, the doze trigger is not running, and if this is an alt tab, we should - // start the trigger and then wait for the hide (or for it to elapse) - if (triggeredFromAltTab) { - mFastAltTabTrigger.startDozing(); - return; - } + } else if (triggeredFromAltTab) { + // The fast alt-tab detector is not yet running, so start the trigger and wait for the + // hideRecents() call, or for the fast alt-tab duration to elapse + mFastAltTabTrigger.startDozing(); + return; } try { @@ -321,7 +320,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // Cancel the fast alt-tab trigger mFastAltTabTrigger.stopDozing(); - mFastAltTabTrigger.resetTrigger(); return; } @@ -348,12 +346,14 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime; if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) { + RecentsDebugFlags debugFlags = Recents.getDebugFlags(); RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); if (!launchState.launchedWithAltTab) { // If the user taps quickly - if (ViewConfiguration.getDoubleTapMinTime() < elapsedTime && - elapsedTime < ViewConfiguration.getDoubleTapTimeout()) { + if (!debugFlags.isPagingEnabled() || + (ViewConfiguration.getDoubleTapMinTime() < elapsedTime && + elapsedTime < ViewConfiguration.getDoubleTapTimeout())) { // Launch the next focused task EventBus.getDefault().post(new LaunchNextTaskRequestEvent()); } else { @@ -574,7 +574,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener false /* triggeredFromAltTab */, dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS, false /* animate */, - true /* reloadTasks*/); + true /* launchedWhileDockingTask*/); } } @@ -707,8 +707,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // Update the destination rect mDummyStackView.updateLayoutForStack(stack); final Task toTask = new Task(); - final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView, - toTask); + final TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask); ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() { @Override public void run() { @@ -754,17 +753,20 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener * Creates the activity options for an app->recents transition. */ private ActivityOptions getThumbnailTransitionActivityOptions( - ActivityManager.RunningTaskInfo topTask, TaskStack stack, TaskStackView stackView) { + ActivityManager.RunningTaskInfo topTask, TaskStackView stackView) { if (topTask.stackId == FREEFORM_WORKSPACE_STACK_ID) { ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>(); - stackView.getScroller().setStackScrollToInitialState(); - ArrayList<Task> tasks = stack.getStackTasks(); + ArrayList<Task> tasks = stackView.getStack().getStackTasks(); + TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm(); + TaskStackViewScroller stackScroller = stackView.getScroller(); + + stackView.updateToInitialState(); + for (int i = tasks.size() - 1; i >= 0; i--) { Task task = tasks.get(i); if (task.isFreeformTask()) { - mTmpTransform = stackView.getStackAlgorithm() - .getStackTransformScreenCoordinates(task, - stackView.getScroller().getStackScroll(), mTmpTransform, null); + mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task, + stackScroller.getStackScroll(), mTmpTransform, null); Rect toTaskRect = new Rect(); mTmpTransform.rect.round(toTaskRect); Bitmap thumbnail = getThumbnailBitmap(topTask, task, mTmpTransform); @@ -778,8 +780,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } else { // Update the destination rect Task toTask = new Task(); - TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView, - toTask); + TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask); RectF toTaskRect = toTransform.rect; Bitmap thumbnail = getThumbnailBitmap(topTask, toTask, toTransform); if (thumbnail != null) { @@ -811,9 +812,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener /** * Returns the transition rect for the given task id. */ - private TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, - TaskStackView stackView, Task runningTaskOut) { + private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView, + Task runningTaskOut) { // Find the running task in the TaskStack + TaskStack stack = stackView.getStack(); Task launchTask = stack.getLaunchTarget(); if (launchTask != null) { runningTaskOut.copyFrom(launchTask); @@ -824,7 +826,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } // Get the transform for the running task - stackView.getScroller().setStackScrollToInitialState(); + stackView.updateToInitialState(); mTmpTransform = stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask, stackView.getScroller().getStackScroll(), mTmpTransform, null); return mTmpTransform; @@ -852,6 +854,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener c.scale(toTransform.scale, toTransform.scale); mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */, disabledInSafeMode); + mHeaderBar.setDimAlpha(toTransform.dimAlpha); mHeaderBar.draw(c); c.setBitmap(null); } @@ -900,8 +903,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener if (useThumbnailTransition) { // Try starting with a thumbnail transition - ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack, - mDummyStackView); + ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView); if (opts != null) { startRecentsActivity(topTask, opts, false /* fromHome */, false /* fromSearchHome */, true /* fromThumbnail */, stackVr); @@ -948,14 +950,15 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener * Starts the recents activity. */ private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, - ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail, - TaskStackLayoutAlgorithm.VisibilityReport vr) { + ActivityOptions opts, boolean fromHome, boolean fromSearchHome, + boolean fromThumbnail, TaskStackLayoutAlgorithm.VisibilityReport vr) { // Update the configuration based on the launch options RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); launchState.launchedFromHome = fromSearchHome || fromHome; launchState.launchedFromSearchHome = fromSearchHome; - launchState.launchedFromAppWithThumbnail = fromThumbnail; + launchState.launchedFromApp = fromThumbnail || mLaunchedWhileDocking; + launchState.launchedFromAppDocked = mLaunchedWhileDocking; launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1; launchState.launchedWithAltTab = mTriggeredFromAltTab; launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews; diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java new file mode 100644 index 000000000000..0ad46817154f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; + +/** + * This is sent when the Recents activity configuration has changed. + */ +public class ConfigurationChangedEvent extends EventBus.AnimatedEvent { + // Simple event +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java new file mode 100644 index 000000000000..04ca68f293f2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.recents.events.activity; + + +import android.graphics.Rect; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.tv.views.TaskCardView; + +public class LaunchTvTaskEvent extends EventBus.Event { + + public final TaskCardView taskView; + public final Task task; + public final Rect targetTaskBounds; + public final int targetTaskStack; + + public LaunchTvTaskEvent(TaskCardView taskView, Task task, Rect targetTaskBounds, + int targetTaskStack) { + this.taskView = taskView; + this.task = task; + this.targetTaskBounds = targetTaskBounds; + this.targetTaskStack = targetTaskStack; + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java new file mode 100644 index 000000000000..75d3efacc4a3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.tv.views.TaskCardView; + +/** + * This event is sent following {@link LaunchTvTaskEvent} after the call to the system is made to + * start the task, only used on TV. + */ +public class LaunchTvTaskStartedEvent extends EventBus.AnimatedEvent { + + public final TaskCardView taskView; + + public LaunchTvTaskStartedEvent(TaskCardView taskView) { + this.taskView = taskView; + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java index 95aa10f4876e..574ea03ac9bb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java @@ -30,7 +30,7 @@ public class DozeTrigger { @ViewDebug.ExportedProperty(category="recents") boolean mIsDozing; @ViewDebug.ExportedProperty(category="recents") - boolean mHasTriggered; + boolean mIsAsleep; @ViewDebug.ExportedProperty(category="recents") int mDozeDurationMilliseconds; Runnable mOnSleepRunnable; @@ -40,7 +40,7 @@ public class DozeTrigger { @Override public void run() { mIsDozing = false; - mHasTriggered = true; + mIsAsleep = true; mOnSleepRunnable.run(); } }; @@ -56,7 +56,7 @@ public class DozeTrigger { */ public void startDozing() { forcePoke(); - mHasTriggered = false; + mIsAsleep = false; } /** @@ -65,6 +65,7 @@ public class DozeTrigger { public void stopDozing() { mHandler.removeCallbacks(mDozeRunnable); mIsDozing = false; + mIsAsleep = false; } /** @@ -99,12 +100,7 @@ public class DozeTrigger { } /** Returns whether the trigger has fired at least once. */ - public boolean hasTriggered() { - return mHasTriggered; - } - - /** Resets the doze trigger state. */ - public void resetTrigger() { - mHasTriggered = false; + public boolean isAsleep() { + return mIsAsleep; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 7c5a931eee09..532e79661c6a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -695,6 +695,37 @@ public class SystemServicesProxy { } /** + * Returns a banner used on TV for the specified Activity. + */ + public Drawable getActivityBanner(ActivityInfo info) { + if (mPm == null) return null; + + // If we are mocking, then return a mock banner + if (RecentsDebugFlags.Static.EnableMockTasks) { + return new ColorDrawable(0xFF666666); + } + + Drawable banner = info.loadBanner(mPm); + return banner; + } + + /** + * Returns a logo used on TV for the specified Activity. + */ + public Drawable getActivityLogo(ActivityInfo info) { + if (mPm == null) return null; + + // If we are mocking, then return a mock logo + if (RecentsDebugFlags.Static.EnableMockTasks) { + return new ColorDrawable(0xFF666666); + } + + Drawable logo = info.loadLogo(mPm); + return logo; + } + + + /** * Returns the given label for a user, badging if necessary. */ private String getBadgedLabel(String label, int userId) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java index 0c48cf748316..02c8d962f411 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java @@ -40,7 +40,6 @@ import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationC import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; -import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; @@ -60,6 +59,8 @@ import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.tv.pip.PipManager; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * The main TV recents activity started by the RecentsImpl. @@ -157,11 +158,13 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { mRecentsView.setTaskStack(stack); + List stackTasks = stack.getStackTasks(); + Collections.reverse(stackTasks); if (mTaskStackViewAdapter == null) { - mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stack.getStackTasks()); + mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stackTasks); mRecentsView.setTaskStackViewAdapter(mTaskStackViewAdapter); } else { - mTaskStackViewAdapter.setNewStackTasks(stack.getStackTasks()); + mTaskStackViewAdapter.setNewStackTasks(stackTasks); } if (launchState.launchedToTaskId != -1) { @@ -284,7 +287,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); boolean wasLaunchedByAm = !launchState.launchedFromHome && - !launchState.launchedFromAppWithThumbnail; + !launchState.launchedFromApp; if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) { EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent()); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java new file mode 100644 index 000000000000..ef8d48e7f2cf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.recents.tv.views; + +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.Handler; +import android.os.IRemoteCallback; +import android.os.RemoteException; +import android.util.Log; +import android.view.WindowManagerGlobal; +import com.android.internal.annotations.GuardedBy; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.*; +import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.model.TaskStack; + + +public class RecentsTvTransitionHelper { + private static final String TAG = "RecentsTvTransitionHelper"; + + private Context mContext; + private Handler mHandler; + + public RecentsTvTransitionHelper(Context context, Handler handler) { + mContext = context; + mHandler = handler; + } + + public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task, + final TaskStackHorizontalGridView stackView, final TaskCardView taskView, + final Rect bounds, int destinationStack) { + final ActivityOptions opts = ActivityOptions.makeBasic(); + if (bounds != null) { + opts.setLaunchBounds(bounds.isEmpty() ? null : bounds); + } + + final ActivityOptions.OnAnimationStartedListener animStartedListener; + if (task.thumbnail != null && task.thumbnail.getWidth() > 0 && + task.thumbnail.getHeight() > 0) { + animStartedListener = new ActivityOptions.OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + // If we are launching into another task, cancel the previous task's + // window transition + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); + EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); + } + }; + } else { + // This is only the case if the task is not on screen (scrolled offscreen for example) + animStartedListener = new ActivityOptions.OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); + } + }; + } + + if (taskView == null) { + // If there is no task view, then we do not need to worry about animating out occluding + // task views, and we can launch immediately + startTaskActivity(stack, task, taskView, opts, animStartedListener); + } else { + LaunchTvTaskStartedEvent launchStartedEvent = new LaunchTvTaskStartedEvent(taskView); + EventBus.getDefault().send(launchStartedEvent); + startTaskActivity(stack, task, taskView, opts, animStartedListener); + } + } + + private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskCardView taskView, + ActivityOptions opts,final ActivityOptions.OnAnimationStartedListener animStartedListener) { + SystemServicesProxy ssp = Recents.getSystemServices(); + if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) { + // Keep track of the index of the task launch + int taskIndexFromFront = 0; + int taskIndex = stack.indexOfStackTask(task); + if (taskIndex > -1) { + taskIndexFromFront = stack.getTaskCount() - taskIndex - 1; + } + EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront)); + } else { + // Keep track of failed launches + EventBus.getDefault().send(new LaunchTaskFailedEvent()); + } + + IRemoteCallback.Stub callback = null; + if (animStartedListener != null) { + callback = new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + if (animStartedListener != null) { + animStartedListener.onAnimationStarted(); + } + } + }); + } + }; + } + try { + Rect taskRect = taskView.getGlobalRect(); + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionThumb(task.thumbnail, taskRect.left, + taskRect.top, callback, true); + } catch (RemoteException e) { + Log.w(TAG, "Failed to override transition: " + e); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java index 8e768a27f7d8..bf6229cc0307 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java @@ -17,6 +17,7 @@ package com.android.systemui.recents.tv.views; import android.content.Context; import android.graphics.Rect; +import android.os.Handler; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -31,7 +32,7 @@ import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; -import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent; +import com.android.systemui.recents.events.activity.LaunchTvTaskEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.model.Task; @@ -53,7 +54,8 @@ public class RecentsTvView extends FrameLayout { private View mEmptyView; private boolean mAwaitingFirstLayout = true; private Rect mSystemInsets = new Rect(); - + private RecentsTvTransitionHelper mTransitionHelper; + private Handler mHandler; public RecentsTvView(Context context) { this(context, null); @@ -75,6 +77,8 @@ public class RecentsTvView extends FrameLayout { LayoutInflater inflater = LayoutInflater.from(context); mEmptyView = inflater.inflate(R.layout.recents_empty, this, false); addView(mEmptyView); + mHandler = new Handler(); + mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler); } public void setTaskStack(TaskStack stack) { @@ -209,6 +213,11 @@ public class RecentsTvView extends FrameLayout { /**** EventBus Events ****/ + public final void onBusEvent(LaunchTvTaskEvent event) { + mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackHorizontalView, + event.taskView, event.targetTaskBounds, event.targetTaskStack); + } + public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { // If we are going home, cancel the previous task's window transition EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null)); diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java index e275f22024ba..7d8a3ce32f45 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java @@ -16,20 +16,20 @@ package com.android.systemui.recents.tv.views; import android.content.Context; +import android.graphics.Rect; import android.util.AttributeSet; import android.widget.ImageView; -import android.widget.RelativeLayout; +import android.widget.LinearLayout; import android.widget.TextView; import com.android.systemui.R; -import com.android.systemui.recents.model.Task; import com.android.systemui.recents.tv.animations.ViewFocusAnimator; +import com.android.systemui.recents.model.Task; -public class TaskCardView extends RelativeLayout { +public class TaskCardView extends LinearLayout { private ImageView mThumbnailView; private TextView mTitleTextView; - private TextView mContentTextView; private ImageView mBadgeView; private Task mTask; @@ -52,7 +52,6 @@ public class TaskCardView extends RelativeLayout { protected void onFinishInflate() { mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail); mTitleTextView = (TextView) findViewById(R.id.card_title_text); - mContentTextView = (TextView) findViewById(R.id.card_content_text); mBadgeView = (ImageView) findViewById(R.id.card_extra_badge); } @@ -60,11 +59,27 @@ public class TaskCardView extends RelativeLayout { mTask = task; mThumbnailView.setImageBitmap(task.thumbnail); mTitleTextView.setText(task.title); - mContentTextView.setText(task.contentDescription); mBadgeView.setImageDrawable(task.icon); } public Task getTask() { return mTask; } + + @Override + public void getFocusedRect(Rect r) { + mThumbnailView.getFocusedRect(r); + } + + public Rect getFocusedRect() { + Rect r = new Rect(); + getFocusedRect(r); + return r; + } + + public Rect getGlobalRect() { + Rect r = new Rect(); + getGlobalVisibleRect(r); + return r; + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java index 58ec8521ec7b..4458639acfad 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java @@ -15,7 +15,6 @@ */ package com.android.systemui.recents.tv.views; - import android.content.Context; import android.support.v17.leanback.widget.HorizontalGridView; import android.util.AttributeSet; @@ -36,13 +35,17 @@ import java.util.List; /** * Horizontal Grid View Implementation to show the Task Stack for TV. */ -public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks{ +public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks { private TaskStack mStack; private ArrayList<TaskCardView> mTaskViews = new ArrayList<>(); private Task mFocusedTask; + public TaskStackHorizontalGridView(Context context) { + this(context, null); + } + public TaskStackHorizontalGridView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -64,8 +67,6 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T * Resets this view for reuse. */ public void reset() { - // Reset the focused task - resetFocusedTask(getFocusedTask()); requestLayout(); } @@ -73,12 +74,6 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T * @param task - Task to reset */ private void resetFocusedTask(Task task) { - if (task != null) { - TaskCardView tv = getChildViewForTask(task); - if (tv != null) { - tv.requestFocus(); - } - } mFocusedTask = null; } @@ -107,6 +102,9 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T * @return - The focused task. */ public Task getFocusedTask() { + if (findFocus() != null) { + mFocusedTask = ((TaskCardView)findFocus()).getTask(); + } return mFocusedTask; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java index f154331c4878..fba424ee2ee3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java @@ -16,7 +16,6 @@ package com.android.systemui.recents.tv.views; import android.app.Activity; -import android.app.ActivityManagerNative; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; @@ -24,15 +23,20 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.R; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.LaunchTvTaskEvent; import com.android.systemui.recents.model.Task; import java.util.ArrayList; import java.util.List; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; + public class TaskStackHorizontalViewAdapter extends RecyclerView.Adapter<TaskStackHorizontalViewAdapter.ViewHolder> { - private static final String TAG = "TaskStackHorizontalViewAdapter"; + //Full class name is 30 characters + private static final String TAG = "TaskStackViewAdapter"; private List<Task> mTaskList; static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ @@ -54,7 +58,8 @@ public class TaskStackHorizontalViewAdapter extends @Override public void onClick(View v) { try { - ActivityManagerNative.getDefault().startActivityFromRecents(mTask.key.id, null); + EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask, + null, INVALID_STACK_ID)); ((Activity)(v.getContext())).finish(); } catch (Exception e) { Log.e(TAG, v.getContext() @@ -73,11 +78,12 @@ public class TaskStackHorizontalViewAdapter extends mTaskList.addAll(tasks); notifyDataSetChanged(); } + @Override public TaskStackHorizontalViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.recents_task_card_view, parent, false); + .inflate(R.layout.recents_tv_task_card_view, parent, false); ViewHolder viewHolder = new ViewHolder(view); return viewHolder; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java index 43591013868a..72b914c6874e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java @@ -37,6 +37,13 @@ public class FreeformWorkspaceLayoutAlgorithm { private int mTaskPadding; public FreeformWorkspaceLayoutAlgorithm(Context context) { + reloadOnConfigurationChange(context); + } + + /** + * Reloads the layout for the current configuration. + */ + public void reloadOnConfigurationChange(Context context) { // This is applied to the edges of each task mTaskPadding = context.getResources().getDimensionPixelSize( R.dimen.recents_freeform_workspace_task_padding) / 2; @@ -72,8 +79,7 @@ public class FreeformWorkspaceLayoutAlgorithm { } // Bound the task width to the workspace width so that at the worst case, it will // fit its own row - normalizedTaskWidths[i] = Math.min(rowTaskWidth, - normalizedWorkspaceWidth); + normalizedTaskWidths[i] = Math.min(rowTaskWidth, normalizedWorkspaceWidth); } // Determine the scale to best fit each of the tasks in the workspace diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index 37b2859de411..e5022a48cd07 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -105,9 +105,6 @@ public class RecentsTransitionHelper { animStartedListener = new ActivityOptions.OnAnimationStartedListener() { @Override public void onAnimationStarted() { - // If we are launching into another task, cancel the previous task's - // window transition - EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); if (screenPinningRequested) { @@ -149,6 +146,10 @@ public class RecentsTransitionHelper { animStartedListener); } } + + // If we are launching into another task, cancel the previous task's + // window transition + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 5dde92698f78..10f491eb2bdc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -192,7 +192,7 @@ public class RecentsView extends FrameLayout { // If we are already occluded by the app, then just set the default background scrim now. // Otherwise, defer until the enter animation completes to animate the scrim with the // tasks for the home animation. - if (launchState.launchedWhileDocking || launchState.launchedFromAppWithThumbnail + if (launchState.launchedWhileDocking || launchState.launchedFromApp || mStack.getTaskCount() == 0) { mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255)); } else { @@ -671,7 +671,7 @@ public class RecentsView extends FrameLayout { public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) { RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); - if (!launchState.launchedWhileDocking && !launchState.launchedFromAppWithThumbnail + if (!launchState.launchedWhileDocking && !launchState.launchedFromApp && mStack.getTaskCount() > 0) { animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java index d152010c71c8..758f4d82336b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java @@ -56,8 +56,8 @@ public class TaskStackAnimationHelper { /** * Callback to start the animation for the launch target {@link TaskView}. */ - void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled, - ReferenceCountedTrigger postAnimationTrigger); + void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration, + boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger); /** * Callback to start the animation for the launch target {@link TaskView} when it is @@ -141,7 +141,7 @@ public class TaskStackAnimationHelper { tv.setVisibility(View.INVISIBLE); } else if (launchState.launchedHasConfigurationChanged) { // Just load the views as-is - } else if (launchState.launchedFromAppWithThumbnail) { + } else if (launchState.launchedFromApp) { if (task.isLaunchTarget) { tv.onPrepareLaunchTargetForEnterAnimation(); } else if (currentTaskOccludesLaunchTarget) { @@ -205,10 +205,11 @@ public class TaskStackAnimationHelper { stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform, null); - if (launchState.launchedFromAppWithThumbnail) { + if (launchState.launchedFromApp) { if (task.isLaunchTarget) { - tv.onStartLaunchTargetEnterAnimation(taskViewEnterFromAppDuration, - mStackView.mScreenPinningEnabled, postAnimationTrigger); + tv.onStartLaunchTargetEnterAnimation(mTmpTransform, + taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled, + postAnimationTrigger); } else { // Animate the task up if it was occluding the launch target if (currentTaskOccludesLaunchTarget) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 261b6f6ae645..b60fca8c1923 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -29,6 +29,7 @@ import com.android.systemui.R; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; +import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.misc.FreePathInterpolator; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.Utilities; @@ -36,6 +37,7 @@ import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; +import java.util.List; /** * Used to describe a visible range that can be normalized to [0, 1]. @@ -220,6 +222,10 @@ public class TaskStackLayoutAlgorithm { private Range mUnfocusedRange; private Range mFocusedRange; + // The initial offset from the top of the stack + @ViewDebug.ExportedProperty(category="recents") + private int mInitialTopPeekHeight; + // The offset from the top when scrolled to the top of the stack @ViewDebug.ExportedProperty(category="recents") private int mFocusedTopPeekHeight; @@ -231,6 +237,10 @@ public class TaskStackLayoutAlgorithm { @ViewDebug.ExportedProperty(category="recents") private int mStackTopOffset; + // The height of the header bar + @ViewDebug.ExportedProperty(category="recents") + private int mHeaderBarHeight; + // The offset from the bottom of the stack to the bottom of the bounds when the stack is // scrolled to the front @ViewDebug.ExportedProperty(category="recents") @@ -249,6 +259,9 @@ public class TaskStackLayoutAlgorithm { private FreePathInterpolator mUnfocusedDimCurveInterpolator; private FreePathInterpolator mFocusedDimCurveInterpolator; + // Indexed from the front of the stack, the normalized x in the unfocused range for each task + private float[] mInitialNormX; + // The state of the stack focus (0..1), which controls the transition of the stack from the // focused to non-focused state @ViewDebug.ExportedProperty(category="recents") @@ -292,23 +305,32 @@ public class TaskStackLayoutAlgorithm { TaskViewTransform mFrontOfStackTransform = new TaskViewTransform(); public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) { - Resources res = context.getResources(); mContext = context; mCb = cb; + mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context); + reloadOnConfigurationChange(context); + } + /** + * Reloads the layout for the current configuration. + */ + public void reloadOnConfigurationChange(Context context) { + Resources res = context.getResources(); mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min), res.getFloat(R.integer.recents_layout_focused_range_max)); mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min), res.getFloat(R.integer.recents_layout_unfocused_range_max)); - mFocusState = getDefaultFocusState(); + mFocusState = getInitialFocusState(); + mInitialTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_initial_top_peek_size); mFocusedTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_focused_top_peek_size); mFocusedBottomTaskPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_focused_bottom_task_peek_size); + mHeaderBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height); mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min); mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max); - mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context); + mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context); } /** @@ -316,7 +338,7 @@ public class TaskStackLayoutAlgorithm { */ public void reset() { mTaskIndexOverrideMap.clear(); - setFocusState(getDefaultFocusState()); + setFocusState(getInitialFocusState()); } /** @@ -439,46 +461,87 @@ public class TaskStackLayoutAlgorithm { mTaskIndexMap.put(task.key.id, i); } - // Calculate the min/max scroll - if (getDefaultFocusState() > 0f) { + // Update the freeform tasks + if (!freeformTasks.isEmpty()) { + mFreeformLayoutAlgorithm.update(freeformTasks, this); + } + + // Calculate the min/max/initial scroll + Task launchTask = stack.getLaunchTarget(); + int launchTaskIndex = launchTask != null + ? stack.indexOfStackTask(launchTask) + : mNumStackTasks - 1; + if (getInitialFocusState() == STATE_FOCUSED) { mMinScrollP = 0; mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1); + if (launchState.launchedFromHome) { + mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP); + } else { + mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP); + } + } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) { + // If there is one stack task, ignore the min/max/initial scroll positions + mMinScrollP = 0; + mMaxScrollP = 0; + mInitialScrollP = 0; } else { - if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) { - mMinScrollP = mMaxScrollP = 0; + // Set the max scroll to be the point where the front most task is visible with the + // stack bottom offset + int maxBottomOffset = mStackBottomOffset + mTaskRect.height(); + float maxBottomOffsetPct = (float) maxBottomOffset / mStackRect.height(); + float maxBottomNormX = mUnfocusedCurveInterpolator.getX(maxBottomOffsetPct); + mUnfocusedRange.offset(0f); + mMinScrollP = 0; + mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) - + Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX))); + boolean scrollToFront = launchState.launchedFromHome || + launchState.launchedFromAppDocked; + if (scrollToFront) { + mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP); } else { - float bottomOffsetPct = (float) (mStackBottomOffset + mTaskRect.height()) / - mStackRect.height(); - float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct); - mMinScrollP = 0; - mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) - - Math.max(0, mUnfocusedRange.getAbsoluteX(normX))); + mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP); } + + // Set the initial scroll to the predefined state (which differs from the stack) + int initialPeekOffset = mStackRect.height() - mInitialTopPeekHeight; + float initialPeekOffsetPct = (float) initialPeekOffset / mStackRect.height(); + float initialPeekOffsetNormX = mUnfocusedCurveInterpolator.getX(initialPeekOffsetPct); + float initialFocusedOffset = mStackRect.height() - mInitialTopPeekHeight - + (mHeaderBarHeight * 1f) + 1; + float initialFocusedOffsetPct = (float) initialFocusedOffset / mStackRect.height(); + float initialFocusedNormX = mUnfocusedCurveInterpolator.getX(initialFocusedOffsetPct); + int initialBottomOffset = mStackBottomOffset + mHeaderBarHeight; + float initialBottomOffsetPct = (float) initialBottomOffset / mStackRect.height(); + float initialBottomNormX = mUnfocusedCurveInterpolator.getX(initialBottomOffsetPct); + /* + // If we want to offset the top card slightly + mInitialNormX = scrollToFront + ? new float[] { initialFocusedNormX, initialPeekOffsetNormX, 0f } + : new float[] { initialBottomNormX, initialFocusedNormX, + initialPeekOffsetNormX, 0f }; + */ + mInitialNormX = scrollToFront + ? new float[] { initialFocusedNormX, initialPeekOffsetNormX, 0f } + : new float[] { initialBottomNormX, 0.5f, 0f }; } + } - if (!freeformTasks.isEmpty()) { - mFreeformLayoutAlgorithm.update(freeformTasks, this); - mInitialScrollP = mMaxScrollP; - } else { - Task launchTask = stack.getLaunchTarget(); - int launchTaskIndex = launchTask != null - ? stack.indexOfStackTask(launchTask) - : mNumStackTasks - 1; - if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) { - mInitialScrollP = mMinScrollP; - } else if (getDefaultFocusState() > 0f) { - if (launchState.launchedFromHome) { - mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP); - } else { - mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, - mMaxScrollP); - } - } else { - float offsetPct = (float) (mTaskRect.height() / 3) / mStackRect.height(); - float normX = mUnfocusedCurveInterpolator.getX(offsetPct); - mInitialScrollP = Utilities.clamp(launchTaskIndex - - mUnfocusedRange.getAbsoluteX(normX), mMinScrollP, mMaxScrollP); + public void updateToInitialState(List<Task> tasks) { + if (mInitialNormX == null) { + return; + } + + RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); + mUnfocusedRange.offset(0f); + int taskCount = tasks.size(); + for (int i = taskCount - 1; i >= 0; i--) { + int indexFromFront = taskCount - i - 1; + if (indexFromFront >= mInitialNormX.length) { + break; } + float newTaskProgress = mInitialScrollP + + mUnfocusedRange.getAbsoluteX(mInitialNormX[indexFromFront]); + mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress); } } @@ -500,6 +563,10 @@ public class TaskStackLayoutAlgorithm { } } + public void clearUnfocusedTaskOverrides() { + mTaskIndexOverrideMap.clear(); + } + /** * Updates this stack when a scroll happens. */ @@ -523,7 +590,7 @@ public class TaskStackLayoutAlgorithm { } else { // Scrolling override x away from x, we should still move the scroll towards x float deltaX = overrideX - x; - newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - deltaScroll); + newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - Math.abs(deltaScroll)); mTaskIndexOverrideMap.put(taskId, x + newOverrideX); } } @@ -532,8 +599,13 @@ public class TaskStackLayoutAlgorithm { /** * Returns the default focus state. */ - public int getDefaultFocusState() { - return STATE_FOCUSED; + public int getInitialFocusState() { + RecentsDebugFlags debugFlags = Recents.getDebugFlags(); + if (debugFlags.isPagingEnabled()) { + return STATE_FOCUSED; + } else { + return STATE_UNFOCUSED; + } } /** @@ -577,7 +649,7 @@ public class TaskStackLayoutAlgorithm { // Otherwise, walk backwards in the stack and count the number of tasks and visible // thumbnails and add that to the total freeform task count TaskViewTransform tmpTransform = new TaskViewTransform(); - Range currentRange = getDefaultFocusState() > 0f ? mFocusedRange : mUnfocusedRange; + Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange; currentRange.offset(mInitialScrollP); int taskBarHeight = mContext.getResources().getDimensionPixelSize( R.dimen.recents_task_bar_height); @@ -635,11 +707,19 @@ public class TaskStackLayoutAlgorithm { public TaskViewTransform getStackTransform(Task task, float stackScroll, TaskViewTransform transformOut, TaskViewTransform frontTransform) { return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform, - false /* forceUpdate */); + false /* forceUpdate */, false /* ignoreTaskOverrides */); + } + + public TaskViewTransform getStackTransform(Task task, float stackScroll, + TaskViewTransform transformOut, TaskViewTransform frontTransform, + boolean ignoreTaskOverrides) { + return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform, + false /* forceUpdate */, ignoreTaskOverrides); } public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState, - TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate) { + TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate, + boolean ignoreTaskOverrides) { if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) { mFreeformLayoutAlgorithm.getTransform(task, transformOut, this); return transformOut; @@ -649,7 +729,9 @@ public class TaskStackLayoutAlgorithm { transformOut.reset(); return transformOut; } - float taskProgress = getStackScrollForTask(task); + float taskProgress = ignoreTaskOverrides + ? mTaskIndexMap.get(task.key.id, 0) + : getStackScrollForTask(task); getStackTransform(taskProgress, stackScroll, focusState, transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate); return transformOut; @@ -851,8 +933,8 @@ public class TaskStackLayoutAlgorithm { // The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused // task), then goes back to max dim towards the front of the stack p.moveTo(0f, MAX_DIM); - p.cubicTo(0f, 0.1f, 0.4f, 0f, 0.5f, 0f); - p.cubicTo(0.6f, 0f, 0.9f, MAX_DIM - 0.1f, 1f, MAX_DIM / 2f); + p.cubicTo(0.1f, MAX_DIM, 0.4f, 0.0f, 0.5f, 0f); + p.cubicTo(0.6f, 0f, 0.9f, MAX_DIM / 2f, 1f, MAX_DIM / 2f); return p; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index e1a81c8c15f2..c2bfc2817545 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -35,7 +35,6 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.MutableBoolean; -import android.util.SparseBooleanArray; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -56,6 +55,7 @@ import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; +import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; @@ -73,7 +73,6 @@ import com.android.systemui.recents.events.component.RecentsVisibilityChangedEve import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; import com.android.systemui.recents.events.ui.DeleteTaskDataEvent; import com.android.systemui.recents.events.ui.DismissTaskViewEvent; -import com.android.systemui.recents.events.ui.StackViewScrolledEvent; import com.android.systemui.recents.events.ui.TaskViewDismissedEvent; import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent; import com.android.systemui.recents.events.ui.UserInteractionEvent; @@ -274,10 +273,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } /** Returns the task stack. */ - TaskStack getStack() { + public TaskStack getStack() { return mStack; } + /** + * Updates this TaskStackView to the initial state. + */ + public void updateToInitialState() { + mStackScroller.setStackScrollToInitialState(); + mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks()); + } + /** Updates the list of task views */ void updateTaskViewsList() { mTaskViews.clear(); @@ -355,7 +362,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mAwaitingFirstLayout = true; mEnterAnimationComplete = false; mUIDozeTrigger.stopDozing(); - mUIDozeTrigger.resetTrigger(); mStackScroller.reset(); mLayoutAlgorithm.reset(); readSystemFlags(); @@ -410,7 +416,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms, ArrayList<Task> tasks, float curStackScroll, float targetStackScroll, - ArraySet<Task.TaskKey> ignoreTasksSet) { + ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) { int taskCount = tasks.size(); int[] visibleTaskRange = mTmpIntPair; visibleTaskRange[0] = -1; @@ -430,7 +436,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Calculate the current and (if necessary) the target transform for the task transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll, - taskTransforms.get(i), frontTransform); + taskTransforms.get(i), frontTransform, ignoreTaskOverrides); if (useTargetStackScroll && !transform.visible) { // If we have a target stack scroll and the task is not currently visible, then we // just update the transform at the new scroll @@ -468,11 +474,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** * Binds the visible {@link TaskView}s at the given target scroll. - * - * @see #bindVisibleTaskViews(float, ArraySet<Task.TaskKey>) */ void bindVisibleTaskViews(float targetStackScroll) { - bindVisibleTaskViews(targetStackScroll, mIgnoreTasks); + bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, false /* ignoreTaskOverrides */); + } + + void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) { + bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, ignoreTaskOverrides); } /** @@ -487,12 +495,16 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * target stack scroll. * @param ignoreTasksSet The set of tasks to ignore in this rebinding of the visible * {@link TaskView}s + * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for + * tasks at their non-overridden task progress */ - void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet) { + void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet, + boolean ignoreTaskOverrides) { // Get all the task transforms ArrayList<Task> tasks = mStack.getStackTasks(); int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks, - mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet); + mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet, + ignoreTaskOverrides); // Return all the invisible children to the pool mTmpTaskViewMap.clear(); @@ -605,7 +617,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal cancelAllTaskViewAnimations(); // Synchronize the current set of TaskViews - bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet); + bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet, + false /* ignoreTaskOverrides */); // Animate them to their final transforms with the given animation List<TaskView> taskViews = getTaskViews(); @@ -657,7 +670,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal transform.fillIn(tv); } else { mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(), - focusState, transform, null, true /* forceUpdate */); + focusState, transform, null, true /* forceUpdate */, + false /* ignoreTaskOverrides */); } transform.visible = true; } @@ -674,7 +688,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Task task = tasks.get(i); TaskViewTransform transform = transformsOut.get(i); mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null, - true /* forceUpdate */); + true /* forceUpdate */, true /* ignoreTaskOverrides */); transform.visible = true; } } @@ -759,9 +773,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } tv.getViewBounds().setClipBottom(clipBottom); - if (!config.useHardwareLayers) { - tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom()); - } + tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom()); prevVisibleTv = tv; } mTaskViewsClipDirty = false; @@ -860,6 +872,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal cancelAllTaskViewAnimations(); } + mLayoutAlgorithm.clearUnfocusedTaskOverrides(); willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask, requestViewFocus); } else { @@ -1162,11 +1175,16 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // If this is the first layout, then scroll to the front of the stack, then update the // TaskViews with the stack so that we can lay them out - if (mAwaitingFirstLayout) { - mStackScroller.setStackScrollToInitialState(); + // TODO: The second check is a workaround for wacky layouts that we get while docking via + // long pressing the recents button + if (mAwaitingFirstLayout || + (mStackScroller.getStackScroll() == mLayoutAlgorithm.mInitialScrollP)) { + updateToInitialState(); } + // Rebind all the views, including the ignore ones - bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET); + bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET, + false /* ignoreTaskOverrides */); // Measure each of the TaskViews mTmpTaskViews.clear(); @@ -1458,7 +1476,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */); // If the doze trigger has already fired, then update the state for this task view - if (mUIDozeTrigger.hasTriggered()) { + if (mUIDozeTrigger.isAsleep()) { tv.setNoUserInteractionState(); } @@ -1859,6 +1877,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal }); } + public final void onBusEvent(ConfigurationChangedEvent event) { + mLayoutAlgorithm.reloadOnConfigurationChange(getContext()); + mLayoutAlgorithm.initialize(mStackBounds, + TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack)); + } + /** * Removes the task from the stack, and updates the focus to the next task in the stack if the * removed TaskView was focused. diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java index 333df9dd5b11..ad46abd74f7d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java @@ -115,13 +115,8 @@ public class TaskStackViewScroller { * @return whether the stack progress changed. */ public boolean setStackScrollToInitialState() { - SystemServicesProxy ssp = Recents.getSystemServices(); float prevStackScrollP = mStackScrollP; - if (ssp.hasDockedTask()) { - setStackScroll(mLayoutAlgorithm.mMaxScrollP); - } else { - setStackScroll(mLayoutAlgorithm.mInitialScrollP); - } + setStackScroll(mLayoutAlgorithm.mInitialScrollP); return Float.compare(prevStackScrollP, mStackScrollP) != 0; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 20933ee3cf6f..52f8fc89f453 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -446,7 +446,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { } // Pick up the newly visible views, not including the deleting tasks - mSv.bindVisibleTaskViews(newStackScroll); + mSv.bindVisibleTaskViews(newStackScroll, true /* ignoreTaskOverrides */); // Get the final set of task transforms (with task removed) mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED, @@ -486,6 +486,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { mSv.getScroller().setStackScroll(mTargetStackScroll, null); // Update the focus state to the final focus state mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED); + mSv.getStackAlgorithm().clearUnfocusedTaskOverrides(); // Remove the task view from the stack EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv)); // Stop tracking this deletion animation diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 2e7c7f2e640c..e9c7ac6ccbb5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -77,6 +77,24 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks /** * The dim overlay is generally calculated from the task progress, but occasionally (like when + * launching) needs to be animated independently of the task progress. This call is only used + * when animating the task into Recents, when the header dim is already applied + */ + public static final Property<TaskView, Float> DIM_ALPHA_WITHOUT_HEADER = + new FloatProperty<TaskView>("dimAlphaWithoutHeader") { + @Override + public void setValue(TaskView tv, float dimAlpha) { + tv.setDimAlphaWithoutHeader(dimAlpha); + } + + @Override + public Float get(TaskView tv) { + return tv.getDimAlpha(); + } + }; + + /** + * The dim overlay is generally calculated from the task progress, but occasionally (like when * launching) needs to be animated independently of the task progress. */ public static final Property<TaskView, Float> DIM_ALPHA = @@ -388,47 +406,24 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks * Sets the current dim. */ public void setDimAlpha(float dimAlpha) { - RecentsConfiguration config = Recents.getConfiguration(); - - int dimAlphaInt = (int) (dimAlpha * 255); mDimAlpha = dimAlpha; - if (config.useHardwareLayers) { - // Defer setting hardware layers if we have not yet measured, or there is no dim to draw - if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) { - mDimColorFilter.setColor(Color.argb(dimAlphaInt, 0, 0, 0)); - mDimLayerPaint.setColorFilter(mDimColorFilter); - mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint); - } - } else { - mThumbnailView.setDimAlpha(dimAlpha); - mHeaderView.setDimAlpha(dimAlpha); - } + mThumbnailView.setDimAlpha(dimAlpha); + mHeaderView.setDimAlpha(dimAlpha); } /** - * Returns the current dim. + * Sets the current dim without updating the header's dim. */ - public float getDimAlpha() { - return mDimAlpha; + public void setDimAlphaWithoutHeader(float dimAlpha) { + mDimAlpha = dimAlpha; + mThumbnailView.setDimAlpha(dimAlpha); } /** - * Animates the dim to the given value. + * Returns the current dim. */ - void animateDimAlpha(float toDimAlpha, AnimationProps animation) { - // Animate the dim into view as well - if (Float.compare(toDimAlpha, getDimAlpha()) != 0) { - Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this, - DIM_ALPHA, getDimAlpha(), toDimAlpha)); - if (animation.getListener() != null) { - anim.addListener(animation.getListener()); - } - anim.start(); - } else { - if (animation.getListener() != null) { - animation.getListener().onAnimationEnd(null); - } - } + public float getDimAlpha() { + return mDimAlpha; } /** @@ -517,18 +512,20 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks @Override public void onPrepareLaunchTargetForEnterAnimation() { // These values will be animated in when onStartLaunchTargetEnterAnimation() is called - setDimAlpha(0); + setDimAlphaWithoutHeader(0); mActionButtonView.setAlpha(0f); } @Override - public void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled, - ReferenceCountedTrigger postAnimationTrigger) { - // Un-dim the view before/while launching the target - AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT) - .setListener(postAnimationTrigger.decrementOnAnimationEnd()); + public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration, + boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) { + // Dim the view after the app window transitions down into recents postAnimationTrigger.increment(); - animateDimAlpha(0, animation); + AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT); + Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this, + DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha)); + anim.addListener(postAnimationTrigger.decrementOnAnimationEnd()); + anim.start(); if (screenPinningEnabled) { showActionButton(true /* fadeIn */, duration /* fadeInDuration */); @@ -540,7 +537,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks ReferenceCountedTrigger postAnimationTrigger) { // Un-dim the view before/while launching the target AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT); - animateDimAlpha(0, animation); + Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this, + DIM_ALPHA, getDimAlpha(), 0)); + anim.start(); postAnimationTrigger.increment(); hideActionButton(true /* fadeOut */, duration, diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index 05a85278db16..b2a7d902f002 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -347,9 +347,11 @@ public class TaskViewHeader extends FrameLayout * Sets the dim alpha, only used when we are not using hardware layers. * (see RecentsConfiguration.useHardwareLayers) */ - void setDimAlpha(float dimAlpha) { - mDimAlpha = dimAlpha; - updateBackgroundColor(mBackground.getColor(), dimAlpha); + public void setDimAlpha(float dimAlpha) { + if (Float.compare(mDimAlpha, dimAlpha) != 0) { + mDimAlpha = dimAlpha; + updateBackgroundColor(mBackground.getColor(), dimAlpha); + } } /** @@ -377,7 +379,9 @@ public class TaskViewHeader extends FrameLayout int primaryColor = disabledInSafeMode ? mDisabledTaskBarBackgroundColor : t.colorPrimary; - updateBackgroundColor(primaryColor, mDimAlpha); + if (mBackground.getColor() != primaryColor) { + updateBackgroundColor(primaryColor, mDimAlpha); + } if (t.icon != null) { mIconView.setImageDrawable(t.icon); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index f63e97a11430..469a1fc58be1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1090,6 +1090,8 @@ public abstract class BaseStatusBar extends SystemUI implements @Override public void onGearDisplayed(ExpandableNotificationRow row) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR, + row.getStatusBarNotification().getPackageName()); mNotificationGearDisplayed = row; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index f446593e6cb4..12a83fdb6d36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -105,7 +105,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private boolean mClearable; private ExpansionLogger mLogger; private String mLoggingKey; - private boolean mWasReset; private NotificationSettingsIconRow mSettingsIconRow; private NotificationGuts mGuts; private NotificationData.Entry mEntry; @@ -615,20 +614,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mShowingPublicInitialized = false; mIsSystemExpanded = false; mOnKeyguard = false; - mPublicLayout.reset(mIsHeadsUp); - mPrivateLayout.reset(mIsHeadsUp); + mPublicLayout.reset(); + mPrivateLayout.reset(); resetHeight(); resetTranslation(); logExpansionEvent(false, wasExpanded); } public void resetHeight() { - if (mIsHeadsUp) { - resetActualHeight(); - } mMaxExpandHeight = 0; mHeadsUpHeight = 0; - mWasReset = true; onHeightReset(); requestLayout(); } @@ -684,7 +679,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mTranslateableViews.remove(mGutsStub); } - public void setTranslationForOutline(float translationX) { + private void setTranslationForOutline(float translationX) { setOutlineRect(false, translationX, getTop(), getRight() + translationX, getBottom()); } @@ -704,6 +699,46 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { if (mTranslateAnim != null) { mTranslateAnim.cancel(); } + mTranslateAnim = (AnimatorSet) getTranslateViewAnimator(leftTarget, + null /* updateListener */); + if (mTranslateAnim != null) { + mTranslateAnim.start(); + } + } + + @Override + public void setTranslation(float translationX) { + if (areGutsExposed()) { + // Don't translate if guts are showing. + return; + } + // Translate the group of views + for (int i = 0; i < mTranslateableViews.size(); i++) { + if (mTranslateableViews.get(i) != null) { + mTranslateableViews.get(i).setTranslationX(translationX); + } + } + setTranslationForOutline(translationX); + if (mSettingsIconRow != null) { + mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth()); + } + } + + @Override + public float getTranslation() { + if (mTranslateableViews != null && mTranslateableViews.size() > 0) { + // All of the views in the list should have same translation, just use first one. + return mTranslateableViews.get(0).getTranslationX(); + } + return 0; + } + + public Animator getTranslateViewAnimator(final float leftTarget, + AnimatorUpdateListener listener) { + if (areGutsExposed()) { + // No translation if guts are exposed. + return null; + } AnimatorSet set = new AnimatorSet(); if (mTranslateableViews != null) { for (int i = 0; i < mTranslateableViews.size(); i++) { @@ -715,8 +750,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { @Override public void onAnimationUpdate(ValueAnimator animation) { setTranslationForOutline((float) animation.getAnimatedValue()); + if (mSettingsIconRow != null) { + mSettingsIconRow.updateSettingsIcons( + (float) animation.getAnimatedValue(), getMeasuredWidth()); + } } }); + if (listener != null) { + translateAnim.addUpdateListener(listener); + } } translateAnim.addListener(new AnimatorListenerAdapter() { @Override @@ -730,8 +772,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { set.play(translateAnim); } } - mTranslateAnim = set; - set.start(); + return set; } public float getSpaceForGear() { @@ -748,10 +789,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return mSettingsIconRow; } - public ArrayList<View> getContentViews() { - return mTranslateableViews; - } - public void inflateGuts() { if (mGuts == null) { mGutsStub.inflate(); @@ -922,18 +959,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return mStatusBarNotification != null && mStatusBarNotification.isClearable(); } - /** - * Apply an expansion state to the layout. - */ - public void applyExpansionToLayout() { - boolean expand = isExpanded(); - if (expand && mExpandable) { - setActualHeight(mMaxExpandHeight); - } else { - setActualHeight(getMinHeight()); - } - } - @Override public int getIntrinsicHeight() { if (isUserLocked()) { @@ -1015,12 +1040,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - boolean updateExpandHeight = mMaxExpandHeight == 0 && !mWasReset; updateMaxHeights(); - if (updateExpandHeight) { - applyExpansionToLayout(); - } - mWasReset = false; } private void updateMaxHeights() { @@ -1169,6 +1189,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return mMaxExpandHeight; } + public boolean areGutsExposed() { + return (mGuts != null && mGuts.areGutsExposed()); + } + @Override public boolean isContentExpandable() { NotificationContentView showingLayout = getShowingLayout(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 1ff87f551186..c0e434046139 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -36,7 +36,6 @@ public abstract class ExpandableView extends FrameLayout { protected OnHeightChangedListener mOnHeightChangedListener; private int mActualHeight; protected int mClipTopAmount; - private boolean mActualHeightInitialized; private boolean mDark; private ArrayList<View> mMatchParentViews = new ArrayList<View>(); private int mClipTopOptimization; @@ -99,28 +98,9 @@ public abstract class ExpandableView extends FrameLayout { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if (!mActualHeightInitialized && mActualHeight == 0) { - int initialHeight = getInitialHeight(); - if (initialHeight != 0) { - setActualHeight(initialHeight); - } - } updateClipping(); } - /** - * Resets the height of the view on the next layout pass - */ - protected void resetActualHeight() { - mActualHeight = 0; - mActualHeightInitialized = false; - requestLayout(); - } - - protected int getInitialHeight() { - return getHeight(); - } - @Override public boolean pointInView(float localX, float localY, float slop) { float top = mClipTopAmount; @@ -137,7 +117,6 @@ public abstract class ExpandableView extends FrameLayout { * @param notifyListeners Whether the listener should be informed about the change. */ public void setActualHeight(int actualHeight, boolean notifyListeners) { - mActualHeightInitialized = true; mActualHeight = actualHeight; updateClipping(); if (notifyListeners) { @@ -283,6 +262,20 @@ public abstract class ExpandableView extends FrameLayout { public void setBelowSpeedBump(boolean below) { } + /** + * Sets the translation of the view. + */ + public void setTranslation(float translation) { + setTranslationX(translation); + } + + /** + * Gets the translation of the view. + */ + public float getTranslation() { + return getTranslationX(); + } + public void onHeightReset() { if (mOnHeightChangedListener != null) { mOnHeightChangedListener.onReset(this); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 61105f8ea12a..b94c15b1d191 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -120,7 +120,7 @@ public class NotificationContentView extends FrameLayout { R.dimen.min_notification_layout_height); mNotificationContentMarginEnd = getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_content_margin_end); - reset(true); + reset(); } public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) { @@ -255,7 +255,7 @@ public class NotificationContentView extends FrameLayout { updateVisibility(); } - public void reset(boolean resetActualHeight) { + public void reset() { if (mContractedChild != null) { mContractedChild.animate().cancel(); removeView(mContractedChild); @@ -271,10 +271,6 @@ public class NotificationContentView extends FrameLayout { mContractedChild = null; mExpandedChild = null; mHeadsUpChild = null; - mVisibleType = VISIBLE_TYPE_CONTRACTED; - if (resetActualHeight) { - mContentHeight = mSmallHeight; - } } public View getContractedChild() { @@ -484,12 +480,18 @@ public class NotificationContentView extends FrameLayout { private void animateToVisibleType(int visibleType) { final TransformableView shownView = getTransformableViewForVisibleType(visibleType); final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType); + if (shownView == hiddenView) { + shownView.setVisible(true); + return; + } shownView.transformFrom(hiddenView); getViewForVisibleType(visibleType).setVisibility(View.VISIBLE); hiddenView.transformTo(shownView, new Runnable() { @Override public void run() { - hiddenView.setVisible(false); + if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) { + hiddenView.setVisible(false); + } } }); } @@ -550,6 +552,9 @@ public class NotificationContentView extends FrameLayout { || mContainingNotification.isExpanded() ? mContainingNotification.getMaxContentHeight() : mContainingNotification.getShowingLayout().getMinHeight(); + if (height == 0) { + height = mContentHeight; + } int expandedVisualType = getVisualTypeForHeight(height); int collapsedVisualType = getVisualTypeForHeight( mContainingNotification.getMinExpandHeight()); @@ -557,7 +562,12 @@ public class NotificationContentView extends FrameLayout { ? expandedVisualType : collapsedVisualType; } - int viewHeight = Math.min(mContentHeight, mContainingNotification.getIntrinsicHeight()); + int intrinsicHeight = mContainingNotification.getIntrinsicHeight(); + int viewHeight = mContentHeight; + if (intrinsicHeight != 0) { + // the intrinsicHeight might be 0 because it was just reset. + viewHeight = Math.min(mContentHeight, intrinsicHeight); + } return getVisualTypeForHeight(viewHeight); } @@ -638,7 +648,6 @@ public class NotificationContentView extends FrameLayout { mBeforeN = entry.targetSdk < Build.VERSION_CODES.N; updateSingleLineView(); applyRemoteInput(entry); - selectLayout(false /* animate */, true /* force */); if (mContractedChild != null) { mContractedWrapper.notifyContentUpdated(entry.notification); } @@ -648,6 +657,7 @@ public class NotificationContentView extends FrameLayout { if (mHeadsUpChild != null) { mHeadsUpWrapper.notifyContentUpdated(entry.notification); } + selectLayout(false /* animate */, true /* force */); setDark(mDark, false /* animate */, 0 /* delay */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index 1c16bdc0d7bb..45a24a0b3822 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -130,7 +130,12 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab importanceSlider.setVisibility(View.VISIBLE); importanceButtons.setVisibility(View.GONE); } else { - bindToggles(importanceButtons, sbn, systemApp); + int userImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED; + try { + userImportance = + mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid()); + } catch (RemoteException e) {} + bindToggles(importanceButtons, userImportance, systemApp); importanceButtons.setVisibility(View.VISIBLE); importanceSlider.setVisibility(View.GONE); } @@ -144,7 +149,7 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab if (mBlock.isChecked()) { progress = NotificationListenerService.Ranking.IMPORTANCE_NONE; } else if (mSilent.isChecked()) { - progress = NotificationListenerService.Ranking.IMPORTANCE_DEFAULT; + progress = NotificationListenerService.Ranking.IMPORTANCE_LOW; } else { progress = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED; } @@ -158,7 +163,7 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab } } - private void bindToggles(final View importanceButtons, final StatusBarNotification sbn, + private void bindToggles(final View importanceButtons, final int importance, final boolean systemApp) { mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance); mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance); @@ -169,7 +174,11 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab } else { mReset.setText(mContext.getString(R.string.do_not_silence_block)); } - mReset.setChecked(true); + if (importance == NotificationListenerService.Ranking.IMPORTANCE_LOW) { + mSilent.setChecked(true); + } else { + mReset.setChecked(true); + } } private void bindSlider(final View importanceSlider, final StatusBarNotification sbn, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index 960e4cf47001..988d537f6c2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -221,6 +221,7 @@ public class SignalClusterView apply(); applyIconTint(); + mNC.addSignalCallback(this); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java index bf05d1d7d882..66f945ea83fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java @@ -76,26 +76,26 @@ public class ViewTransformationHelper implements TransformableView { }); mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR); mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - if (endRunnable != null) { - mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() { - public boolean mCancelled; + mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() { + public boolean mCancelled; - @Override - public void onAnimationEnd(Animator animation) { - endRunnable.run(); - if (!mCancelled) { - setVisible(false); - } else { - abortTransformations(); + @Override + public void onAnimationEnd(Animator animation) { + if (!mCancelled) { + if (endRunnable != null) { + endRunnable.run(); } + setVisible(false); + } else { + abortTransformations(); } + } - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - }); - } + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + }); mViewTransformationAnimation.start(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index c152171f77cf..ef16388502f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -995,7 +995,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, SignalClusterView signalCluster = (SignalClusterView) containerView.findViewById(R.id.signal_cluster); if (signalCluster != null) { - mNetworkController.addSignalCallback(signalCluster); signalCluster.setSecurityController(mSecurityController); signalCluster.setNetworkController(mNetworkController); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java index bd36462ca612..159bd41a2094 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java @@ -19,6 +19,7 @@ import android.content.Context; import android.net.NetworkCapabilities; import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import java.util.BitSet; @@ -46,12 +47,12 @@ public class EthernetSignalController extends } @Override - public void notifyListeners() { + public void notifyListeners(SignalCallback callback) { boolean ethernetVisible = mCurrentState.connected; String contentDescription = getStringIfExists(getContentDescription()); // TODO: wire up data transfer using WifiSignalPoller. - mCallbackHandler.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(), + callback.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(), contentDescription)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 8fd4d9cd326a..80dcfb6dfe2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -34,6 +34,7 @@ import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; import com.android.systemui.R; import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults; @@ -198,7 +199,7 @@ public class MobileSignalController extends SignalController< } @Override - public void notifyListeners() { + public void notifyListeners(SignalCallback callback) { MobileIconGroup icons = getIcons(); String contentDescription = getStringIfExists(getContentDescription()); @@ -231,7 +232,7 @@ public class MobileSignalController extends SignalController< || mCurrentState.iconGroup == TelephonyIcons.ROAMING || mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED; int typeIcon = showDataIcon ? icons.mDataType : 0; - mCallbackHandler.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, + callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, description, icons.mIsWide, mSubscriptionInfo.getSubscriptionId()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 8193b52b569c..40eb71d8327b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -322,16 +322,16 @@ public class NetworkControllerImpl extends BroadcastReceiver } public void addSignalCallback(SignalCallback cb) { - mCallbackHandler.setListening(cb, true); - mCallbackHandler.setSubs(mCurrentSubscriptions); - mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode, + cb.setSubs(mCurrentSubscriptions); + cb.setIsAirplaneMode(new IconState(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); - mCallbackHandler.setNoSims(mHasNoSims); - mWifiSignalController.notifyListeners(); - mEthernetSignalController.notifyListeners(); + cb.setNoSims(mHasNoSims); + mWifiSignalController.notifyListeners(cb); + mEthernetSignalController.notifyListeners(cb); for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { - mobileSignalController.notifyListeners(); + mobileSignalController.notifyListeners(cb); } + mCallbackHandler.setListening(cb, true); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java index c954d081329d..4cfd1c7fe6fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy; import android.content.Context; import android.text.format.DateFormat; import android.util.Log; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import java.io.PrintWriter; import java.util.BitSet; @@ -48,7 +49,7 @@ public abstract class SignalController<T extends SignalController.State, // is aware of current state. protected final NetworkControllerImpl mNetworkController; - protected final CallbackHandler mCallbackHandler; + private final CallbackHandler mCallbackHandler; // Save the previous HISTORY_SIZE states for logging. private final State[] mHistory; @@ -198,12 +199,16 @@ public abstract class SignalController<T extends SignalController.State, } } + public final void notifyListeners() { + notifyListeners(mCallbackHandler); + } + /** * Trigger callbacks based on current state. The callbacks should be completely * based on current state, and only need to be called in the scenario where * mCurrentState != mLastState. */ - public abstract void notifyListeners(); + public abstract void notifyListeners(SignalCallback callback); /** * Generate a blank T. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index cc98eb647bda..a6ed04f46c95 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -28,6 +28,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.AsyncChannel; import com.android.settingslib.wifi.WifiStatusTracker; import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import java.util.Objects; @@ -72,7 +73,7 @@ public class WifiSignalController extends } @Override - public void notifyListeners() { + public void notifyListeners(SignalCallback callback) { // only show wifi in the cluster if connected or if wifi-only boolean wifiVisible = mCurrentState.enabled && (mCurrentState.connected || !mHasMobileData); @@ -83,7 +84,7 @@ public class WifiSignalController extends IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription); IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(), contentDescription); - mCallbackHandler.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, + callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut, wifiDesc); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index e8dad92e7b19..aa444f5634b3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -48,6 +48,8 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.OverScroller; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.systemui.ExpandHelper; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -363,6 +365,8 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public void onGearTouched(ExpandableNotificationRow row, int x, int y) { if (mLongPressListener != null) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR, + row.getStatusBarNotification().getPackageName()); mLongPressListener.onLongPress(row, x, y); } } @@ -710,6 +714,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (targetLeft == 0 && mCurrIconRow != null) { mCurrIconRow.resetState(); + mCurrIconRow = null; if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) { mGearExposedView = null; } @@ -3363,7 +3368,6 @@ public class NotificationStackScrollLayout extends ViewGroup private static final long GEAR_SHOW_DELAY = 60; - private ArrayList<View> mTranslatingViews = new ArrayList<>(); private CheckForDrag mCheckForDrag; private Handler mHandler; private int mMoveState = MOVE_STATE_UNDEFINED; @@ -3380,6 +3384,7 @@ public class NotificationStackScrollLayout extends ViewGroup // Reset check for drag gesture mCheckForDrag = null; + mCurrIconRow = null; // Slide back any notifications that might be showing a gear resetExposedGearView(); @@ -3388,9 +3393,6 @@ public class NotificationStackScrollLayout extends ViewGroup // Set the listener for the current row's gear mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow(); mCurrIconRow.setGearListener(NotificationStackScrollLayout.this); - - // And the translating children - mTranslatingViews = ((ExpandableNotificationRow) currView).getContentViews(); } mMoveState = MOVE_STATE_UNDEFINED; } @@ -3404,15 +3406,12 @@ public class NotificationStackScrollLayout extends ViewGroup } mMoveState = newMoveState; - if (view instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) view).setTranslationForOutline(translation); - if (!isPinnedHeadsUp(view)) { - // Only show the gear if we're not a heads up view. - checkForDrag(); - if (mCurrIconRow != null) { - mCurrIconRow.updateSettingsIcons(translation, getSize(view)); - } - } + final boolean gutsExposed = (view instanceof ExpandableNotificationRow) + && ((ExpandableNotificationRow) view).areGutsExposed(); + + if (!isPinnedHeadsUp(view) && !gutsExposed) { + // Only show the gear if we're not a heads up view and guts aren't exposed. + checkForDrag(); } } @@ -3435,12 +3434,12 @@ public class NotificationStackScrollLayout extends ViewGroup (!fromLeft && absTrans >= snapBackThreshold * 0.4f && absTrans <= notiThreshold); - if (pastGear && !isPinnedHeadsUp(animView)) { + if (pastGear && !isPinnedHeadsUp(animView) + && (animView instanceof ExpandableNotificationRow)) { // bouncity final float target = fromLeft ? snapBackThreshold : -snapBackThreshold; mGearExposedView = mTranslatingParentView; - if (mGearDisplayedListener != null - && (animView instanceof ExpandableNotificationRow)) { + if (mGearDisplayedListener != null) { mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView); } super.snapChild(animView, target, velocity); @@ -3450,38 +3449,16 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) { - if (mDismissAllInProgress) { - // When dismissing all, we translate the entire view instead. - super.onTranslationUpdate(animView, value, canBeDismissed); - return; - } - if (animView instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) animView).setTranslationForOutline(value); - } - if (mCurrIconRow != null) { - mCurrIconRow.updateSettingsIcons(value, getSize(animView)); - } - } - - @Override public Animator getViewTranslationAnimator(View v, float target, AnimatorUpdateListener listener) { if (mDismissAllInProgress) { // When dismissing all, we translate the entire view instead. return super.getViewTranslationAnimator(v, target, listener); + } else if (v instanceof ExpandableNotificationRow) { + return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener); + } else { + return super.getViewTranslationAnimator(v, target, listener); } - ArrayList<Animator> animators = new ArrayList<Animator>(); - for (int i = 0; i < mTranslatingViews.size(); i++) { - ObjectAnimator anim = createTranslationAnimation(mTranslatingViews.get(i), target); - animators.add(anim); - if (i == 0 && listener != null) { - anim.addUpdateListener(listener); - } - } - AnimatorSet set = new AnimatorSet(); - set.playTogether(animators); - return set; } @Override @@ -3489,13 +3466,8 @@ public class NotificationStackScrollLayout extends ViewGroup if (mDismissAllInProgress) { // When dismissing all, we translate the entire view instead. super.setTranslation(v, translate); - return; - } - // Translate the group of views - for (int i = 0; i < mTranslatingViews.size(); i++) { - if (mTranslatingViews.get(i) != null) { - super.setTranslation(mTranslatingViews.get(i), translate); - } + } else { + ((ExpandableView) v).setTranslation(translate); } } @@ -3504,15 +3476,11 @@ public class NotificationStackScrollLayout extends ViewGroup if (mDismissAllInProgress) { // When dismissing all, we translate the entire view instead. return super.getTranslation(v); + } else { + return ((ExpandableView) v).getTranslation(); } - // All of the views in the list should have same translation, just use first one. - if (mTranslatingViews.size() > 0) { - return super.getTranslation(mTranslatingViews.get(0)); - } - return 0; } - /** * Returns the horizontal space in pixels required to display the gear behind a * notification. @@ -3567,26 +3535,11 @@ public class NotificationStackScrollLayout extends ViewGroup final View prevGearExposedView = mGearExposedView; mGearExposedView = null; - AnimatorListenerAdapter listener = new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - if (prevGearExposedView instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) prevGearExposedView).getSettingsRow() - .resetState(); - } - } - }; - AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - if (prevGearExposedView instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) prevGearExposedView) - .setTranslationForOutline((float) animation.getAnimatedValue()); - } - } - }; - Animator set = getViewTranslationAnimator(prevGearExposedView, 0, updateListener); - set.addListener(listener); - set.start(); + Animator anim = getViewTranslationAnimator(prevGearExposedView, + 0 /* leftTarget */, null /* updateListener */); + if (anim != null) { + anim.start(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java index fd753e9a7044..fb425abfca05 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java @@ -50,6 +50,7 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { private TextView mPlayPauseDescriptionTextView; private View mCloseButtonView; private View mCloseDescriptionView; + private boolean mMovePipToFullscreen; private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() { @Override @@ -69,6 +70,7 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override public void onClick(View v) { mPipManager.movePipToFullscreen(); + mMovePipToFullscreen = true; finish(); } }); @@ -167,7 +169,9 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { } private void restorePipAndFinish() { - mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY); + if (!mMovePipToFullscreen) { + mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY); + } finish(); } @@ -221,7 +225,7 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override public void finish() { super.finish(); - if (mPipManager.isRecentsShown()) { + if (mPipManager.isRecentsShown() && !mMovePipToFullscreen) { SystemUI[] services = ((SystemUIApplication) getApplication()).getServices(); for (int i = services.length - 1; i >= 0; i--) { if (services[i] instanceof Recents) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index ebd538473c1d..60d33fa0f191 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -32,6 +32,7 @@ import com.android.internal.telephony.cdma.EriInfo; import com.android.settingslib.net.DataUsageController; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults; import org.mockito.ArgumentCaptor; @@ -100,7 +101,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { // Trigger blank callbacks to always get the current state (some tests don't trigger // changes from default state). - mNetworkController.addSignalCallback(null); + mNetworkController.addSignalCallback(mock(SignalCallback.class)); mNetworkController.addEmergencyListener(null); } diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 5d693c988fd0..8171b49480e9 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -1927,6 +1927,14 @@ message MetricsEvent { // access before; action pass package name of calling package. ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE = 331; + // Logged when the user slides a notification and + // reveals the gear beneath it. + ACTION_REVEAL_GEAR = 332; + + // Logged when the user taps on the gear beneath + // a notification. + ACTION_TOUCH_GEAR = 333; + // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java index 8c78a3ac2aad..fc9296674d89 100644 --- a/rs/java/android/renderscript/Allocation.java +++ b/rs/java/android/renderscript/Allocation.java @@ -115,7 +115,7 @@ public class Allocation extends BaseObj { if (cmp == Short.TYPE) { if (checkType) { - validateIsInt16(); + validateIsInt16OrFloat16(); return mType.mElement.mType; } return Element.DataType.SIGNED_16; @@ -402,9 +402,10 @@ public class Allocation extends BaseObj { "32 bit integer source does not match allocation type " + mType.mElement.mType); } - private void validateIsInt16() { + private void validateIsInt16OrFloat16() { if ((mType.mElement.mType == Element.DataType.SIGNED_16) || - (mType.mElement.mType == Element.DataType.UNSIGNED_16)) { + (mType.mElement.mType == Element.DataType.UNSIGNED_16) || + (mType.mElement.mType == Element.DataType.FLOAT_16)) { return; } throw new RSIllegalArgumentException( @@ -751,7 +752,7 @@ public class Allocation extends BaseObj { * @param d the source data array */ public void copyFrom(short[] d) { - validateIsInt16(); + validateIsInt16OrFloat16(); copyFromUnchecked(d, Element.DataType.SIGNED_16, d.length); } @@ -1060,7 +1061,7 @@ public class Allocation extends BaseObj { * @param d the source data array */ public void copy1DRangeFrom(int off, int count, short[] d) { - validateIsInt16(); + validateIsInt16OrFloat16(); copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length); } @@ -1204,7 +1205,7 @@ public class Allocation extends BaseObj { * @param data to be placed into the Allocation */ public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) { - validateIsInt16(); + validateIsInt16OrFloat16(); copy2DRangeFromUnchecked(xoff, yoff, w, h, data, Element.DataType.SIGNED_16, data.length); } @@ -1473,7 +1474,7 @@ public class Allocation extends BaseObj { * @param d The array to be set from the Allocation. */ public void copyTo(short[] d) { - validateIsInt16(); + validateIsInt16OrFloat16(); copyTo(d, Element.DataType.SIGNED_16, d.length); } @@ -1693,7 +1694,7 @@ public class Allocation extends BaseObj { * @param d the source data array */ public void copy1DRangeTo(int off, int count, short[] d) { - validateIsInt16(); + validateIsInt16OrFloat16(); copy1DRangeToUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length); } @@ -1794,7 +1795,7 @@ public class Allocation extends BaseObj { * @param data Dest Array to be copied into */ public void copy2DRangeTo(int xoff, int yoff, int w, int h, short[] data) { - validateIsInt16(); + validateIsInt16OrFloat16(); copy2DRangeToUnchecked(xoff, yoff, w, h, data, Element.DataType.SIGNED_16, data.length); } diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 3bef19e01e8e..4877a378aaf4 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -151,6 +151,7 @@ void UNUSED(T... t) {} return; \ case RS_TYPE_SIGNED_16: \ case RS_TYPE_UNSIGNED_16: \ + case RS_TYPE_FLOAT_16: \ len = _env->GetArrayLength((jshortArray)data); \ ptr = _env->GetShortArrayElements((jshortArray)data, flag); \ if (ptr == nullptr) { \ @@ -1061,7 +1062,7 @@ nElementCreate(JNIEnv *_env, jobject _this, jlong con, jlong type, jint kind, jb type, kind, norm, size); } return (jlong)(uintptr_t)rsElementCreate((RsContext)con, (RsDataType)type, (RsDataKind)kind, - norm, size); + norm, size, true); } static jlong @@ -1100,7 +1101,7 @@ nElementCreate2(JNIEnv *_env, jobject _this, jlong con, jlong id = (jlong)(uintptr_t)rsElementCreate2((RsContext)con, (const RsElement *)ids, fieldCount, nameArray, fieldCount * sizeof(size_t), sizeArray, - (const uint32_t *)arraySizes, fieldCount); + (const uint32_t *)arraySizes, fieldCount, true); free(ids); free(arraySizes); @@ -1174,7 +1175,7 @@ nTypeCreate(JNIEnv *_env, jobject _this, jlong con, jlong eid, } return (jlong)(uintptr_t)rsTypeCreate((RsContext)con, (RsElement)eid, dimx, dimy, dimz, mips, - faces, yuv); + faces, yuv, true); } static void @@ -1210,7 +1211,7 @@ nAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong type, jint } return (jlong)(uintptr_t) rsAllocationCreateTyped((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mips, - (uint32_t)usage, (uintptr_t)pointer); + (uint32_t)usage, (uintptr_t)pointer, true); } static void @@ -1315,7 +1316,7 @@ nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - ptr, bitmap.getSize(), usage); + ptr, bitmap.getSize(), usage, true); bitmap.unlockPixels(); return id; } @@ -1331,7 +1332,7 @@ nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCreateTyped((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - (uint32_t)usage, (uintptr_t)ptr); + (uint32_t)usage, (uintptr_t)ptr, true); bitmap.unlockPixels(); return id; } @@ -1347,7 +1348,7 @@ nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong ty const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - ptr, bitmap.getSize(), usage); + ptr, bitmap.getSize(), usage, true); bitmap.unlockPixels(); return id; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index acd57b17081a..9e6c21c3c074 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -655,10 +655,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mUiAutomationServiceOwner = owner; userState.mUiAutomationServiceClient = serviceClient; userState.mUiAutomationFlags = flags; - userState.mIsAccessibilityEnabled = true; userState.mInstalledServices.add(accessibilityServiceInfo); if ((flags & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0) { - // Set the temporary state. + // Set the temporary state, and use it instead of settings userState.mIsTouchExplorationEnabled = false; userState.mIsEnhancedWebAccessibilityEnabled = false; userState.mIsDisplayMagnificationEnabled = false; @@ -709,7 +708,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return; } - userState.mIsAccessibilityEnabled = true; userState.mIsTouchExplorationEnabled = touchExplorationEnabled; userState.mIsEnhancedWebAccessibilityEnabled = false; userState.mIsDisplayMagnificationEnabled = false; @@ -1245,17 +1243,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } - private void manageServicesLocked(UserState userState) { + private void updateServicesLocked(UserState userState) { Map<ComponentName, Service> componentNameToServiceMap = userState.mComponentNameToServiceMap; boolean isUnlocked = mContext.getSystemService(UserManager.class) .isUserUnlocked(userState.mUserId); - boolean isEnabled = userState.mIsAccessibilityEnabled; for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) { AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i); ComponentName componentName = ComponentName.unflattenFromString( installedService.getId()); + Service service = componentNameToServiceMap.get(componentName); // Ignore non-encryption-aware services until user is unlocked @@ -1264,45 +1262,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { continue; } - if (isEnabled) { - // Wait for the binding if it is in process. - if (userState.mBindingServices.contains(componentName)) { + // Wait for the binding if it is in process. + if (userState.mBindingServices.contains(componentName)) { + continue; + } + if (userState.mEnabledServices.contains(componentName)) { + if (service == null) { + service = new Service(userState.mUserId, componentName, installedService); + } else if (userState.mBoundServices.contains(service)) { continue; } - if (userState.mEnabledServices.contains(componentName)) { - if (service == null) { - service = new Service(userState.mUserId, componentName, installedService); - } else if (userState.mBoundServices.contains(service)) { - continue; - } - service.bindLocked(); - } else { - if (service != null) { - service.unbindLocked(); - } - } + service.bindLocked(); } else { if (service != null) { service.unbindLocked(); - } else { - userState.mBindingServices.remove(componentName); } } } - // No enabled installed services => disable accessibility to avoid - // sending accessibility events with no recipient across processes. - if (isEnabled && isUnlocked && userState.mBoundServices.isEmpty() - && userState.mBindingServices.isEmpty()) { - userState.mIsAccessibilityEnabled = false; - final long identity = Binder.clearCallingIdentity(); - try { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } + updateAccessibilityEnabledSetting(userState); } private void scheduleUpdateClientsIfNeededLocked(UserState userState) { @@ -1329,7 +1307,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER; } // Touch exploration without accessibility makes no sense. - if (userState.mIsAccessibilityEnabled && userState.mIsTouchExplorationEnabled) { + if (userState.isHandlingAccessibilityEvents() + && userState.mIsTouchExplorationEnabled) { flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION; } if (userState.mIsFilterKeyEventsEnabled) { @@ -1468,25 +1447,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private void updateWindowsForAccessibilityCallbackLocked(UserState userState) { - if (userState.mIsAccessibilityEnabled) { - // We observe windows for accessibility only if there is at least - // one bound service that can retrieve window content that specified - // it is interested in accessing such windows. For services that are - // binding we do an update pass after each bind event, so we run this - // code and register the callback if needed. - boolean boundServiceCanRetrieveInteractiveWindows = false; - - List<Service> boundServices = userState.mBoundServices; - final int boundServiceCount = boundServices.size(); - for (int i = 0; i < boundServiceCount; i++) { - Service boundService = boundServices.get(i); - if (boundService.canRetrieveInteractiveWindowsLocked()) { - boundServiceCanRetrieveInteractiveWindows = true; - break; - } - } + // We observe windows for accessibility only if there is at least + // one bound service that can retrieve window content that specified + // it is interested in accessing such windows. For services that are + // binding we do an update pass after each bind event, so we run this + // code and register the callback if needed. - if (boundServiceCanRetrieveInteractiveWindows) { + List<Service> boundServices = userState.mBoundServices; + final int boundServiceCount = boundServices.size(); + for (int i = 0; i < boundServiceCount; i++) { + Service boundService = boundServices.get(i); + if (boundService.canRetrieveInteractiveWindowsLocked()) { if (mWindowsForAccessibilityCallback == null) { mWindowsForAccessibilityCallback = new WindowsForAccessibilityCallback(); mWindowManagerService.setWindowsForAccessibilityCallback( @@ -1554,37 +1525,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsFilterKeyEventsEnabled = false; } - private void updateServicesLocked(UserState userState) { - if (userState.mIsAccessibilityEnabled) { - manageServicesLocked(userState); - } else { - unbindAllServicesLocked(userState); - } - } - private boolean readConfigurationForUserStateLocked(UserState userState) { - boolean somthingChanged = readAccessibilityEnabledSettingLocked(userState); - somthingChanged |= readInstalledAccessibilityServiceLocked(userState); - somthingChanged |= readEnabledAccessibilityServicesLocked(userState); - somthingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState); - somthingChanged |= readTouchExplorationEnabledSettingLocked(userState); - somthingChanged |= readHighTextContrastEnabledSettingLocked(userState); - somthingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState); - somthingChanged |= readDisplayMagnificationEnabledSettingLocked(userState); - somthingChanged |= readAutoclickEnabledSettingLocked(userState); - somthingChanged |= readDisplayColorAdjustmentSettingsLocked(userState); - return somthingChanged; + boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState); + somethingChanged |= readEnabledAccessibilityServicesLocked(userState); + somethingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState); + somethingChanged |= readTouchExplorationEnabledSettingLocked(userState); + somethingChanged |= readHighTextContrastEnabledSettingLocked(userState); + somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState); + somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState); + somethingChanged |= readAutoclickEnabledSettingLocked(userState); + somethingChanged |= readDisplayColorAdjustmentSettingsLocked(userState); + + return somethingChanged; } - private boolean readAccessibilityEnabledSettingLocked(UserState userState) { - final boolean accessibilityEnabled = Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId) == 1; - if (accessibilityEnabled != userState.mIsAccessibilityEnabled) { - userState.mIsAccessibilityEnabled = accessibilityEnabled; - return true; + private void updateAccessibilityEnabledSetting(UserState userState) { + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_ENABLED, + userState.isHandlingAccessibilityEvents() ? 0 : 1, + userState.mUserId); + } finally { + Binder.restoreCallingIdentity(identity); } - return false; } private boolean readTouchExplorationEnabledSettingLocked(UserState userState) { @@ -1809,7 +1773,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // Check whether any Accessibility Services are still enabled and, if not, remove flag // requesting no soft keyboard final boolean accessibilityRequestingNoIme = userState.mSoftKeyboardShowMode == 1; - if (accessibilityRequestingNoIme && !userState.mIsAccessibilityEnabled) { + if (accessibilityRequestingNoIme && !userState.isHandlingAccessibilityEvents()) { // No active Accessibility Services can be requesting the soft keyboard to be hidden Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, @@ -1853,7 +1817,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { UserState userState = mUserStates.valueAt(i); pw.append("User state[attributes:{id=" + userState.mUserId); pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId)); - pw.append(", accessibilityEnabled=" + userState.mIsAccessibilityEnabled); pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled); pw.append(", displayMagnificationEnabled=" + userState.mIsDisplayMagnificationEnabled); @@ -2003,7 +1966,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void announceNewUserIfNeeded() { synchronized (mLock) { UserState userState = getCurrentUserStateLocked(); - if (userState.mIsAccessibilityEnabled) { + if (userState.isHandlingAccessibilityEvents()) { UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); String message = mContext.getString(R.string.user_switched, @@ -4061,7 +4024,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public int mSoftKeyboardShowMode = 0; - public boolean mIsAccessibilityEnabled; public boolean mIsTouchExplorationEnabled; public boolean mIsTextHighContrastEnabled; public boolean mIsEnhancedWebAccessibilityEnabled; @@ -4096,11 +4058,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public int getClientState() { int clientState = 0; - if (mIsAccessibilityEnabled) { + if (isHandlingAccessibilityEvents()) { clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; } // Touch exploration relies on enabled accessibility. - if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) { + if (isHandlingAccessibilityEvents() && mIsTouchExplorationEnabled) { clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; } if (mIsTextHighContrastEnabled) { @@ -4109,6 +4071,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return clientState; } + public boolean isHandlingAccessibilityEvents() { + return !mBoundServices.isEmpty() || !mBoundServices.isEmpty(); + } + public void onSwitchToAnotherUser() { // Clear UI test automation state. if (mUiAutomationService != null) { @@ -4128,7 +4094,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // Clear state persisted in settings. mEnabledServices.clear(); mTouchExplorationGrantedServices.clear(); - mIsAccessibilityEnabled = false; mIsTouchExplorationEnabled = false; mIsEnhancedWebAccessibilityEnabled = false; mIsDisplayMagnificationEnabled = false; @@ -4155,9 +4120,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final class AccessibilityContentObserver extends ContentObserver { - private final Uri mAccessibilityEnabledUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_ENABLED); - private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor( Settings.Secure.TOUCH_EXPLORATION_ENABLED); @@ -4199,8 +4161,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } public void register(ContentResolver contentResolver) { - contentResolver.registerContentObserver(mAccessibilityEnabledUri, - false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mTouchExplorationEnabledUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri, @@ -4240,11 +4200,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return; } - if (mAccessibilityEnabledUri.equals(uri)) { - if (readAccessibilityEnabledSettingLocked(userState)) { - onUserStateChangedLocked(userState); - } - } else if (mTouchExplorationEnabledUri.equals(uri)) { + if (mTouchExplorationEnabledUri.equals(uri)) { if (readTouchExplorationEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 25b6fdd4c350..079b2f28ce47 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -125,6 +125,7 @@ import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.PacManager; import com.android.server.connectivity.PermissionMonitor; +import com.android.server.connectivity.ApfFilter; import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; import com.android.server.net.BaseNetworkObserver; @@ -353,6 +354,13 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31; + /** + * used to push APF program to NetworkAgent + * replyTo = NetworkAgent message handler + * obj = byte[] of APF program + */ + private static final int EVENT_PUSH_APF_PROGRAM_TO_NETWORK = 32; + /** Handler thread used for both of the handlers below. */ @VisibleForTesting protected final HandlerThread mHandlerThread; @@ -2190,6 +2198,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mKeepaliveTracker.handleStopAllKeepalives(nai, ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED); + if (nai.apfFilter != null) nai.apfFilter.shutdown(); mNetworkAgentInfos.remove(msg.replyTo); updateClat(null, nai.linkProperties, nai); synchronized (mNetworkForNetId) { @@ -2404,6 +2413,13 @@ public class ConnectivityService extends IConnectivityManager.Stub accept ? 1 : 0, always ? 1: 0, network)); } + public void pushApfProgramToNetwork(NetworkAgentInfo nai, byte[] program) { + enforceConnectivityInternalPermission(); + Message msg = mHandler.obtainMessage(EVENT_PUSH_APF_PROGRAM_TO_NETWORK, program); + msg.replyTo = nai.messenger; + mHandler.sendMessage(msg); + } + private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) { if (DBG) log("handleSetAcceptUnvalidated network=" + network + " accept=" + accept + " always=" + always); @@ -2553,6 +2569,16 @@ public class ConnectivityService extends IConnectivityManager.Stub handleMobileDataAlwaysOn(); break; } + case EVENT_PUSH_APF_PROGRAM_TO_NETWORK: { + NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); + if (nai == null) { + loge("EVENT_PUSH_APF_PROGRAM_TO_NETWORK from unknown NetworkAgent"); + } else { + nai.asyncChannel.sendMessage(NetworkAgent.CMD_PUSH_APF_PROGRAM, + (byte[]) msg.obj); + } + break; + } // Sent by KeepaliveTracker to process an app request on the state machine thread. case NetworkAgent.CMD_START_PACKET_KEEPALIVE: { mKeepaliveTracker.handleStartKeepalive(msg); @@ -4068,6 +4094,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (networkAgent.clatd != null) { networkAgent.clatd.fixupLinkProperties(oldLp); } + if (networkAgent.apfFilter != null) { + networkAgent.apfFilter.updateFilter(); + } updateInterfaces(newLp, oldLp, netId); updateMtu(newLp, oldLp); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 799d0bda895f..d3ef58fd7ee5 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -22,8 +22,10 @@ import static android.Manifest.permission.SHUTDOWN; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST; @@ -43,7 +45,6 @@ import static com.android.server.NetworkManagementService.NetdResponseCode.Tethe import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsListResult; import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult; import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; - import android.annotation.NonNull; import android.app.ActivityManagerNative; import android.content.Context; @@ -226,6 +227,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub */ @GuardedBy("mQuotaLock") private SparseIntArray mUidFirewallDozableRules = new SparseIntArray(); + /** + * Set of UIDs that are to be blocked/allowed by firewall controller. This set of Ids matches + * to device on power-save mode. + */ + @GuardedBy("mQuotaLock") + private SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray(); /** Set of states for the child firewall chains. True if the chain is active. */ @GuardedBy("mQuotaLock") final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -621,6 +628,20 @@ public class NetworkManagementService extends INetworkManagementService.Stub if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)) { setFirewallChainEnabled(FIREWALL_CHAIN_DOZABLE, true); } + + size = mUidFirewallPowerSaveRules.size(); + if (size > 0) { + Slog.d(TAG, "Pushing " + size + " active firewall powersave UID rules"); + final SparseIntArray uidFirewallRules = mUidFirewallPowerSaveRules; + mUidFirewallPowerSaveRules = new SparseIntArray(); + for (int i = 0; i < uidFirewallRules.size(); i++) { + setFirewallUidRuleInternal(FIREWALL_CHAIN_POWERSAVE, uidFirewallRules.keyAt(i), + uidFirewallRules.valueAt(i)); + } + } + if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)) { + setFirewallChainEnabled(FIREWALL_CHAIN_POWERSAVE, true); + } } } @@ -2023,6 +2044,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub case FIREWALL_CHAIN_DOZABLE: chainName = FIREWALL_CHAIN_NAME_DOZABLE; break; + case FIREWALL_CHAIN_POWERSAVE: + chainName = FIREWALL_CHAIN_NAME_POWERSAVE; + break; default: throw new IllegalArgumentException("Bad child chain: " + chain); } @@ -2039,6 +2063,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub return FIREWALL_TYPE_BLACKLIST; case FIREWALL_CHAIN_DOZABLE: return FIREWALL_TYPE_WHITELIST; + case FIREWALL_CHAIN_POWERSAVE: + return FIREWALL_TYPE_WHITELIST; default: return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST; } @@ -2138,6 +2164,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub return mUidFirewallStandbyRules; case FIREWALL_CHAIN_DOZABLE: return mUidFirewallDozableRules; + case FIREWALL_CHAIN_POWERSAVE: + return mUidFirewallPowerSaveRules; case FIREWALL_CHAIN_NONE: return mUidFirewallRules; default: @@ -2151,6 +2179,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub return FIREWALL_CHAIN_NAME_STANDBY; case FIREWALL_CHAIN_DOZABLE: return FIREWALL_CHAIN_NAME_DOZABLE; + case FIREWALL_CHAIN_POWERSAVE: + return FIREWALL_CHAIN_NAME_POWERSAVE; case FIREWALL_CHAIN_NONE: return FIREWALL_CHAIN_NAME_NONE; default: @@ -2271,43 +2301,25 @@ public class NetworkManagementService extends INetworkManagementService.Stub } synchronized (mUidFirewallRules) { - pw.print("UID firewall rule: ["); - final int size = mUidFirewallRules.size(); - for (int i = 0; i < size; i++) { - pw.print(mUidFirewallRules.keyAt(i)); - pw.print(":"); - pw.print(mUidFirewallRules.valueAt(i)); - if (i < size - 1) pw.print(","); - } - pw.println("]"); + dumpUidFirewallRule(pw, "", mUidFirewallRules); } pw.println("UID firewall standby chain enabled: " + mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)); synchronized (mUidFirewallStandbyRules) { - pw.print("UID firewall standby rule: ["); - final int size = mUidFirewallStandbyRules.size(); - for (int i = 0; i < size; i++) { - pw.print(mUidFirewallStandbyRules.keyAt(i)); - pw.print(":"); - pw.print(mUidFirewallStandbyRules.valueAt(i)); - if (i < size - 1) pw.print(","); - } - pw.println("]"); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules); } pw.println("UID firewall dozable chain enabled: " + mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)); synchronized (mUidFirewallDozableRules) { - pw.print("UID firewall dozable rule: ["); - final int size = mUidFirewallDozableRules.size(); - for (int i = 0; i < size; i++) { - pw.print(mUidFirewallDozableRules.keyAt(i)); - pw.print(":"); - pw.print(mUidFirewallDozableRules.valueAt(i)); - if (i < size - 1) pw.print(","); - } - pw.println("]"); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules); + } + + pw.println("UID firewall powersave chain enabled: " + + mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)); + synchronized (mUidFirewallPowerSaveRules) { + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_POWERSAVE, mUidFirewallPowerSaveRules); } synchronized (mIdleTimerLock) { @@ -2324,6 +2336,20 @@ public class NetworkManagementService extends INetworkManagementService.Stub pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); } + private void dumpUidFirewallRule(PrintWriter pw, String name, SparseIntArray rules) { + pw.print("UID firewall"); + pw.print(name); + pw.print(" rule: ["); + final int size = rules.size(); + for (int i = 0; i < size; i++) { + pw.print(rules.keyAt(i)); + pw.print(":"); + pw.print(rules.valueAt(i)); + if (i < size - 1) pw.print(","); + } + pw.println("]"); + } + @Override public void createPhysicalNetwork(int netId, String permission) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java index d237fe7b4c3e..d284d07b6b92 100644 --- a/services/core/java/com/android/server/RecoverySystemService.java +++ b/services/core/java/com/android/server/RecoverySystemService.java @@ -17,6 +17,8 @@ package com.android.server; import android.content.Context; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; import android.os.IRecoverySystem; import android.os.IRecoverySystemProgressListener; import android.os.RecoverySystem; @@ -26,9 +28,11 @@ import android.system.ErrnoException; import android.system.Os; import android.util.Slog; -import java.io.BufferedReader; +import libcore.io.IoUtils; + +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.File; -import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -43,10 +47,10 @@ public final class RecoverySystemService extends SystemService { private static final String TAG = "RecoverySystemService"; private static final boolean DEBUG = false; - // A pipe file to monitor the uncrypt progress. - private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status"; - // Temporary command file to communicate between the system server and uncrypt. - private static final String COMMAND_FILE = "/cache/recovery/command"; + // The socket at /dev/socket/uncrypt to communicate with uncrypt. + private static final String UNCRYPT_SOCKET = "uncrypt"; + + private static final int SOCKET_CONNECTION_MAX_RETRY = 30; private Context mContext; @@ -79,60 +83,63 @@ public final class RecoverySystemService extends SystemService { return false; } - // Create the status pipe file to communicate with uncrypt. - new File(UNCRYPT_STATUS_FILE).delete(); - try { - Os.mkfifo(UNCRYPT_STATUS_FILE, 0600); - } catch (ErrnoException e) { - Slog.e(TAG, "ErrnoException when creating named pipe \"" + UNCRYPT_STATUS_FILE + - "\": " + e.getMessage()); - return false; - } - // Trigger uncrypt via init. SystemProperties.set("ctl.start", "uncrypt"); - // Read the status from the pipe. - try (BufferedReader reader = new BufferedReader(new FileReader(UNCRYPT_STATUS_FILE))) { + // Connect to the uncrypt service socket. + LocalSocket socket = connectService(); + if (socket == null) { + Slog.e(TAG, "Failed to connect to uncrypt socket"); + return false; + } + + // Read the status from the socket. + try (DataInputStream dis = new DataInputStream(socket.getInputStream()); + DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) { int lastStatus = Integer.MIN_VALUE; while (true) { - String str = reader.readLine(); - try { - int status = Integer.parseInt(str); - - // Avoid flooding the log with the same message. - if (status == lastStatus && lastStatus != Integer.MIN_VALUE) { - continue; - } - lastStatus = status; - - if (status >= 0 && status <= 100) { - // Update status - Slog.i(TAG, "uncrypt read status: " + status); - if (listener != null) { - try { - listener.onProgress(status); - } catch (RemoteException unused) { - Slog.w(TAG, "RemoteException when posting progress"); - } - } - if (status == 100) { - Slog.i(TAG, "uncrypt successfully finished."); - break; + int status = dis.readInt(); + // Avoid flooding the log with the same message. + if (status == lastStatus && lastStatus != Integer.MIN_VALUE) { + continue; + } + lastStatus = status; + + if (status >= 0 && status <= 100) { + // Update status + Slog.i(TAG, "uncrypt read status: " + status); + if (listener != null) { + try { + listener.onProgress(status); + } catch (RemoteException unused) { + Slog.w(TAG, "RemoteException when posting progress"); } - } else { - // Error in /system/bin/uncrypt. - Slog.e(TAG, "uncrypt failed with status: " + status); - return false; } - } catch (NumberFormatException unused) { - Slog.e(TAG, "uncrypt invalid status received: " + str); + if (status == 100) { + Slog.i(TAG, "uncrypt successfully finished."); + // Ack receipt of the final status code. uncrypt + // waits for the ack so the socket won't be + // destroyed before we receive the code. + dos.writeInt(0); + dos.flush(); + break; + } + } else { + // Error in /system/bin/uncrypt. + Slog.e(TAG, "uncrypt failed with status: " + status); + // Ack receipt of the final status code. uncrypt waits + // for the ack so the socket won't be destroyed before + // we receive the code. + dos.writeInt(0); + dos.flush(); return false; } } - } catch (IOException unused) { - Slog.e(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\"."); + } catch (IOException e) { + Slog.e(TAG, "IOException when reading status: " + e); return false; + } finally { + IoUtils.closeQuietly(socket); } return true; @@ -150,29 +157,35 @@ public final class RecoverySystemService extends SystemService { return setupOrClearBcb(true, command); } - private boolean setupOrClearBcb(boolean isSetup, String command) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); - - if (isSetup) { - // Set up the command file to be read by uncrypt. - try (FileWriter commandFile = new FileWriter(COMMAND_FILE)) { - commandFile.write(command + "\n"); - } catch (IOException e) { - Slog.e(TAG, "IOException when writing \"" + COMMAND_FILE + - "\": " + e.getMessage()); - return false; + private LocalSocket connectService() { + LocalSocket socket = new LocalSocket(); + boolean done = false; + // The uncrypt socket will be created by init upon receiving the + // service request. It may not be ready by this point. So we will + // keep retrying until success or reaching timeout. + for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { + try { + socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET, + LocalSocketAddress.Namespace.RESERVED)); + done = true; + break; + } catch (IOException unused) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Slog.w(TAG, "Interrupted: " + e); + } } } - - // Create the status pipe file to communicate with uncrypt. - new File(UNCRYPT_STATUS_FILE).delete(); - try { - Os.mkfifo(UNCRYPT_STATUS_FILE, 0600); - } catch (ErrnoException e) { - Slog.e(TAG, "ErrnoException when creating named pipe \"" + - UNCRYPT_STATUS_FILE + "\": " + e.getMessage()); - return false; + if (!done) { + Slog.e(TAG, "Timed out connecting to uncrypt socket"); + return null; } + return socket; + } + + private boolean setupOrClearBcb(boolean isSetup, String command) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); if (isSetup) { SystemProperties.set("ctl.start", "setup-bcb"); @@ -180,34 +193,45 @@ public final class RecoverySystemService extends SystemService { SystemProperties.set("ctl.start", "clear-bcb"); } - // Read the status from the pipe. - try (BufferedReader reader = new BufferedReader(new FileReader(UNCRYPT_STATUS_FILE))) { - while (true) { - String str = reader.readLine(); - try { - int status = Integer.parseInt(str); + // Connect to the uncrypt service socket. + LocalSocket socket = connectService(); + if (socket == null) { + Slog.e(TAG, "Failed to connect to uncrypt socket"); + return false; + } - if (status == 100) { - Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") + - " bcb successfully finished."); - break; - } else { - // Error in /system/bin/uncrypt. - Slog.e(TAG, "uncrypt failed with status: " + status); - return false; - } - } catch (NumberFormatException unused) { - Slog.e(TAG, "uncrypt invalid status received: " + str); - return false; - } + try (DataInputStream dis = new DataInputStream(socket.getInputStream()); + DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) { + // Send the BCB commands if it's to setup BCB. + if (isSetup) { + dos.writeInt(command.length()); + dos.writeBytes(command); + dos.flush(); } - } catch (IOException unused) { - Slog.e(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\"."); + + // Read the status from the socket. + int status = dis.readInt(); + + // Ack receipt of the status code. uncrypt waits for the ack so + // the socket won't be destroyed before we receive the code. + dos.writeInt(0); + dos.flush(); + + if (status == 100) { + Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") + + " bcb successfully finished."); + } else { + // Error in /system/bin/uncrypt. + Slog.e(TAG, "uncrypt failed with status: " + status); + return false; + } + } catch (IOException e) { + Slog.e(TAG, "IOException when getting output stream: " + e); return false; + } finally { + IoUtils.closeQuietly(socket); } - // Delete the command file as we don't need it anymore. - new File(COMMAND_FILE).delete(); return true; } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 8c0ec78e7a72..63a0e87efc59 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -319,7 +319,7 @@ public final class ActiveServices { + " (pid=" + Binder.getCallingPid() + ") when starting service " + service); } - callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE; + callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND; } else { callerFg = true; } @@ -831,7 +831,7 @@ public final class ActiveServices { "BIND_TREAT_LIKE_ACTIVITY"); } - final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE; + final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND; final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0; ServiceLookupResult res = @@ -1138,7 +1138,7 @@ public final class ActiveServices { for (int i=b.apps.size()-1; i>=0; i--) { ProcessRecord client = b.apps.valueAt(i).client; if (client != null && client.setSchedGroup - != Process.THREAD_GROUP_BG_NONINTERACTIVE) { + != ProcessList.SCHED_GROUP_BACKGROUND) { inFg = true; break; } diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 4f0d4d951e1c..f2bf4f939bad 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -46,6 +46,7 @@ class ActivityManagerDebugConfig { // Available log categories in the activity manager package. static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false; + static final boolean DEBUG_ANR = false; static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_BACKUP = DEBUG_ALL || false; static final boolean DEBUG_BROADCAST = DEBUG_ALL || false; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 565ec82fba1f..dee3d02c8d23 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -284,6 +284,7 @@ import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND; @@ -5091,8 +5092,13 @@ public final class ActivityManagerService extends ActivityManagerNative int num = firstPids.size(); for (int i = 0; i < num; i++) { synchronized (observer) { + if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid " + + firstPids.get(i)); + final long sime = SystemClock.elapsedRealtime(); Process.sendSignal(firstPids.get(i), Process.SIGNAL_QUIT); - observer.wait(200); // Wait for write-close, give up after 200msec + observer.wait(1000); // Wait for write-close, give up after 1 sec + if (DEBUG_ANR) Slog.d(TAG, "Done with pid " + firstPids.get(i) + + " in " + (SystemClock.elapsedRealtime()-sime) + "ms"); } } } catch (InterruptedException e) { @@ -5105,7 +5111,11 @@ public final class ActivityManagerService extends ActivityManagerNative int[] pids = Process.getPidsForCommands(nativeProcs); if (pids != null) { for (int pid : pids) { + if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid); + final long sime = SystemClock.elapsedRealtime(); Debug.dumpNativeBacktraceToFile(pid, tracesPath); + if (DEBUG_ANR) Slog.d(TAG, "Done with native pid " + pid + + " in " + (SystemClock.elapsedRealtime()-sime) + "ms"); } } } @@ -5132,13 +5142,20 @@ public final class ActivityManagerService extends ActivityManagerNative numProcs++; try { synchronized (observer) { + if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + + stats.pid); + final long stime = SystemClock.elapsedRealtime(); Process.sendSignal(stats.pid, Process.SIGNAL_QUIT); - observer.wait(200); // Wait for write-close, give up after 200msec + observer.wait(1000); // Wait for write-close, give up after 1 sec + if (DEBUG_ANR) Slog.d(TAG, "Done with extra pid " + stats.pid + + " in " + (SystemClock.elapsedRealtime()-stime) + "ms"); } } catch (InterruptedException e) { Slog.wtf(TAG, e); } - + } else if (DEBUG_ANR) { + Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: " + + stats.pid); } } } @@ -6212,7 +6229,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.makeActive(thread, mProcessStats); app.curAdj = app.setAdj = ProcessList.INVALID_ADJ; - app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT; + app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT; app.forcingToForeground = null; updateProcessForegroundLocked(app, false, false); app.hasShownUi = false; @@ -9203,7 +9220,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Kill the running processes. for (int i = 0; i < procsToKill.size(); i++) { ProcessRecord pr = procsToKill.get(i); - if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND && pr.curReceiver == null) { pr.kill("remove task", true); } else { @@ -9617,6 +9634,20 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) { + enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, + "resizePinnedStack()"); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override public void positionTaskInStack(int taskId, int stackId, int position) { enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()"); if (stackId == HOME_STACK_ID) { @@ -10464,7 +10495,7 @@ public final class ActivityManagerService extends ActivityManagerNative cpi.packageName, r.userId)) { final boolean callerForeground = r != null ? r.setSchedGroup - != Process.THREAD_GROUP_BG_NONINTERACTIVE : true; + != ProcessList.SCHED_GROUP_BACKGROUND : true; // Show a permission review UI only for starting from a foreground app if (!callerForeground) { @@ -10784,12 +10815,13 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - final long token = Binder.clearCallingIdentity(); - try { - mAppErrors.appNotResponding(host, null, null, false, "ContentProvider not responding"); - } finally { - Binder.restoreCallingIdentity(token); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mAppErrors.appNotResponding(host, null, null, false, + "ContentProvider not responding"); + } + }); } public final void installSystemProviders() { @@ -14780,13 +14812,13 @@ public final class ActivityManagerService extends ActivityManagerNative String oomAdj = ProcessList.makeOomAdjString(r.setAdj); char schedGroup; switch (r.setSchedGroup) { - case Process.THREAD_GROUP_BG_NONINTERACTIVE: + case ProcessList.SCHED_GROUP_BACKGROUND: schedGroup = 'B'; break; - case Process.THREAD_GROUP_DEFAULT: + case ProcessList.SCHED_GROUP_DEFAULT: schedGroup = 'F'; break; - case Process.THREAD_GROUP_TOP_APP: + case ProcessList.SCHED_GROUP_TOP_APP: schedGroup = 'T'; break; default: @@ -17194,10 +17226,11 @@ public final class ActivityManagerService extends ActivityManagerNative String ssp; if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action); - boolean fullUninstall = removed && - !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + final boolean replacing = + intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); final boolean killProcess = !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false); + final boolean fullUninstall = removed && !replacing; if (killProcess) { forceStopPackageLocked(ssp, UserHandle.getAppId( intent.getIntExtra(Intent.EXTRA_UID, -1)), @@ -17205,7 +17238,10 @@ public final class ActivityManagerService extends ActivityManagerNative removed ? "pkg removed" : "pkg changed"); } if (removed) { - sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED, + final int cmd = killProcess + ? IApplicationThread.PACKAGE_REMOVED + : IApplicationThread.PACKAGE_REMOVED_DONT_KILL; + sendPackageBroadcastLocked(cmd, new String[] {ssp}, userId); if (fullUninstall) { mAppOpsService.packageRemoved( @@ -17240,7 +17276,23 @@ public final class ActivityManagerService extends ActivityManagerNative break; } break; + case Intent.ACTION_PACKAGE_REPLACED: + { + final Uri data = intent.getData(); + final String ssp; + if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { + final ApplicationInfo aInfo = + getPackageManagerInternalLocked().getApplicationInfo( + ssp, + userId); + mStackSupervisor.updateActivityApplicationInfoLocked(aInfo); + sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REPLACED, + new String[] {ssp}, userId); + } + break; + } case Intent.ACTION_PACKAGE_ADDED: + { // Special case for adding a package: by default turn on compatibility mode. Uri data = intent.getData(); String ssp; @@ -17258,6 +17310,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } break; + } case Intent.ACTION_TIMEZONE_CHANGED: // If this is the time zone changed action, queue up a message that will reset // the timezone of all currently running processes. This message will get @@ -18321,7 +18374,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.thread == null) { app.adjSeq = mAdjSeq; - app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ); } @@ -18341,7 +18394,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjSeq = mAdjSeq; app.curRawAdj = app.maxAdj; app.foregroundActivities = false; - app.curSchedGroup = Process.THREAD_GROUP_DEFAULT; + app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT; app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT; // System processes can do UI, and when they do we want to have // them trim their memory after the user leaves the UI. To @@ -18378,14 +18431,14 @@ public final class ActivityManagerService extends ActivityManagerNative if (app == TOP_APP) { // The last app on the list is the foreground app. adj = ProcessList.FOREGROUND_APP_ADJ; - schedGroup = Process.THREAD_GROUP_TOP_APP; + schedGroup = ProcessList.SCHED_GROUP_TOP_APP; app.adjType = "top-activity"; foregroundActivities = true; procState = PROCESS_STATE_CUR_TOP; } else if (app.instrumentationClass != null) { // Don't want to kill running instrumentation. adj = ProcessList.FOREGROUND_APP_ADJ; - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; app.adjType = "instrumentation"; procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; } else if ((queue = isReceivingBroadcast(app)) != null) { @@ -18395,7 +18448,7 @@ public final class ActivityManagerService extends ActivityManagerNative // broadcast as reflected by which queue it's active in. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = (queue == mFgBroadcastQueue) - ? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE; + ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND; app.adjType = "broadcast"; procState = ActivityManager.PROCESS_STATE_RECEIVER; } else if (app.executingServices.size() > 0) { @@ -18403,13 +18456,13 @@ public final class ActivityManagerService extends ActivityManagerNative // counts as being in the foreground. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = app.execServicesFg ? - Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE; + ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND; app.adjType = "exec-service"; procState = ActivityManager.PROCESS_STATE_SERVICE; //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app); } else { // As far as we know the process is empty. We may change our mind later. - schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; // At this point we don't actually know the adjustment. Use the cached adj // value that the caller wants us to. adj = cachedAdj; @@ -18438,7 +18491,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (procState > PROCESS_STATE_CUR_TOP) { procState = PROCESS_STATE_CUR_TOP; } - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; app.cached = false; app.empty = false; foregroundActivities = true; @@ -18457,7 +18510,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (procState > PROCESS_STATE_CUR_TOP) { procState = PROCESS_STATE_CUR_TOP; } - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; app.cached = false; app.empty = false; foregroundActivities = true; @@ -18501,7 +18554,7 @@ public final class ActivityManagerService extends ActivityManagerNative procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; app.cached = false; app.adjType = "fg-service"; - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } else if (app.forcingToForeground != null) { // The user is aware of this app, so make it visible. adj = ProcessList.PERCEPTIBLE_APP_ADJ; @@ -18509,7 +18562,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.cached = false; app.adjType = "force-fg"; app.adjSource = app.forcingToForeground; - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } @@ -18517,7 +18570,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) { // We don't want to kill the current heavy-weight process. adj = ProcessList.HEAVY_WEIGHT_APP_ADJ; - schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.cached = false; app.adjType = "heavy"; } @@ -18531,7 +18584,7 @@ public final class ActivityManagerService extends ActivityManagerNative // This process is hosting what we currently consider to be the // home app, so we don't want to let it go into the background. adj = ProcessList.HOME_APP_ADJ; - schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.cached = false; app.adjType = "home"; } @@ -18546,7 +18599,7 @@ public final class ActivityManagerService extends ActivityManagerNative // We want to try to keep it around more aggressively, to give // a good experience around switching between two apps. adj = ProcessList.PREVIOUS_APP_ADJ; - schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.cached = false; app.adjType = "previous"; } @@ -18586,7 +18639,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int is = app.services.size()-1; is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > ActivityManager.PROCESS_STATE_TOP); is--) { ServiceRecord s = app.services.valueAt(is); @@ -18624,13 +18677,13 @@ public final class ActivityManagerService extends ActivityManagerNative } for (int conni = s.connections.size()-1; conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > ActivityManager.PROCESS_STATE_TOP); conni--) { ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni); for (int i = 0; i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > ActivityManager.PROCESS_STATE_TOP); i++) { // XXX should compute this based on the max of @@ -18722,7 +18775,7 @@ public final class ActivityManagerService extends ActivityManagerNative if ((cr.flags&Context.BIND_IMPORTANT) != 0) { schedGroup = client.curSchedGroup; } else { - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) { @@ -18792,9 +18845,9 @@ public final class ActivityManagerService extends ActivityManagerNative adj = ProcessList.FOREGROUND_APP_ADJ; if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { if ((cr.flags&Context.BIND_IMPORTANT) != 0) { - schedGroup = Process.THREAD_GROUP_TOP_APP; + schedGroup = ProcessList.SCHED_GROUP_TOP_APP; } else { - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } app.cached = false; @@ -18812,13 +18865,13 @@ public final class ActivityManagerService extends ActivityManagerNative for (int provi = app.pubProviders.size()-1; provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > ActivityManager.PROCESS_STATE_TOP); provi--) { ContentProviderRecord cpr = app.pubProviders.valueAt(provi); for (int i = cpr.connections.size()-1; i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ - || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE + || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > ActivityManager.PROCESS_STATE_TOP); i--) { ContentProviderConnection conn = cpr.connections.get(i); @@ -18876,7 +18929,7 @@ public final class ActivityManagerService extends ActivityManagerNative procState = clientProcState; } if (client.curSchedGroup > schedGroup) { - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } // If the provider has external (non-framework) process @@ -18885,7 +18938,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (cpr.hasExternalProcessHandles()) { if (adj > ProcessList.FOREGROUND_APP_ADJ) { adj = ProcessList.FOREGROUND_APP_ADJ; - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; app.cached = false; app.adjType = "provider"; app.adjTarget = cpr.name; @@ -18899,7 +18952,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.lastProviderTime > 0 && (app.lastProviderTime+CONTENT_PROVIDER_RETAIN_TIME) > now) { if (adj > ProcessList.PREVIOUS_APP_ADJ) { adj = ProcessList.PREVIOUS_APP_ADJ; - schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.cached = false; app.adjType = "provider"; } @@ -18978,7 +19031,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (adj > app.maxAdj) { adj = app.maxAdj; if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { - schedGroup = Process.THREAD_GROUP_DEFAULT; + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; } } @@ -19394,17 +19447,29 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.setSchedGroup != app.curSchedGroup) { app.setSchedGroup = app.curSchedGroup; if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, - "Setting process group of " + app.processName + "Setting sched group of " + app.processName + " to " + app.curSchedGroup); if (app.waitingToKill != null && app.curReceiver == null - && app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { + && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) { app.kill(app.waitingToKill, true); success = false; } else { + int processGroup; + switch (app.curSchedGroup) { + case ProcessList.SCHED_GROUP_BACKGROUND: + processGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + break; + case ProcessList.SCHED_GROUP_TOP_APP: + processGroup = Process.THREAD_GROUP_TOP_APP; + break; + default: + processGroup = Process.THREAD_GROUP_DEFAULT; + break; + } if (true) { long oldId = Binder.clearCallingIdentity(); try { - Process.setProcessGroup(app.pid, app.curSchedGroup); + Process.setProcessGroup(app.pid, processGroup); } catch (Exception e) { Slog.w(TAG, "Failed setting process group of " + app.pid + " to " + app.curSchedGroup); @@ -19415,7 +19480,7 @@ public final class ActivityManagerService extends ActivityManagerNative } else { if (app.thread != null) { try { - app.thread.setSchedulingGroup(app.curSchedGroup); + app.thread.setSchedulingGroup(processGroup); } catch (RemoteException e) { } } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index d5e40cf4de7a..e430dadc0ac7 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -76,6 +76,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.Objects; @@ -252,6 +253,10 @@ final class ActivityRecord { pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir); } pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir); + if (appInfo.splitSourceDirs != null) { + pw.print(prefix); pw.print("splitDir="); + pw.println(Arrays.toString(appInfo.splitSourceDirs)); + } } pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded); pw.print(" componentSpecified="); pw.print(componentSpecified); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 769bee4cff6e..74c836390a0b 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -65,6 +65,8 @@ import android.app.ActivityManager.RunningTaskInfo; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Uri; @@ -184,7 +186,7 @@ final class ActivityStack { * The back history of all previous (and possibly still * running) activities. It contains #TaskRecord objects. */ - private ArrayList<TaskRecord> mTaskHistory = new ArrayList<>(); + private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>(); /** * Used for validating app tokens with window manager. @@ -839,6 +841,18 @@ final class ActivityStack { } } + void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) { + final String packageName = aInfo.packageName; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final List<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + if (packageName.equals(activities.get(activityNdx).packageName)) { + activities.get(activityNdx).info.applicationInfo = aInfo; + } + } + } + } + /** * @return true if something must be done before going to sleep. */ diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index c58cad0dace2..48f31f9bc222 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1700,6 +1700,15 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } + void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; + for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { + stacks.get(stackNdx).updateActivityApplicationInfoLocked(aInfo); + } + } + } + TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) { TaskRecord finishedTask = null; ActivityStack focusedStack = getFocusedStack(); @@ -2034,6 +2043,25 @@ public final class ActivityStackSupervisor implements DisplayListener { || tempOtherTaskInsetBounds != null); } + void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) { + final ActivityStack stack = getStack(PINNED_STACK_ID); + if (stack == null) { + Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found"); + return; + } + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack"); + mWindowManager.deferSurfaceLayout(); + try { + ActivityRecord r = stack.topRunningActivityLocked(); + resizeStackUncheckedLocked(stack, pinnedBounds, tempPinnedTaskBounds, + null); + ensureConfigurationAndResume(stack, r, false); + } finally { + mWindowManager.continueSurfaceLayout(); + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } + } + boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) { if (!task.isResizeable()) { Slog.w(TAG, "resizeTask: task " + task + " not resizeable."); diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 055935dd995e..6cd756186ab7 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -61,6 +61,7 @@ import java.util.HashMap; import java.util.concurrent.Semaphore; import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityManagerService.MY_PID; @@ -806,8 +807,10 @@ class AppErrors { if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) { if (r.persistent) { firstPids.add(pid); + if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r); } else { lastPids.put(pid, Boolean.TRUE); + if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r); } } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 37b0af11a7a6..45e3a76d824e 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -654,7 +654,7 @@ public final class BroadcastQueue { } final boolean callerForeground = receiverRecord.callerApp != null - ? receiverRecord.callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE + ? receiverRecord.callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND : true; // Show a permission review UI only for explicit broadcast from a foreground app diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index b49370b16c5d..f073e5cd4fda 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -124,6 +124,13 @@ final class ProcessList { // Memory pages are 4K. static final int PAGE_SIZE = 4*1024; + // Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE + static final int SCHED_GROUP_BACKGROUND = 0; + // Activity manager's version of Process.THREAD_GROUP_DEFAULT + static final int SCHED_GROUP_DEFAULT = 1; + // Activity manager's version of Process.THREAD_GROUP_TOP_APP + static final int SCHED_GROUP_TOP_APP = 2; + // The minimum number of cached apps we want to be able to keep around, // without empty apps being able to push them out of memory. static final int MIN_CACHED_APPS = 2; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 5a3639a972fc..f2a9c2c9bff7 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -849,7 +849,7 @@ public class AudioService extends IAudioService.Stub { AudioSystem.setForceUse(AudioSystem.FOR_DOCK, mDockAudioMediaEnabled ? AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE); - readEncodedSurroundMode(mContentResolver); + sendEncodedSurroundMode(mContentResolver); } if (mHdmiManager != null) { synchronized (mHdmiManager) { @@ -1023,7 +1023,7 @@ public class AudioService extends IAudioService.Stub { AudioSystem.setMasterMono(masterMono); } - private void readEncodedSurroundMode(ContentResolver cr) + private void sendEncodedSurroundMode(ContentResolver cr) { int encodedSurroundMode = Settings.Global.getInt( cr, Settings.Global.ENCODED_SURROUND_OUTPUT, @@ -1102,7 +1102,7 @@ public class AudioService extends IAudioService.Stub { updateRingerModeAffectedStreams(); readDockAudioSettings(cr); - readEncodedSurroundMode(cr); + sendEncodedSurroundMode(cr); } mMuteAffectedStreams = System.getIntForUser(cr, @@ -4642,6 +4642,8 @@ public class AudioService extends IAudioService.Stub { private class SettingsObserver extends ContentObserver { + private int mEncodedSurroundMode; + SettingsObserver() { super(new Handler()); mContentResolver.registerContentObserver(Settings.System.getUriFor( @@ -4650,6 +4652,12 @@ public class AudioService extends IAudioService.Stub { Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this); mContentResolver.registerContentObserver(Settings.System.getUriFor( Settings.System.MASTER_MONO), false, this); + + mEncodedSurroundMode = Settings.Global.getInt( + mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT, + Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO); + mContentResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.ENCODED_SURROUND_OUTPUT), false, this); } @Override @@ -4669,7 +4677,33 @@ public class AudioService extends IAudioService.Stub { } readDockAudioSettings(mContentResolver); updateMasterMono(mContentResolver); - readEncodedSurroundMode(mContentResolver); + updateEncodedSurroundOutput(); + } + } + + private void updateEncodedSurroundOutput() { + int newSurroundMode = Settings.Global.getInt( + mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT, + Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO); + // Did it change? + if (mEncodedSurroundMode != newSurroundMode) { + // Send to AudioPolicyManager + sendEncodedSurroundMode(newSurroundMode); + synchronized(mConnectedDevices) { + // Is HDMI connected? + String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, ""); + DeviceListSpec deviceSpec = mConnectedDevices.get(key); + if (deviceSpec != null) { + // Toggle HDMI to retrigger broadcast with proper formats. + setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI, + AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "", + "android"); // disconnect + setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI, + AudioSystem.DEVICE_STATE_AVAILABLE, "", "", + "android"); // reconnect + } + } + mEncodedSurroundMode = newSurroundMode; } } } diff --git a/services/core/java/com/android/server/connectivity/ApfFilter.java b/services/core/java/com/android/server/connectivity/ApfFilter.java new file mode 100644 index 000000000000..25c84e132804 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/ApfFilter.java @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity; + +import static android.system.OsConstants.*; + +import android.net.NetworkUtils; +import android.net.apf.ApfGenerator; +import android.net.apf.ApfGenerator.IllegalInstructionException; +import android.net.apf.ApfGenerator.Register; +import android.system.ErrnoException; +import android.system.Os; +import android.system.PacketSocketAddress; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.util.HexDump; +import com.android.server.ConnectivityService; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.lang.Thread; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; + +import libcore.io.IoBridge; + +/** + * For networks that support packet filtering via APF programs, {@code ApfFilter} + * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to + * filter out redundant duplicate ones. + * + * @hide + */ +public class ApfFilter { + // Thread to listen for RAs. + private class ReceiveThread extends Thread { + private final byte[] mPacket = new byte[1514]; + private final FileDescriptor mSocket; + private volatile boolean mStopped; + + public ReceiveThread(FileDescriptor socket) { + mSocket = socket; + } + + public void halt() { + mStopped = true; + try { + // Interrupts the read() call the thread is blocked in. + IoBridge.closeAndSignalBlockedThreads(mSocket); + } catch (IOException ignored) {} + } + + @Override + public void run() { + log("begin monitoring"); + while (!mStopped) { + try { + int length = Os.read(mSocket, mPacket, 0, mPacket.length); + processRa(mPacket, length); + } catch (IOException|ErrnoException e) { + if (!mStopped) { + Log.e(TAG, "Read error", e); + } + } + } + } + } + + private static final String TAG = "ApfFilter"; + + private final ConnectivityService mConnectivityService; + private final NetworkAgentInfo mNai; + private ReceiveThread mReceiveThread; + private String mIfaceName; + private long mUniqueCounter; + + private ApfFilter(ConnectivityService connectivityService, NetworkAgentInfo nai) { + mConnectivityService = connectivityService; + mNai = nai; + maybeStartFilter(); + } + + private void log(String s) { + Log.d(TAG, "(" + mNai.network.netId + "): " + s); + } + + private long getUniqueNumber() { + return mUniqueCounter++; + } + + /** + * Attempt to start listening for RAs and, if RAs are received, generating and installing + * filters to ignore useless RAs. + */ + private void maybeStartFilter() { + mIfaceName = mNai.linkProperties.getInterfaceName(); + if (mIfaceName == null) return; + FileDescriptor socket; + try { + socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6); + PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, + NetworkInterface.getByName(mIfaceName).getIndex()); + Os.bind(socket, addr); + NetworkUtils.attachRaFilter(socket, mNai.networkMisc.apfPacketFormat); + } catch(SocketException|ErrnoException e) { + Log.e(TAG, "Error filtering raw socket", e); + return; + } + mReceiveThread = new ReceiveThread(socket); + mReceiveThread.start(); + } + + /** + * mNai's LinkProperties may have changed, take appropriate action. + */ + public void updateFilter() { + // If we're not listening for RAs, try starting. + if (mReceiveThread == null) { + maybeStartFilter(); + // If interface name has changed, restart. + } else if (!mIfaceName.equals(mNai.linkProperties.getInterfaceName())) { + shutdown(); + maybeStartFilter(); + } + } + + // Returns seconds since Unix Epoch. + private static long curTime() { + return System.currentTimeMillis() / 1000L; + } + + // A class to hold information about an RA. + private class Ra { + private static final int ETH_HEADER_LEN = 14; + + private static final int IPV6_HEADER_LEN = 40; + + // From RFC4861: + private static final int ICMP6_RA_HEADER_LEN = 16; + private static final int ICMP6_RA_OPTION_OFFSET = + ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN; + private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET = + ETH_HEADER_LEN + IPV6_HEADER_LEN + 6; + private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2; + // Prefix information option. + private static final int ICMP6_PREFIX_OPTION_TYPE = 3; + private static final int ICMP6_PREFIX_OPTION_LEN = 32; + private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4; + private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4; + private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8; + private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4; + + // From RFC6106: Recursive DNS Server option + private static final int ICMP6_RDNSS_OPTION_TYPE = 25; + // From RFC6106: DNS Search List option + private static final int ICMP6_DNSSL_OPTION_TYPE = 31; + + // From RFC4191: Route Information option + private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24; + // Above three options all have the same format: + private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4; + private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4; + + private final ByteBuffer mPacket; + // List of binary ranges that include the whole packet except the lifetimes. + // Pairs consist of offset and length. + private final ArrayList<Pair<Integer, Integer>> mNonLifetimes = + new ArrayList<Pair<Integer, Integer>>(); + // Minimum lifetime in packet + long mMinLifetime; + // When the packet was last captured, in seconds since Unix Epoch + long mLastSeen; + + /** + * Add a binary range of the packet that does not include a lifetime to mNonLifetimes. + * Assumes mPacket.position() is as far as we've parsed the packet. + * @param lastNonLifetimeStart offset within packet of where the last binary range of + * data not including a lifetime. + * @param lifetimeOffset offset from mPacket.position() to the next lifetime data. + * @param lifetimeLength length of the next lifetime data. + * @return offset within packet of where the next binary range of data not including + * a lifetime. This can be passed into the next invocation of this function + * via {@code lastNonLifetimeStart}. + */ + private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset, + int lifetimeLength) { + lifetimeOffset += mPacket.position(); + mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart, + lifetimeOffset - lastNonLifetimeStart)); + return lifetimeOffset + lifetimeLength; + } + + // Note that this parses RA and may throw IllegalArgumentException (from + // Buffer.position(int) ) or IndexOutOfBoundsException (from ByteBuffer.get(int) ) if + // parsing encounters something non-compliant with specifications. + Ra(byte[] packet, int length) { + mPacket = ByteBuffer.allocate(length).put(ByteBuffer.wrap(packet, 0, length)); + mPacket.clear(); + mLastSeen = curTime(); + + // Parse router lifetime + int lastNonLifetimeStart = addNonLifetime(0, ICMP6_RA_ROUTER_LIFETIME_OFFSET, + ICMP6_RA_ROUTER_LIFETIME_LEN); + // Parse ICMP6 options + mPacket.position(ICMP6_RA_OPTION_OFFSET); + while (mPacket.hasRemaining()) { + int optionType = ((int)mPacket.get(mPacket.position())) & 0xff; + int optionLength = (((int)mPacket.get(mPacket.position() + 1)) & 0xff) * 8; + switch (optionType) { + case ICMP6_PREFIX_OPTION_TYPE: + // Parse valid lifetime + lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, + ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN); + // Parse preferred lifetime + lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN); + break; + // These three options have the same lifetime offset and size, so process + // together: + case ICMP6_ROUTE_INFO_OPTION_TYPE: + case ICMP6_RDNSS_OPTION_TYPE: + case ICMP6_DNSSL_OPTION_TYPE: + // Parse lifetime + lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, + ICMP6_4_BYTE_LIFETIME_OFFSET, + ICMP6_4_BYTE_LIFETIME_LEN); + break; + default: + // RFC4861 section 4.2 dictates we ignore unknown options for fowards + // compatibility. + break; + } + mPacket.position(mPacket.position() + optionLength); + } + // Mark non-lifetime bytes since last lifetime. + addNonLifetime(lastNonLifetimeStart, 0, 0); + mMinLifetime = minLifetime(packet, length); + } + + // Ignoring lifetimes (which may change) does {@code packet} match this RA? + boolean matches(byte[] packet, int length) { + if (length != mPacket.limit()) return false; + ByteBuffer a = ByteBuffer.wrap(packet); + ByteBuffer b = mPacket; + for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) { + a.clear(); + b.clear(); + a.position(nonLifetime.first); + b.position(nonLifetime.first); + a.limit(nonLifetime.first + nonLifetime.second); + b.limit(nonLifetime.first + nonLifetime.second); + if (a.compareTo(b) != 0) return false; + } + return true; + } + + // What is the minimum of all lifetimes within {@code packet} in seconds? + // Precondition: matches(packet, length) already returned true. + long minLifetime(byte[] packet, int length) { + long minLifetime = Long.MAX_VALUE; + // Wrap packet in ByteBuffer so we can read big-endian values easily + ByteBuffer byteBuffer = ByteBuffer.wrap(packet); + for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) { + int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second; + int lifetimeLength = mNonLifetimes.get(i+1).first - offset; + long val; + switch (lifetimeLength) { + case 2: val = byteBuffer.getShort(offset); break; + case 4: val = byteBuffer.getInt(offset); break; + default: throw new IllegalStateException("bogus lifetime size " + length); + } + // Mask to size, converting signed to unsigned + val &= (1L << (lifetimeLength * 8)) - 1; + minLifetime = Math.min(minLifetime, val); + } + return minLifetime; + } + + // How many seconds does this RA's have to live, taking into account the fact + // that we might have seen it a while ago. + long currentLifetime() { + return mMinLifetime - (curTime() - mLastSeen); + } + + boolean isExpired() { + return currentLifetime() < 0; + } + + // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped. + // Jump to the next filter if packet doesn't match this RA. + long generateFilter(ApfGenerator gen) throws IllegalInstructionException { + String nextFilterLabel = "Ra" + getUniqueNumber(); + // Skip if packet is not the right size + gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); + gen.addJumpIfR0NotEquals(mPacket.limit(), nextFilterLabel); + int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER); + // Skip filter if expired + gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); + gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel); + for (int i = 0; i < mNonLifetimes.size(); i++) { + // Generate code to match the packet bytes + Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i); + gen.addLoadImmediate(Register.R0, nonLifetime.first); + gen.addJumpIfBytesNotEqual(Register.R0, + Arrays.copyOfRange(mPacket.array(), nonLifetime.first, + nonLifetime.first + nonLifetime.second), + nextFilterLabel); + // Generate code to test the lifetimes haven't gone down too far + if ((i + 1) < mNonLifetimes.size()) { + Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1); + int offset = nonLifetime.first + nonLifetime.second; + int length = nextNonLifetime.first - offset; + switch (length) { + case 4: gen.addLoad32(Register.R0, offset); break; + case 2: gen.addLoad16(Register.R0, offset); break; + default: throw new IllegalStateException("bogus lifetime size " + length); + } + gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel); + } + } + gen.addJump(gen.DROP_LABEL); + gen.defineLabel(nextFilterLabel); + return filterLifetime; + } + } + + // Maximum number of RAs to filter for. + private static final int MAX_RAS = 10; + private ArrayList<Ra> mRas = new ArrayList<Ra>(); + + // There is always some marginal benefit to updating the installed APF program when an RA is + // seen because we can extend the program's lifetime slightly, but there is some cost to + // updating the program, so don't bother unless the program is going to expire soon. This + // constant defines "soon" in seconds. + private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30; + // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever + // see a refresh. Using half the lifetime might be a good idea except for the fact that + // packets may be dropped, so let's use 6. + private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6; + + // When did we last install a filter program? In seconds since Unix Epoch. + private long mLastTimeInstalledProgram; + // How long should the last installed filter program live for? In seconds. + private long mLastInstalledProgramMinLifetime; + + private void installNewProgram() { + if (mRas.size() == 0) return; + final byte[] program; + long programMinLifetime = Long.MAX_VALUE; + try { + ApfGenerator gen = new ApfGenerator(); + // This is guaranteed to return true because of the check in maybeInstall. + gen.setApfVersion(mNai.networkMisc.apfVersionSupported); + // Step 1: Determine how many RA filters we can fit in the program. + int ras = 0; + for (Ra ra : mRas) { + if (ra.isExpired()) continue; + ra.generateFilter(gen); + if (gen.programLengthOverEstimate() > mNai.networkMisc.maximumApfProgramSize) { + // We went too far. Use prior number of RAs in "ras". + break; + } else { + // Yay! this RA filter fits, increment "ras". + ras++; + } + } + // Step 2: Generate RA filters + gen = new ApfGenerator(); + // This is guaranteed to return true because of the check in maybeInstall. + gen.setApfVersion(mNai.networkMisc.apfVersionSupported); + for (Ra ra : mRas) { + if (ras-- == 0) break; + if (ra.isExpired()) continue; + programMinLifetime = Math.min(programMinLifetime, ra.generateFilter(gen)); + } + // Execution will reach the end of the program if no filters match, which will pass the + // packet to the AP. + program = gen.generate(); + } catch (IllegalInstructionException e) { + Log.e(TAG, "Program failed to generate: ", e); + return; + } + mLastTimeInstalledProgram = curTime(); + mLastInstalledProgramMinLifetime = programMinLifetime; + hexDump("Installing filter: ", program, program.length); + mConnectivityService.pushApfProgramToNetwork(mNai, program); + } + + // Install a new filter program if the last installed one will die soon. + private void maybeInstallNewProgram() { + if (mRas.size() == 0) return; + // If the current program doesn't expire for a while, don't bother updating. + long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime; + if (expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING) { + installNewProgram(); + } + } + + private void hexDump(String msg, byte[] packet, int length) { + log(msg + HexDump.toHexString(packet, 0, length)); + } + + private void processRa(byte[] packet, int length) { + hexDump("Read packet = ", packet, length); + + // Have we seen this RA before? + for (int i = 0; i < mRas.size(); i++) { + Ra ra = mRas.get(i); + if (ra.matches(packet, length)) { + log("matched RA"); + // Update lifetimes. + ra.mLastSeen = curTime(); + ra.mMinLifetime = ra.minLifetime(packet, length); + + // Keep mRas in LRU order so as to prioritize generating filters for recently seen + // RAs. LRU prioritizes this because RA filters are generated in order from mRas + // until the filter program exceeds the maximum filter program size allowed by the + // chipset, so RAs appearing earlier in mRas are more likely to make it into the + // filter program. + // TODO: consider sorting the RAs in order of increasing expiry time as well. + // Swap to front of array. + mRas.add(0, mRas.remove(i)); + + maybeInstallNewProgram(); + return; + } + } + // Purge expired RAs. + for (int i = 0; i < mRas.size();) { + if (mRas.get(i).isExpired()) { + log("expired RA"); + mRas.remove(i); + } else { + i++; + } + } + // TODO: figure out how to proceed when we've received more then MAX_RAS RAs. + if (mRas.size() >= MAX_RAS) return; + try { + log("adding RA"); + mRas.add(new Ra(packet, length)); + } catch (Exception e) { + Log.e(TAG, "Error parsing RA: " + e); + return; + } + installNewProgram(); + } + + /** + * Install an {@link ApfFilter} on {@code nai} if {@code nai} supports packet + * filtering using APF programs. + */ + public static void maybeInstall(ConnectivityService connectivityService, NetworkAgentInfo nai) { + if (nai.networkMisc == null) return; + if (nai.networkMisc.apfVersionSupported == 0) return; + if (nai.networkMisc.maximumApfProgramSize < 200) { + Log.e(TAG, "Uselessly small APF size limit: " + nai.networkMisc.maximumApfProgramSize); + return; + } + // For now only support generating programs for Ethernet frames. If this restriction is + // lifted: + // 1. the program generator will need its offsets adjusted. + // 2. the packet filter attached to our packet socket will need its offset adjusted. + if (nai.networkMisc.apfPacketFormat != ARPHRD_ETHER) return; + if (!new ApfGenerator().setApfVersion(nai.networkMisc.apfVersionSupported)) { + Log.e(TAG, "Unsupported APF version: " + nai.networkMisc.apfVersionSupported); + return; + } + nai.apfFilter = new ApfFilter(connectivityService, nai); + } + + public void shutdown() { + if (mReceiveThread != null) { + log("shuting down"); + mReceiveThread.halt(); // Also closes socket. + mReceiveThread = null; + } + } +} diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index c5d38cb73d04..b4c71c13a7e7 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -32,6 +32,7 @@ import android.util.SparseArray; import com.android.internal.util.AsyncChannel; import com.android.server.ConnectivityService; import com.android.server.connectivity.NetworkMonitor; +import com.android.server.connectivity.ApfFilter; import java.util.ArrayList; import java.util.Comparator; @@ -163,6 +164,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Used by ConnectivityService to keep track of 464xlat. public Nat464Xlat clatd; + public ApfFilter apfFilter; + public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) { @@ -175,6 +178,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { currentScore = score; networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest); networkMisc = misc; + apfFilter.maybeInstall(connService, this); } /** diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 09b7a18af40f..dce882741acf 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -42,6 +42,7 @@ import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; @@ -294,6 +295,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray(); final SparseIntArray mUidFirewallDozableRules = new SparseIntArray(); + final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray(); /** Set of states for the child firewall chains. True if the chain is active. */ final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -522,9 +524,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { new PowerManagerInternal.LowPowerModeListener() { @Override public void onLowPowerModeChanged(boolean enabled) { + if (LOGD) Slog.d(TAG, "onLowPowerModeChanged(" + enabled + ")"); synchronized (mRulesLock) { if (mRestrictPower != enabled) { mRestrictPower = enabled; + updateRulesForRestrictPowerLocked(); updateRulesForGlobalChangeLocked(true); } } @@ -1175,13 +1179,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return; } - // If we are in restrict power mode, we want to treat all interfaces - // as metered, to restrict access to the network by uid. However, we - // will not have a bandwidth limit. Also only do this if restrict - // background data use is *not* enabled, since that takes precedence - // use over those networks can have a cost associated with it). - final boolean powerSave = mRestrictPower && !mRestrictBackground; - // First, generate identities of all connected networks so we can // quickly compare them against all defined policies below. final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length); @@ -1193,9 +1190,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final String baseIface = state.linkProperties.getInterfaceName(); if (baseIface != null) { connIdents.add(Pair.create(baseIface, ident)); - if (powerSave) { - connIfaces.add(baseIface); - } } // Stacked interfaces are considered to have same identity as @@ -1205,9 +1199,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final String stackedIface = stackedLink.getInterfaceName(); if (stackedIface != null) { connIdents.add(Pair.create(stackedIface, ident)); - if (powerSave) { - connIfaces.add(stackedIface); - } } } } @@ -1286,9 +1277,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { removeInterfaceQuota(iface); setInterfaceQuota(iface, quotaBytes); newMeteredIfaces.add(iface); - if (powerSave) { - connIfaces.remove(iface); - } } } @@ -2299,9 +2287,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // state changed, push updated rules mUidState.put(uid, uidState); updateRulesForUidStateChangeLocked(uid, oldUidState, uidState); - if (mDeviceIdleMode && isProcStateAllowedWhileIdle(oldUidState) - != isProcStateAllowedWhileIdle(uidState)) { - updateRuleForDeviceIdleLocked(uid); + if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState) + != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) { + if (mDeviceIdleMode) { + updateRuleForDeviceIdleLocked(uid); + } + if (mRestrictPower) { + updateRulesForRestrictPowerLocked(uid); + } } } } @@ -2317,6 +2310,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mDeviceIdleMode) { updateRuleForDeviceIdleLocked(uid); } + if (mRestrictPower) { + updateRulesForRestrictPowerLocked(uid); + } } } } @@ -2354,15 +2350,36 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - static boolean isProcStateAllowedWhileIdle(int procState) { + static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) { return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; } + void updateRulesForRestrictPowerLocked() { + updateRulesForWhitelistedPowerSaveLocked(mRestrictPower, FIREWALL_CHAIN_POWERSAVE, + mUidFirewallPowerSaveRules); + } + + void updateRulesForRestrictPowerLocked(int uid) { + updateRulesForWhitelistedPowerSaveLocked(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE); + } + void updateRulesForDeviceIdleLocked() { - if (mDeviceIdleMode) { - // sync the whitelists before enable dozable chain. We don't care about the rules if + updateRulesForWhitelistedPowerSaveLocked(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE, + mUidFirewallDozableRules); + } + + void updateRuleForDeviceIdleLocked(int uid) { + updateRulesForWhitelistedPowerSaveLocked(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE); + } + + // NOTE: since both fw_dozable and fw_powersave uses the same map (mPowerSaveTempWhitelistAppIds) + // for whitelisting, we can reuse their logic in this method. + private void updateRulesForWhitelistedPowerSaveLocked(boolean enabled, int chain, + SparseIntArray rules) { + if (enabled) { + // Sync the whitelists before enabling the chain. We don't care about the rules if // we are disabling the chain. - final SparseIntArray uidRules = mUidFirewallDozableRules; + final SparseIntArray uidRules = rules; uidRules.clear(); final List<UserInfo> users = mUserManager.getUsers(); for (int ui = users.size() - 1; ui >= 0; ui--) { @@ -2381,24 +2398,26 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } for (int i = mUidState.size() - 1; i >= 0; i--) { - if (isProcStateAllowedWhileIdle(mUidState.valueAt(i))) { + if (isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.valueAt(i))) { uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW); } } - setUidFirewallRules(FIREWALL_CHAIN_DOZABLE, uidRules); + setUidFirewallRules(chain, uidRules); } - enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode); + enableFirewallChainLocked(chain, enabled); } - void updateRuleForDeviceIdleLocked(int uid) { - if (mDeviceIdleMode) { + // NOTE: since both fw_dozable and fw_powersave uses the same map (mPowerSaveTempWhitelistAppIds) + // for whitelisting, we can reuse their logic in this method. + private void updateRulesForWhitelistedPowerSaveLocked(int uid, boolean enabled, int chain) { + if (enabled) { int appId = UserHandle.getAppId(uid); if (mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId) - || isProcStateAllowedWhileIdle(mUidState.get(uid))) { - setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_ALLOW); + || isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid))) { + setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW); } else { - setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT); + setUidFirewallRule(chain, uid, FIREWALL_RULE_DEFAULT); } } @@ -2458,6 +2477,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForDeviceIdleLocked(); updateRulesForAppIdleLocked(); + updateRulesForRestrictPowerLocked(); // update rules for all installed applications final List<UserInfo> users = mUserManager.getUsers(); @@ -2491,6 +2511,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { int uid = UserHandle.getUid(user.id, appId); updateRuleForAppIdleLocked(uid); updateRuleForDeviceIdleLocked(uid); + updateRulesForRestrictPowerLocked(uid); } } } @@ -2583,6 +2604,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { uidRules = RULE_REJECT_ALL; } + // Check powersave state, which is whitelist + if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE) + && mUidFirewallPowerSaveRules.get(uid, FIREWALL_RULE_DEFAULT) != FIREWALL_RULE_ALLOW) { + uidRules = RULE_REJECT_ALL; + } + // Check standby state, which is blacklist if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY) && mUidFirewallStandbyRules.get(uid, FIREWALL_RULE_DEFAULT) == FIREWALL_RULE_DENY) { @@ -2810,6 +2837,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallDozableRules.put(uid, rule); } else if (chain == FIREWALL_CHAIN_STANDBY) { mUidFirewallStandbyRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_POWERSAVE) { + mUidFirewallPowerSaveRules.put(uid, rule); } try { diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java index 5e4703d7d34b..7dff2c1bdd23 100644 --- a/services/core/java/com/android/server/notification/NotificationComparator.java +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -28,10 +28,11 @@ public class NotificationComparator final int leftImportance = left.getImportance(); final int rightImportance = right.getImportance(); if (leftImportance != rightImportance) { - // by priority, high to low + // by importance, high to low return -1 * Integer.compare(leftImportance, rightImportance); } + // Whether or not the notification can bypass DND. final int leftPackagePriority = left.getPackagePriority(); final int rightPackagePriority = right.getPackagePriority(); if (leftPackagePriority != rightPackagePriority) { @@ -39,6 +40,13 @@ public class NotificationComparator return -1 * Integer.compare(leftPackagePriority, rightPackagePriority); } + final int leftPriority = left.sbn.getNotification().priority; + final int rightPriority = right.sbn.getNotification().priority; + if (leftPriority != rightPriority) { + // by priority, high to low + return -1 * Integer.compare(leftPriority, rightPriority); + } + final float leftPeople = left.getContactAffinity(); final float rightPeople = right.getContactAffinity(); if (leftPeople != rightPeople) { diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java index b57cc75ebf13..bcdeb666987f 100644 --- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java @@ -23,7 +23,7 @@ import android.util.Log; import android.util.Slog; /** - * This {@link com.android.server.notification.NotificationSignalExtractor} noticies noisy + * This {@link com.android.server.notification.NotificationSignalExtractor} notices noisy * notifications and marks them to get a temporary ranking bump. */ public class NotificationIntrusivenessExtractor implements NotificationSignalExtractor { @@ -44,9 +44,15 @@ public class NotificationIntrusivenessExtractor implements NotificationSignalExt return null; } - final Notification notification = record.getNotification(); - if (record.getImportance() > NotificationListenerService.Ranking.IMPORTANCE_DEFAULT) { - record.setRecentlyIntrusive(true); + if (record.getImportance() >= NotificationListenerService.Ranking.IMPORTANCE_DEFAULT) { + final Notification notification = record.getNotification(); + if ((notification.defaults & Notification.DEFAULT_VIBRATE) != 0 || + notification.vibrate != null || + (notification.defaults & Notification.DEFAULT_SOUND) != 0 || + notification.sound != null || + notification.fullScreenIntent != null) { + record.setRecentlyIntrusive(true); + } } return new RankingReconsideration(record.getKey(), HANG_TIME_MS) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e8d27dbb80f3..385557981376 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1320,7 +1320,7 @@ public class NotificationManagerService extends SystemService { @Override public int getImportance(String pkg, int uid) { - checkCallerIsSystem(); + enforceSystemOrSystemUI("Caller not system or systemui"); return mRankingHelper.getImportance(pkg, uid); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 1476e6ea8904..93dcc72c0ae8 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -203,6 +203,11 @@ public final class Installer extends SystemService { mInstaller.execute("linkfile", relativePath, fromBase, toBase); } + public void moveAb(String apkPath, String instructionSet, String outputPath) + throws InstallerException { + mInstaller.execute("move_ab", apkPath, instructionSet, outputPath); + } + private static void assertValidInstructionSet(String instructionSet) throws InstallerException { for (String abi : Build.SUPPORTED_ABIS) { diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 3d2a3555ecd9..67aeed116df5 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -16,36 +16,28 @@ package com.android.server.pm; -import android.app.AppGlobals; +import static com.android.server.pm.Installer.DEXOPT_OTA; +import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; +import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; + import android.content.Context; -import android.content.Intent; import android.content.pm.IOtaDexopt; import android.content.pm.PackageParser; import android.content.pm.PackageParser.Package; -import android.content.pm.ResolveInfo; import android.os.Environment; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; -import android.os.UserHandle; import android.os.storage.StorageManager; -import android.util.ArraySet; import android.util.Log; +import android.util.Slog; -import dalvik.system.DexFile; +import com.android.internal.os.InstallerConnection.InstallerException; import java.io.File; import java.io.FileDescriptor; -import java.util.ArrayList; import java.util.Collection; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; -import java.util.Set; - -import static com.android.server.pm.Installer.DEXOPT_OTA; /** * A service for A/B OTA dexopting. @@ -70,6 +62,9 @@ public class OtaDexoptService extends IOtaDexopt.Stub { // Use the package manager install and install lock here for the OTA dex optimizer. mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller, packageManagerService.mInstallLock, context); + + // Now it's time to check whether we need to move any A/B artifacts. + moveAbArtifacts(packageManagerService.mInstaller); } public static OtaDexoptService main(Context context, @@ -150,20 +145,50 @@ public class OtaDexoptService extends IOtaDexopt.Stub { false /* extractOnly */); } - private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) { - List<ResolveInfo> ris = null; - try { - ris = AppGlobals.getPackageManager().queryIntentReceivers( - intent, null, 0, userId); - } catch (RemoteException e) { + private void moveAbArtifacts(Installer installer) { + if (mDexoptPackages != null) { + throw new IllegalStateException("Should not be ota-dexopting when trying to move."); } - ArraySet<String> pkgNames = new ArraySet<String>(ris == null ? 0 : ris.size()); - if (ris != null) { - for (ResolveInfo ri : ris) { - pkgNames.add(ri.activityInfo.packageName); + + // Look into all packages. + Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages(); + for (PackageParser.Package pkg : pkgs) { + if (pkg == null) { + continue; + } + + // Does the package have code? If not, there won't be any artifacts. + if (!PackageDexOptimizer.canOptimizePackage(pkg)) { + continue; + } + if (pkg.codePath == null) { + Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath"); + continue; + } + + // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into + // /data/ota and moved into the dalvik-cache already. + if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) { + continue; + } + + final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo); + final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly(); + final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); + for (String dexCodeInstructionSet : dexCodeInstructionSets) { + for (String path : paths) { + String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)). + getAbsolutePath(); + + // TODO: Check first whether there is an artifact, to save the roundtrip time. + + try { + installer.moveAb(path, dexCodeInstructionSet, oatDir); + } catch (InstallerException e) { + } + } } } - return pkgNames; } private static class OTADexoptPackageDexOptimizer extends diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 86f2f978f9ee..5562e76c024e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -360,6 +360,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final int SCAN_MOVE = 1<<13; static final int SCAN_INITIAL = 1<<14; static final int SCAN_CHECK_ONLY = 1<<15; + static final int SCAN_DONT_KILL_APP = 1<<17; static final int REMOVE_CHATTY = 1<<16; @@ -499,6 +500,9 @@ public class PackageManagerService extends IPackageManager.Stub { final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<String, PackageParser.Package>(); + final ArrayMap<String, Set<String>> mKnownCodebase = + new ArrayMap<String, Set<String>>(); + // Tracks available target package names -> overlay package paths. final ArrayMap<String, ArrayMap<String, PackageParser.Package>> mOverlays = new ArrayMap<String, ArrayMap<String, PackageParser.Package>>(); @@ -1425,19 +1429,21 @@ public class PackageManagerService extends IPackageManager.Stub { final boolean grantPermissions = (args.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0; + final boolean killApp = (args.installFlags + & PackageManager.INSTALL_DONT_KILL_APP) == 0; final String[] grantedPermissions = args.installGrantPermissions; // Handle the parent package - handlePackagePostInstall(parentRes, grantPermissions, grantedPermissions, - args.observer); + handlePackagePostInstall(parentRes, grantPermissions, killApp, + grantedPermissions, args.observer); // Handle the child packages final int childCount = (parentRes.addedChildPackages != null) ? parentRes.addedChildPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i); - handlePackagePostInstall(childRes, grantPermissions, grantedPermissions, - args.observer); + handlePackagePostInstall(childRes, grantPermissions, killApp, + grantedPermissions, args.observer); } // Log tracing if needed @@ -1632,11 +1638,12 @@ public class PackageManagerService extends IPackageManager.Stub { } private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, - String[] grantedPermissions, IPackageInstallObserver2 installObserver) { + boolean killApp, String[] grantedPermissions, + IPackageInstallObserver2 installObserver) { if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { // Send the removed broadcasts if (res.removedInfo != null) { - res.removedInfo.sendPackageRemovedBroadcasts(); + res.removedInfo.sendPackageRemovedBroadcasts(killApp); } // Now that we successfully installed the package, grant runtime @@ -7899,13 +7906,17 @@ public class PackageManagerService extends IPackageManager.Stub { // Request the ActivityManager to kill the process(only for existing packages) // so that we do not end up in a confused state while the user is still using the older // version of the application while the new one gets installed. - if ((scanFlags & SCAN_REPLACING) != 0) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "killApplication"); + final boolean isReplacing = (scanFlags & SCAN_REPLACING) != 0; + final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0; + if (killApp) { + if (isReplacing) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "killApplication"); - killApplication(pkg.applicationInfo.packageName, - pkg.applicationInfo.uid, "replace pkg"); + killApplication(pkg.applicationInfo.packageName, + pkg.applicationInfo.uid, "replace pkg"); - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } } // Also need to kill any apps that are dependent on the library. @@ -10594,7 +10605,7 @@ public class PackageManagerService extends IPackageManager.Stub { info.removedPackage = packageName; info.removedUsers = new int[] {userId}; info.uid = UserHandle.getUid(userId, pkgSetting.appId); - info.sendPackageRemovedBroadcasts(); + info.sendPackageRemovedBroadcasts(true /*killApp*/); } private void sendPackagesSuspendedForUser(String[] pkgList, int userId, boolean suspended) { @@ -13077,6 +13088,15 @@ public class PackageManagerService extends IPackageManager.Stub { } } + public List<String> getPreviousCodePaths(String packageName) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + final List<String> result = new ArrayList<String>(); + if (ps != null && ps.oldCodePaths != null) { + result.addAll(ps.oldCodePaths); + } + return result; + } + private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user, int[] allUsers, String installerPackageName, PackageInstalledInfo res) { @@ -13086,12 +13106,16 @@ public class PackageManagerService extends IPackageManager.Stub { String pkgName = deletedPackage.packageName; boolean deletedPkg = true; boolean addedPkg = false; + boolean updatedSettings = false; + final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0; + final int deleteFlags = PackageManager.DELETE_KEEP_DATA + | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP); final long origUpdateTime = (pkg.mExtras != null) ? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0; // First delete the existing package while retaining the data directory - if (!deletePackageLI(pkgName, null, true, allUsers, PackageManager.DELETE_KEEP_DATA, + if (!deletePackageLI(pkgName, null, true, allUsers, deleteFlags, res.removedInfo, true, pkg)) { // If the existing package wasn't successfully deleted res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI"); @@ -13117,6 +13141,27 @@ public class PackageManagerService extends IPackageManager.Stub { final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user); updateSettingsLI(newPackage, installerPackageName, allUsers, res, user); + + // Update the in-memory copy of the previous code paths. + PackageSetting ps = mSettings.mPackages.get(pkgName); + if (!killApp) { + if (ps.oldCodePaths == null) { + ps.oldCodePaths = new ArraySet<>(); + } + Collections.addAll(ps.oldCodePaths, deletedPackage.baseCodePath); + if (deletedPackage.splitCodePaths != null) { + Collections.addAll(ps.oldCodePaths, deletedPackage.splitCodePaths); + } + } else { + ps.oldCodePaths = null; + } + if (ps.childPackageNames != null) { + for (int i = ps.childPackageNames.size() - 1; i >= 0; --i) { + final String childPkgName = ps.childPackageNames.get(i); + final PackageSetting childPs = mSettings.mPackages.get(childPkgName); + childPs.oldCodePaths = ps.oldCodePaths; + } + } prepareAppDataAfterInstall(newPackage); addedPkg = true; } catch (PackageManagerException e) { @@ -13129,7 +13174,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Revert all internal state mutations and added folders for the failed install if (addedPkg) { - deletePackageLI(pkgName, null, true, allUsers, PackageManager.DELETE_KEEP_DATA, + deletePackageLI(pkgName, null, true, allUsers, deleteFlags, res.removedInfo, true, null); } @@ -13582,6 +13627,9 @@ public class PackageManagerService extends IPackageManager.Stub { // moving a complete application; perform an initial scan on the new install location scanFlags |= SCAN_INITIAL; } + if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { + scanFlags |= SCAN_DONT_KILL_APP; + } // Result object to be returned res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); @@ -14302,7 +14350,8 @@ public class PackageManagerService extends IPackageManager.Stub { } if (res) { - info.sendPackageRemovedBroadcasts(); + final boolean killApp = (flags & PackageManager.INSTALL_DONT_KILL_APP) == 0; + info.sendPackageRemovedBroadcasts(killApp); info.sendSystemPackageUpdatedBroadcasts(); info.sendSystemPackageAppearedBroadcasts(); } @@ -14334,12 +14383,12 @@ public class PackageManagerService extends IPackageManager.Stub { ArrayMap<String, PackageRemovedInfo> removedChildPackages; ArrayMap<String, PackageInstalledInfo> appearedChildPackages; - void sendPackageRemovedBroadcasts() { - sendPackageRemovedBroadcastInternal(); + void sendPackageRemovedBroadcasts(boolean killApp) { + sendPackageRemovedBroadcastInternal(killApp); final int childCount = removedChildPackages != null ? removedChildPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageRemovedInfo childInfo = removedChildPackages.valueAt(i); - childInfo.sendPackageRemovedBroadcastInternal(); + childInfo.sendPackageRemovedBroadcastInternal(killApp); } } @@ -14381,10 +14430,11 @@ public class PackageManagerService extends IPackageManager.Stub { null, 0, removedPackage, null, null); } - private void sendPackageRemovedBroadcastInternal() { + private void sendPackageRemovedBroadcastInternal(boolean killApp) { Bundle extras = new Bundle(2); extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved); + extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp); if (isUpdate || isRemovedPackageSystemUpdate) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } @@ -14875,7 +14925,10 @@ public class PackageManagerService extends IPackageManager.Stub { } else { if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name); // Kill application pre-emptively especially for apps on sd. - killApplication(packageName, ps.appId, "uninstall pkg"); + final boolean killApp = (flags & PackageManager.DELETE_DONT_KILL_APP) == 0; + if (killApp) { + killApplication(packageName, ps.appId, "uninstall pkg"); + } ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, allUserHandles, outInfo, writeSettings, replacingPackage); } @@ -19124,6 +19177,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); return permissionsState.isPermissionReviewRequired(userId); } } + + @Override + public ApplicationInfo getApplicationInfo(String packageName, int userId) { + return PackageManagerService.this.getApplicationInfo(packageName, 0 /*flags*/, userId); + } } @Override @@ -19151,4 +19209,14 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); boolean isHistoricalPackageUsageAvailable() { return mPackageUsage.isHistoricalPackageUsageAvailable(); } + + /** + * Return a <b>copy</b> of the collection of packages known to the package manager. + * @return A copy of the values of mPackages. + */ + Collection<PackageParser.Package> getPackages() { + synchronized (mPackages) { + return new ArrayList<>(mPackages.values()); + } + } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index e5eec7efe59a..143471886a6f 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -30,6 +30,7 @@ import android.util.SparseArray; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Settings base class for pending and resolved classes. @@ -118,7 +119,14 @@ abstract class PackageSettingBase extends SettingBase { * platform will refuse to launch packages in a frozen state. */ boolean frozen = false; - + /** + * Non-persisted value. During an "upgrade without restart", we need the set + * of all previous code paths so we can surgically add the new APKs to the + * active classloader. If at any point an application is upgraded with a + * restart, this field will be cleared since the classloader would be created + * using the full set of code paths when the package's process is started. + */ + Set<String> oldCodePaths; PackageSettingBase origPackage; /** Package name of the app that installed this package */ diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index f9e258d35d8e..ecfbc0a30256 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -375,6 +375,7 @@ class AppWindowToken extends WindowToken { // The application has stopped, so destroy any surfaces which were keeping alive // in case they were still being used. void notifyAppStopped() { + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this); mAppStopped = true; destroySurfaces(); @@ -472,7 +473,7 @@ class AppWindowToken extends WindowToken { winNdx = Math.min(winNdx - 1, allAppWindows.size() - 1)) { WindowState win = allAppWindows.get(winNdx); if (win.mAppDied) { - if (DEBUG_WINDOW_MOVEMENT) { + if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { Slog.w(TAG, "removeAllDeadWindows: " + win); } // Set mDestroying, we don't want any animation or delayed removal here. diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index f0efebed0747..79d3d84cfae2 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -54,7 +54,8 @@ public class BoundsAnimationController { private final AnimateBoundsUser mTarget; private final Rect mFrom; private final Rect mTo; - private final Rect mTmpRect; + private final Rect mTmpRect = new Rect(); + private final Rect mTmpTaskBounds = new Rect(); private final boolean mMoveToFullScreen; // True if this this animation was cancelled and will be replaced the another animation from // the same {@link #AnimateBoundsUser} target. @@ -63,17 +64,40 @@ public class BoundsAnimationController { // {@link #AnimateBoundsUser} target. private final boolean mReplacement; + // Depending on whether we are animating from + // a smaller to a larger size + private final int mFrozenTaskWidth; + private final int mFrozenTaskHeight; + BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen, boolean replacement) { super(); mTarget = target; mFrom = from; mTo = to; - mTmpRect = new Rect(); mMoveToFullScreen = moveToFullScreen; mReplacement = replacement; addUpdateListener(this); addListener(this); + + // If we are animating from smaller to larger, we want to change the task bounds + // to their final size immediately so we can use scaling to make the window + // larger. Likewise if we are going from bigger to smaller, we want to wait until + // the end so we don't have to upscale from the smaller finished size. + if (animatingToLargerSize()) { + mFrozenTaskWidth = mTo.width(); + mFrozenTaskHeight = mTo.height(); + } else { + mFrozenTaskWidth = mFrom.width(); + mFrozenTaskHeight = mFrom.height(); + } + } + + boolean animatingToLargerSize() { + if (mFrom.width() * mFrom.height() > mTo.width() * mTo.height()) { + return false; + } + return true; } @Override @@ -87,7 +111,13 @@ public class BoundsAnimationController { if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds=" + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value + " remains=" + remains); - if (!mTarget.setSize(mTmpRect)) { + + if (remains != 0) { + mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top, + mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight); + } + + if (!mTarget.setPinnedStackSize(mTmpRect, remains != 0 ? mTmpTaskBounds : null)) { // Whoops, the target doesn't feel like animating anymore. Let's immediately finish // any further animation. animation.cancel(); @@ -99,6 +129,10 @@ public class BoundsAnimationController { public void onAnimationStart(Animator animation) { if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget + " mReplacement=" + mReplacement); + if (animatingToLargerSize()) { + mTarget.setPinnedStackSize(mFrom, mTo); + } + if (!mReplacement) { mTarget.onAnimationStart(); } @@ -108,6 +142,7 @@ public class BoundsAnimationController { public void onAnimationEnd(Animator animation) { if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget + " mMoveToFullScreen=" + mMoveToFullScreen + " mWillReplace=" + mWillReplace); + finishAnimation(); if (mMoveToFullScreen && !mWillReplace) { mTarget.moveToFullscreen(); @@ -159,6 +194,12 @@ public class BoundsAnimationController { * from the hierarchy and is not valid anymore. */ boolean setSize(Rect bounds); + /** + * Behaves as setSize, but freezes the bounds of any tasks in the target at taskBounds, + * to allow for more flexibility during resizing. Only + * works for the pinned stack at the moment. + */ + boolean setPinnedStackSize(Rect bounds, Rect taskBounds); void onAnimationStart(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c7b559904209..f097eb242bfa 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; @@ -685,6 +686,10 @@ class Task implements DimLayer.DimLayerUser { && mStack != null && StackId.isTaskResizeableByDockedStack(mStack.mStackId); } + boolean isFloating() { + return StackId.tasksAreFloating(mStack.mStackId); + } + /** * Whether the task should be treated as if it's docked. Returns true if the task * is currently in docked workspace, or it's side-by-side to a docked task. diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 07a6514de5f1..86327f71efa2 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -111,6 +111,17 @@ public class TaskStack implements DimLayer.DimLayerUser, private float mMinimizeAmount; private final int mDockedStackMinimizeThickness; + // If this is true, the task will be down or upscaled + // to perfectly fit the region it would have been cropped + // to. + private boolean mForceScaleToCrop = false; + // By default, movement animations are applied to all + // window movement. If this is true, animations will not + // be applied within this stack. This is useful for example + // if the windows are moving as the result of a stack animation, + // in which case a second window animation would cause jitter. + private boolean mFreezeMovementAnimations = false; + TaskStack(WindowManagerService service, int stackId) { mService = service; mStackId = stackId; @@ -1128,17 +1139,38 @@ public class TaskStack implements DimLayer.DimLayerUser, return true; } + public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) { + synchronized (mService.mWindowMap) { + if (mDisplayContent == null) { + return false; + } + if (mStackId != PINNED_STACK_ID) { + Slog.w(TAG_WM, "Attempt to use pinned stack resize animation helper on" + + "non pinned stack"); + return false; + } + } + try { + mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds); + } catch (RemoteException e) { + // I don't believe you. + } + return true; + } + @Override // AnimatesBounds public void onAnimationStart() { synchronized (mService.mWindowMap) { - setDragResizingLocked(true); + mFreezeMovementAnimations = true; + mForceScaleToCrop = true; } } @Override // AnimatesBounds public void onAnimationEnd() { synchronized (mService.mWindowMap) { - setDragResizingLocked(false); + mFreezeMovementAnimations = false; + mForceScaleToCrop = false; mService.requestTraversal(); } if (mStackId == PINNED_STACK_ID) { @@ -1163,4 +1195,12 @@ public class TaskStack implements DimLayer.DimLayerUser, public void getFullScreenBounds(Rect bounds) { getDisplayContent().getContentRect(bounds); } + + public boolean getFreezeMovementAnimations() { + return mFreezeMovementAnimations; + } + + public boolean getForceScaleToCrop() { + return mForceScaleToCrop; + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index fe215d523338..7c2a8e338557 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2147,12 +2147,15 @@ public class WindowManagerService extends IWindowManager.Stub void removeWindowLocked(WindowState win) { win.mWindowRemovalAllowed = true; + if (DEBUG_ADD_REMOVE) Slog.v(TAG, + "removeWindowLocked: " + win + " callers=" + Debug.getCallers(4)); + final boolean startingWindow = win.mAttrs.type == TYPE_APPLICATION_STARTING; if (startingWindow) { if (DEBUG_STARTING_WINDOW) Slog.d(TAG_WM, "Starting window removed " + win); } - if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v( + if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win == mCurrentFocus) Slog.v( TAG_WM, "Remove " + win + " client=" + Integer.toHexString(System.identityHashCode(win.mClient.asBinder())) + ", surfaceController=" + win.mWinAnimator.mSurfaceController + " Callers=" diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e8f1b5d8d326..4ad021e1c00c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -184,6 +184,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { private Configuration mConfiguration = Configuration.EMPTY; private Configuration mOverrideConfig = Configuration.EMPTY; + // Represents the changes from our override configuration applied + // to the global configuration. This is the only form of configuration + // which is suitable for delivery to the client. + private Configuration mMergedConfiguration = new Configuration(); // Sticky answer to isConfigChanged(), remains true until new Configuration is assigned. // Used only on {@link #TYPE_KEYGUARD}. private boolean mConfigHasChanged; @@ -434,9 +438,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { // If not null, the window that will be used to replace the old one. This is being set when // the window is added and unset when this window reports its first draw. WindowState mReplacingWindow = null; - // Whether this window is being moved via the resize API boolean mMovedByResize; + /** * Wake lock for drawing. * Even though it's slightly more expensive to do so, we will use a separate wake lock @@ -635,7 +639,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { final Task task = getTask(); final boolean fullscreenTask = task == null || task.isFullscreen(); - final boolean freeformWorkspace = task != null && task.inFreeformWorkspace(); + final boolean windowsAreFloating = task != null && task.isFloating(); if (fullscreenTask || (isChildWindow() && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0)) { @@ -661,10 +665,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { mContainingFrame.top -= mContainingFrame.bottom - cf.bottom; } - if (freeformWorkspace) { - // In free form mode we have only to set the rectangle if it wasn't set already. No - // need to intersect it with the (visible) "content frame" since it is allowed to - // be outside the visible desktop. + if (windowsAreFloating) { + // In floating modes (e.g. freeform, pinned) we have only to set the rectangle + // if it wasn't set already. No need to intersect it with the (visible) + // "content frame" since it is allowed to be outside the visible desktop. if (mContainingFrame.isEmpty()) { mContainingFrame.set(cf); } @@ -720,7 +724,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Make sure the content and visible frames are inside of the // final window frame. - if (freeformWorkspace && !mFrame.isEmpty()) { + if (windowsAreFloating && !mFrame.isEmpty()) { // Keep the frame out of the blocked system area, limit it in size to the content area // and make sure that there is always a minimum visible so that the user can drag it // into a usable area.. @@ -772,9 +776,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { Math.min(mStableFrame.bottom, frame.bottom)); } - if (!inFreeformWorkspace()) { - // Freeform windows can be positioned outside of the display frame, but that is not a - // reason to provide them with overscan insets. + if (!windowsAreFloating) { + // Windows from floating tasks (e.g. freeform, pinned) may be positioned outside + // of the display frame, but that is not a reason to provide them with overscan insets. mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0), Math.max(mOverscanFrame.top - frame.top, 0), Math.max(frame.right - mOverscanFrame.right, 0), @@ -1355,6 +1359,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { mConfiguration = newConfig; mOverrideConfig = newOverrideConfig; mConfigHasChanged = false; + + mMergedConfiguration.setTo(newConfig); + if (newOverrideConfig != null && newOverrideConfig != Configuration.EMPTY) { + mMergedConfiguration.updateFrom(newOverrideConfig); + } } void setHasSurface(boolean hasSurface) { @@ -1616,9 +1625,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { mTurnOnScreen = true; } if (isConfigChanged()) { + final Configuration newConfig = updateConfiguration(); if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + " visible with new config: " - + mService.mCurConfiguration); - outConfig.setTo(mService.mCurConfiguration); + + newConfig); + outConfig.setTo(newConfig); } } @@ -2061,21 +2071,30 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + /** + * Update our current configurations, based on task configuration. + * + * @return A configuration suitable for sending to the client. + */ + private Configuration updateConfiguration() { + final Task task = getTask(); + final Configuration overrideConfig = + (task != null) ? task.mOverrideConfig : Configuration.EMPTY; + final boolean configChanged = isConfigChanged(); + if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) { + Slog.i(TAG, "Sending new config to window " + this + ": " + + " / config=" + mService.mCurConfiguration + " overrideConfig=" + overrideConfig); + } + setConfiguration(mService.mCurConfiguration, overrideConfig); + return mMergedConfiguration; + } + void reportResized() { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag()); try { if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this + ": " + mCompatFrame); - final boolean configChanged = isConfigChanged(); - final Task task = getTask(); - final Configuration overrideConfig = - (task != null) ? task.mOverrideConfig : Configuration.EMPTY; - if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) { - Slog.i(TAG, "Sending new config to window " + this + ": " - + " / config=" - + mService.mCurConfiguration + " overrideConfig=" + overrideConfig); - } - setConfiguration(mService.mCurConfiguration, overrideConfig); + final Configuration newConfig = isConfigChanged() ? updateConfiguration() : null; if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING"); @@ -2086,7 +2105,6 @@ final class WindowState implements WindowManagerPolicy.WindowState { final Rect stableInsets = mLastStableInsets; final Rect outsets = mLastOutsets; final boolean reportDraw = mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING; - final Configuration newConfig = configChanged ? mConfiguration : null; if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING && mClient instanceof IWindow.Stub) { // To prevent deadlock simulate one-way call if win.mClient is a local object. @@ -2485,7 +2503,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { final int ph = mContainingFrame.height(); final Task task = getTask(); final boolean nonFullscreenTask = task != null && !task.isFullscreen(); - + final boolean fitToDisplay = task != null && + !task.isFloating(); float x, y; int w,h; @@ -2542,7 +2561,17 @@ final class WindowState implements WindowManagerPolicy.WindowState { (int) (y + mAttrs.verticalMargin * ph), mFrame); // Now make sure the window fits in the overall display frame. - Gravity.applyDisplay(mAttrs.gravity, mDisplayFrame, mFrame); + if (fitToDisplay) { + Gravity.applyDisplay(mAttrs.gravity, mDisplayFrame, mFrame); + } + + // We need to make sure we update the CompatFrame as it is used for + // cropping decisions, etc, on systems where we lack a decor layer. + mCompatFrame.set(mFrame); + if (mEnforceSizeCompat) { + // See comparable block in computeFrameLw. + mCompatFrame.scale(mInvGlobalScale); + } } boolean isChildWindow() { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 02f9aa13c939..c1ff96e72ae5 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; import static android.view.WindowManager.LayoutParams.FLAG_SCALED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; @@ -467,9 +468,8 @@ class WindowStateAnimator { return; } - if (WindowManagerService.localLOGV) Slog.v( - TAG, "Exit animation finished in " + this - + ": remove=" + mWin.mRemoveOnExit); + if (WindowManagerService.localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Exit animation finished in " + this + ": remove=" + mWin.mRemoveOnExit); mWin.mDestroying = true; @@ -1115,11 +1115,11 @@ class WindowStateAnimator { } } - void updateSurfaceWindowCrop(final boolean recoveringMemory) { + Rect calculateSurfaceWindowCrop() { final WindowState w = mWin; final DisplayContent displayContent = w.getDisplayContent(); if (displayContent == null) { - return; + return null; } final DisplayInfo displayInfo = displayContent.getDisplayInfo(); if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Updating crop for window: " + w + ", " + "mLastCrop=" + @@ -1187,7 +1187,7 @@ class WindowStateAnimator { clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top); adjustCropToStackBounds(w, clipRect, isFreeformResizing); - if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + mClipRect); + if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + clipRect); w.transformFromScreenToSurfaceSpace(clipRect); @@ -1196,6 +1196,10 @@ class WindowStateAnimator { clipRect.setEmpty(); } + return clipRect; + } + + void updateSurfaceWindowCrop(Rect clipRect, boolean recoveringMemory) { if (!clipRect.equals(mLastClipRect)) { mLastClipRect.set(clipRect); mSurfaceController.setCropInTransaction(clipRect, recoveringMemory); @@ -1237,6 +1241,7 @@ class WindowStateAnimator { w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left; final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() : w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top; + // We need to do some acrobatics with surface position, because their clip region is // relative to the inside of the surface, but the stack bounds aren't. clipRect.left = Math.max(0, @@ -1251,14 +1256,44 @@ class WindowStateAnimator { void setSurfaceBoundariesLocked(final boolean recoveringMemory) { final WindowState w = mWin; + final Task task = w.getTask(); mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0); calculateSurfaceBounds(w, w.getAttrs()); - mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, recoveringMemory); + float extraHScale = (float) 1.0; + float extraVScale = (float) 1.0; + + final Rect crop = calculateSurfaceWindowCrop(); + if (task != null && task.mStack.getForceScaleToCrop()) { + extraHScale = crop.width() / (float)mTmpSize.width(); + extraVScale = crop.height() / (float)mTmpSize.height(); + + // In the case of ForceScaleToCrop we scale entire tasks together, + // and so we need to scale our offsets relative to the task bounds + // or parent and child windows would fall out of alignment. + int posX = (int) (mTmpSize.left - w.mAttrs.x * (1 - extraHScale)); + int posY = (int) (mTmpSize.top - w.mAttrs.y * (1 - extraVScale)); + posX += w.getAttrs().surfaceInsets.left * (1 - extraHScale); + posY += w.getAttrs().surfaceInsets.top * (1 - extraVScale); + mSurfaceController.setPositionInTransaction(posX, posY, recoveringMemory); + + // Since we are scaled to fit in our previously desired crop, we can now + // expose the whole window in buffer space, and not risk extending + // past where the system would have cropped us + crop.set(0, 0, mTmpSize.width(), mTmpSize.height()); + updateSurfaceWindowCrop(crop, recoveringMemory); + } else { + mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, + recoveringMemory); + updateSurfaceWindowCrop(crop, recoveringMemory); + } + - mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale, mDtDx * w.mVScale, - mDsDy * w.mHScale, mDtDy * w.mVScale, recoveringMemory); + mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * extraHScale, + mDtDx * w.mVScale * extraVScale, + mDsDy * w.mHScale * extraHScale, + mDtDy * w.mVScale * extraVScale, recoveringMemory); mSurfaceResized = mSurfaceController.setSizeInTransaction( mTmpSize.width(), mTmpSize.height(), recoveringMemory); @@ -1270,7 +1305,6 @@ class WindowStateAnimator { w.applyDimLayerIfNeeded(); } - updateSurfaceWindowCrop(recoveringMemory); } void prepareSurfaceLocked(final boolean recoveringMemory) { @@ -1409,7 +1443,7 @@ class WindowStateAnimator { SurfaceControl.openTransaction(); mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left, mWin.mFrame.top + top, false); - updateSurfaceWindowCrop(false); + updateSurfaceWindowCrop(calculateSurfaceWindowCrop(), false); } catch (RuntimeException e) { Slog.w(TAG, "Error positioning surface of " + mWin + " pos=(" + left + "," + top + ")", e); diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 2cdf471c1420..fb07512f16e2 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -138,7 +138,7 @@ class WindowSurfaceController { void destroyInTransaction() { // if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { - Slog.i(TAG, "Destroying surface " + this + " called by " + Debug.getCallers(4)); + Slog.i(TAG, "Destroying surface " + this + " called by " + Debug.getCallers(8)); // } try { if (mSurfaceControl != null) { diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 856d30ab7cf1..e3955fe00f39 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -651,6 +651,7 @@ class WindowSurfacePlacer { for (int i = windows.size() - 1; i >= 0; i--) { WindowState w = windows.get(i); + final Task task = w.getTask(); final boolean obscuredChanged = w.mObscured != mObscured; // Update effect. @@ -683,7 +684,8 @@ class WindowSurfacePlacer { final boolean adjustedForMinimizedDockedStack = w.getTask() != null && w.getTask().mStack.isAdjustedForMinimizedDockedStack(); if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0 - && !w.isDragResizing() && !adjustedForMinimizedDockedStack) { + && !w.isDragResizing() && !adjustedForMinimizedDockedStack + && (task == null || !w.getTask().mStack.getFreezeMovementAnimations())) { winAnimator.setMoveAnimation(left, top); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 9aa2b9452b87..b8c31e3f063e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -431,6 +431,24 @@ public final class SystemServer { mPackageManager = mSystemContext.getPackageManager(); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename + // A/B artifacts after boot, before anything else might touch/need them. + // Note: this isn't needed during decryption (we don't have /data anyways). + if (!mOnlyCore) { + boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt", + false); + if (!disableOtaDexopt) { + traceBeginAndSlog("StartOtaDexOptService"); + try { + OtaDexoptService.main(mSystemContext, mPackageManagerService); + } catch (Throwable e) { + reportWtf("starting OtaDexOptService", e); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } + } + traceBeginAndSlog("StartUserManagerService"); ServiceManager.addService(Context.USER_SERVICE, UserManagerService.getInstance()); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); @@ -1124,19 +1142,6 @@ public final class SystemServer { reportWtf("starting BackgroundDexOptService", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); - - // Manages A/B OTA dexopting. - boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt", - false); - if (!disableOtaDexopt) { - traceBeginAndSlog("StartOtaDexOptService"); - try { - OtaDexoptService.main(mSystemContext, mPackageManagerService); - } catch (Throwable e) { - reportWtf("starting BackgroundDexOptService", e); - } - Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); - } } mSystemServiceManager.startService(LauncherAppsService.class); diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java index 22d15e148ba4..eef823565dd4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java @@ -16,7 +16,6 @@ package android.graphics.drawable; -import com.android.internal.R; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; @@ -136,7 +135,21 @@ public class VectorDrawable_Delegate { VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr); + Canvas_Delegate.native_save(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG); + Canvas_Delegate.native_translate(canvasWrapperPtr, bounds.left, bounds.top); + + if (needsMirroring) { + Canvas_Delegate.native_translate(canvasWrapperPtr, bounds.width(), 0); + Canvas_Delegate.native_scale(canvasWrapperPtr, -1.0f, 1.0f); + } + + // At this point, canvas has been translated to the right position. + // And we use this bound for the destination rect for the drawBitmap, so + // we offset to (0, 0); + bounds.offsetTo(0, 0); nativePathRenderer.draw(canvasWrapperPtr, colorFilterPtr, bounds.width(), bounds.height()); + + Canvas_Delegate.native_restore(canvasWrapperPtr, true); } @LayoutlibDelegate @@ -492,28 +505,6 @@ public class VectorDrawable_Delegate { super(copy); } - public void inflate(Resources r, AttributeSet attrs, Theme theme) { - final TypedArray a = obtainAttributes(r, theme, attrs, - R.styleable.VectorDrawableClipPath); - updateStateFromTypedArray(a); - a.recycle(); - } - - private void updateStateFromTypedArray(TypedArray a) { - // Account for any configuration changes. - mChangingConfigurations |= a.getChangingConfigurations(); - - final String pathName = a.getString(R.styleable.VectorDrawableClipPath_name); - if (pathName != null) { - mPathName = pathName; - } - - final String pathData = a.getString(R.styleable.VectorDrawableClipPath_pathData); - if (pathData != null) { - mNodes = PathParser_Delegate.createNodesFromPathData(pathData); - } - } - @Override public boolean isClipPath() { return true; diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index ddd8f437955d..7dc8049595f7 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -1357,6 +1357,7 @@ public class WifiConfiguration implements Parcelable { append(" PROVIDER-NAME: ").append(this.providerFriendlyName). append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN) .append(" PRIO: ").append(this.priority) + .append(" HIDDEN: ").append(this.hiddenSSID) .append('\n'); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index e2dd11121c17..a5bfd3c094bc 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -889,24 +889,6 @@ public class WifiManager { } /** - * Sets whether or not the given network is metered from a network policy - * point of view. A network should be classified as metered when the user is - * sensitive to heavy data usage on that connection due to monetary costs, - * data limitations or battery/performance issues. A typical example would - * be a wifi connection where the user was being charged for usage. - * @param netId the integer that identifies the network configuration - * to the supplicant. - * @param isMetered True to mark the network as metered. - * @return {@code true} if the operation succeeded. - * @hide - */ - @SystemApi - public boolean setMetered(int netId, boolean isMetered) { - // TODO(jjoslin): Implement - return false; - } - - /** * Remove the specified network from the list of configured networks. * This may result in the asynchronous delivery of state change * events. |