diff options
131 files changed, 3173 insertions, 1244 deletions
diff --git a/Android.bp b/Android.bp index abf95a8ebc58..2ccddd25c9de 100644 --- a/Android.bp +++ b/Android.bp @@ -73,6 +73,7 @@ java_defaults { "core/java/android/app/IInstrumentationWatcher.aidl", "core/java/android/app/INotificationManager.aidl", "core/java/android/app/IProcessObserver.aidl", + "core/java/android/app/IRequestFinishCallback.aidl", "core/java/android/app/ISearchManager.aidl", "core/java/android/app/ISearchManagerCallback.aidl", "core/java/android/app/IServiceConnection.aidl", diff --git a/api/test-current.txt b/api/test-current.txt index 5dc7929d06df..181932cf1260 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -725,7 +725,6 @@ package android.content.pm { field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000 - field public static boolean RESTRICTED_PERMISSIONS_ENABLED; field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services"; field public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared"; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 3a84b79fc681..033dbf7aa4a5 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -41,7 +41,7 @@ import "frameworks/base/core/proto/android/server/location/enums.proto"; import "frameworks/base/core/proto/android/service/procstats_enum.proto"; import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/stats/connectivity/network_stack.proto"; -import "frameworks/base/core/proto/android/stats/connectivity/resolv_stats.proto"; +import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto"; import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; @@ -5109,36 +5109,36 @@ message AppCompacted { * Logs a DNS lookup operation initiated by the system resolver on behalf of an application * invoking native APIs such as getaddrinfo() or Java APIs such as Network#getAllByName(). * - * The top-level message represents the entire lookup operation, which may result one or more - * queries to the recursive DNS resolvers. Those are individually logged in DnsQueryEvent to - * enable computing error rates and network latency and timeouts broken up by query type, - * transport, network interface, etc. + * The NetworkDnsEventReported message represents the entire lookup operation, which may + * result one or more queries to the recursive DNS resolvers. Those are individually logged + * in DnsQueryEvents to enable computing error rates and network latency and timeouts + * broken up by query type, transport, network interface, etc. */ message NetworkDnsEventReported { + optional android.stats.dnsresolver.EventType event_type = 1; - optional android.stats.connectivity.EventType event_type = 1; - - optional android.stats.connectivity.ReturnCode return_code = 2; + optional android.stats.dnsresolver.ReturnCode return_code = 2; // The latency in microseconds of the entire DNS lookup operation. optional int32 latency_micros = 3; - optional android.stats.connectivity.DnsQueryEventRe dns_query_event_re = 4 [(log_mode) = MODE_BYTES]; + // Only valid for event_type = EVENT_GETADDRINFO. + optional int32 hints_ai_flags = 4; - // ResNSend flags defined in android/multinetwork.h - optional int32 flags = 5; + // Flags passed to android_res_nsend() defined in multinetwork.h + // Only valid for event_type = EVENT_RESNSEND. + optional int32 res_nsend_flags = 5; - optional android.net.NetworkCapabilitiesProto.Transport network_type = 6; + optional android.stats.dnsresolver.Transport network_type = 6; // The DNS over TLS mode on a specific netId. - optional android.stats.connectivity.PrivateDnsModes private_dns_modes = 7; + optional android.stats.dnsresolver.PrivateDnsModes private_dns_modes = 7; // Additional pass-through fields opaque to statsd. // The DNS resolver Mainline module can add new fields here without requiring an OS update. - optional android.stats.connectivity.DnsCallEvent dns_call_event = 8 [(log_mode) = MODE_BYTES]; + optional android.stats.dnsresolver.DnsQueryEvents dns_query_events = 8 [(log_mode) = MODE_BYTES]; } - /** * Logs when a data stall event occurs. * @@ -6131,6 +6131,15 @@ message GpuStatsGlobalInfo { // Total count of the Vulkan driver fails to be loaded. optional int64 vk_loading_failure_count = 8; + + // Api version of the system Vulkan driver. + optional int32 vulkan_version = 9; + + // Api version of the system CPU Vulkan driver. + optional int32 cpu_vulkan_version = 10; + + // Api version of the system GLES driver. + optional int32 gles_version = 11; } /** diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp index 3fa932fddd04..876383c16863 100644 --- a/cmds/statsd/src/external/GpuStatsPuller.cpp +++ b/cmds/statsd/src/external/GpuStatsPuller.cpp @@ -65,6 +65,9 @@ static bool pullGpuStatsGlobalInfo(const sp<IGpuService>& gpuService, if (!event->write((int64_t)info.glLoadingFailureCount)) return false; if (!event->write((int64_t)info.vkLoadingCount)) return false; if (!event->write((int64_t)info.vkLoadingFailureCount)) return false; + if (!event->write(info.vulkanVersion)) return false; + if (!event->write(info.cpuVulkanVersion)) return false; + if (!event->write(info.glesVersion)) return false; event->init(); data->emplace_back(event); } diff --git a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp index 8625487d1aca..2acfb8354905 100644 --- a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp +++ b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp @@ -48,7 +48,10 @@ static const int64_t GL_DRIVER_LOADING_TIME_1 = 666; static const int64_t VK_DRIVER_LOADING_TIME_0 = 777; static const int64_t VK_DRIVER_LOADING_TIME_1 = 888; static const int64_t VK_DRIVER_LOADING_TIME_2 = 999; -static const size_t NUMBER_OF_VALUES_GLOBAL = 8; +static const int32_t VULKAN_VERSION = 1; +static const int32_t CPU_VULKAN_VERSION = 2; +static const int32_t GLES_VERSION = 3; +static const size_t NUMBER_OF_VALUES_GLOBAL = 11; static const size_t NUMBER_OF_VALUES_APP = 4; // clang-format on @@ -93,6 +96,9 @@ TEST_F(GpuStatsPuller_test, PullGpuStatsGlobalInfo) { EXPECT_TRUE(event->write(GL_LOADING_FAILURE_COUNT)); EXPECT_TRUE(event->write(VK_LOADING_COUNT)); EXPECT_TRUE(event->write(VK_LOADING_FAILURE_COUNT)); + EXPECT_TRUE(event->write(VULKAN_VERSION)); + EXPECT_TRUE(event->write(CPU_VULKAN_VERSION)); + EXPECT_TRUE(event->write(GLES_VERSION)); event->init(); inData.emplace_back(event); MockGpuStatsPuller mockPuller(android::util::GPU_STATS_GLOBAL_INFO, &inData); @@ -110,6 +116,9 @@ TEST_F(GpuStatsPuller_test, PullGpuStatsGlobalInfo) { EXPECT_EQ(GL_LOADING_FAILURE_COUNT, outData[0]->getValues()[5].mValue.long_value); EXPECT_EQ(VK_LOADING_COUNT, outData[0]->getValues()[6].mValue.long_value); EXPECT_EQ(VK_LOADING_FAILURE_COUNT, outData[0]->getValues()[7].mValue.long_value); + EXPECT_EQ(VULKAN_VERSION, outData[0]->getValues()[8].mValue.int_value); + EXPECT_EQ(CPU_VULKAN_VERSION, outData[0]->getValues()[9].mValue.int_value); + EXPECT_EQ(GLES_VERSION, outData[0]->getValues()[10].mValue.int_value); } TEST_F(GpuStatsPuller_test, PullGpuStatsAppInfo) { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2914f6c5abf2..c08ed268833b 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3666,7 +3666,25 @@ public class Activity extends ContextThemeWrapper FragmentManager fragmentManager = mFragments.getFragmentManager(); - if (fragmentManager.isStateSaved() || !fragmentManager.popBackStackImmediate()) { + if (!fragmentManager.isStateSaved() && fragmentManager.popBackStackImmediate()) { + return; + } + if (!isTaskRoot()) { + // If the activity is not the root of the task, allow finish to proceed normally. + finishAfterTransition(); + return; + } + try { + // Inform activity task manager that the activity received a back press + // while at the root of the task. This call allows ActivityTaskManager + // to intercept or defer finishing. + ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken, + new IRequestFinishCallback.Stub() { + public void requestFinish() { + finishAfterTransition(); + } + }); + } catch (RemoteException e) { finishAfterTransition(); } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index a36b167004f8..17368b789645 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -64,7 +64,6 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; -import android.permission.PermissionManager; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Singleton; @@ -3739,7 +3738,6 @@ public class ActivityManager { } // Isolated processes don't get any permissions. if (UserHandle.isIsolated(uid)) { - PermissionManager.addPermissionDenialHint("uid " + uid + " is isolated"); return PackageManager.PERMISSION_DENIED; } // If there is a uid that owns whatever is being accessed, it has @@ -3755,26 +3753,24 @@ public class ActivityManager { Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid, here); */ - PermissionManager.addPermissionDenialHint( - "Target is not exported. owningUid=" + owningUid); return PackageManager.PERMISSION_DENIED; } if (permission == null) { return PackageManager.PERMISSION_GRANTED; } - return checkUidPermission(permission, uid); + try { + return AppGlobals.getPackageManager() + .checkUidPermission(permission, uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @hide */ public static int checkUidPermission(String permission, int uid) { try { - List<String> hints = PermissionManager.getPermissionDenialHints(); - if (hints == null) { - return AppGlobals.getPackageManager().checkUidPermission(permission, uid); - } else { - return AppGlobals.getPackageManager() - .checkUidPermissionWithDenialHintForwarding(permission, uid, hints); - } + return AppGlobals.getPackageManager() + .checkUidPermission(permission, uid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 931e3553c2b6..41a4fba0434c 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -68,7 +68,6 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; -import android.permission.PermissionManager; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -99,7 +98,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -1830,17 +1828,11 @@ class ContextImpl extends Context { } Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold " + permission); - PermissionManager.addPermissionDenialHint("Missing ActivityManager"); return PackageManager.PERMISSION_DENIED; } try { - List<String> hints = PermissionManager.getPermissionDenialHints(); - if (hints == null) { - return am.checkPermission(permission, pid, uid); - } else { - return am.checkPermissionWithDenialHintForwarding(permission, pid, uid, hints); - } + return am.checkPermission(permission, pid, uid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1897,61 +1889,43 @@ class ContextImpl extends Context { String permission, int resultOfCheck, boolean selfToo, int uid, String message) { if (resultOfCheck != PackageManager.PERMISSION_GRANTED) { - List<String> hints = PermissionManager.getPermissionDenialHints(); throw new SecurityException( (message != null ? (message + ": ") : "") + (selfToo ? "Neither user " + uid + " nor current process has " - : "uid " + uid + " does not have ") - + permission + "." - + (hints == null ? "" : " Hints: " + hints)); + : "uid " + uid + " does not have ") + + permission + + "."); } } @Override public void enforcePermission( String permission, int pid, int uid, String message) { - List<String> prev = PermissionManager.collectPermissionDenialHints(this, uid); - try { - enforce(permission, - checkPermission(permission, pid, uid), - false, - uid, - message); - } finally { - PermissionManager.resetPermissionDenialHints(prev); - } + enforce(permission, + checkPermission(permission, pid, uid), + false, + uid, + message); } @Override public void enforceCallingPermission(String permission, String message) { - List<String> prev = PermissionManager.collectPermissionDenialHints(this, - Binder.getCallingUid()); - try { - enforce(permission, - checkCallingPermission(permission), - false, - Binder.getCallingUid(), - message); - } finally { - PermissionManager.resetPermissionDenialHints(prev); - } + enforce(permission, + checkCallingPermission(permission), + false, + Binder.getCallingUid(), + message); } @Override public void enforceCallingOrSelfPermission( String permission, String message) { - List<String> prev = PermissionManager.collectPermissionDenialHints(this, - Binder.getCallingUid()); - try { - enforce(permission, - checkCallingOrSelfPermission(permission), - true, - Binder.getCallingUid(), - message); - } finally { - PermissionManager.resetPermissionDenialHints(prev); - } + enforce(permission, + checkCallingOrSelfPermission(permission), + true, + Binder.getCallingUid(), + message); } @Override diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index f82536f65ddb..48ca71690a1b 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -194,7 +194,6 @@ interface IActivityManager { int getProcessLimit(); @UnsupportedAppUsage int checkPermission(in String permission, int pid, int uid); - int checkPermissionWithDenialHintForwarding(in String permission, int pid, int uid, inout List<String> permissionDenialHints); int checkUriPermission(in Uri uri, int pid, int uid, int mode, int userId, in IBinder callerToken); void grantUriPermission(in IApplicationThread caller, in String targetPkg, in Uri uri, diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 7953d42514fc..26720fca4df8 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -26,6 +26,7 @@ import android.app.IAppTask; import android.app.IAssistDataReceiver; import android.app.IInstrumentationWatcher; import android.app.IProcessObserver; +import android.app.IRequestFinishCallback; import android.app.IServiceConnection; import android.app.IStopUserCallback; import android.app.ITaskStackListener; @@ -484,4 +485,12 @@ interface IActivityTaskManager { * @param activityToken The token of the target activity to restart. */ void restartActivityProcessIfVisible(in IBinder activityToken); + + /** + * Reports that an Activity received a back key press when there were no additional activities + * on the back stack. If the Activity should be finished, the callback will be invoked. A + * callback is used instead of finishing the activity directly from the server such that the + * client may perform actions prior to finishing. + */ + void onBackPressedOnTaskRoot(in IBinder activityToken, in IRequestFinishCallback callback); } diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/core/java/android/app/IRequestFinishCallback.aidl new file mode 100644 index 000000000000..3270565727d9 --- /dev/null +++ b/core/java/android/app/IRequestFinishCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +/** + * This callback allows ActivityTaskManager to ask the calling Activity + * to finish in response to a call to onBackPressedOnTaskRoot. + * + * {@hide} + */ +oneway interface IRequestFinishCallback { + void requestFinish(); +} diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 841ff6a3f12e..1fdc8ca5b291 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -161,4 +161,12 @@ oneway interface ITaskStackListener { * @see com.android.server.wm.AppWindowToken#inSizeCompatMode */ void onSizeCompatModeActivityChanged(int displayId, in IBinder activityToken); + + /** + * Reports that an Activity received a back key press when there were no additional activities + * on the back stack. + * + * @param taskInfo info about the task which received the back press + */ + void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo); } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index a4a97c4cac1d..00f3ad58afa6 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -168,4 +168,9 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) throws RemoteException { } + + @Override + public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) + throws RemoteException { + } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 225eec13d6eb..6ab4657d727d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -108,7 +108,6 @@ interface IPackageManager { @UnsupportedAppUsage int checkPermission(String permName, String pkgName, int userId); - int checkUidPermissionWithDenialHintForwarding(String permName, int uid, inout List<String> permissionDenialHints); int checkUidPermission(String permName, int uid); @UnsupportedAppUsage diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index a8815ec6cfaa..89eabc285e38 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1278,7 +1278,7 @@ public class PackageInstaller { public int mode = MODE_INVALID; /** {@hide} */ @UnsupportedAppUsage - public int installFlags; + public int installFlags = PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; /** {@hide} */ public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; /** {@hide} */ @@ -1513,18 +1513,21 @@ public class PackageInstaller { * state of the permission can be determined only at install time and cannot be * changed on updated or at a later point via the package manager APIs. * + * <p>Initially, all restricted permissions are whitelisted but you can change + * which ones are whitelisted by calling this method or the corresponding ones + * on the {@link PackageManager}. + * * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int) * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int) */ public void setWhitelistedRestrictedPermissions(@Nullable Set<String> permissions) { if (permissions == RESTRICTED_PERMISSIONS_ALL) { installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; - } - if (permissions != null) { - this.whitelistedRestrictedPermissions = new ArrayList<>(permissions); + whitelistedRestrictedPermissions = null; } else { installFlags &= ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; - this.whitelistedRestrictedPermissions = null; + whitelistedRestrictedPermissions = (permissions != null) + ? new ArrayList<>(permissions) : null; } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index dd5ca6706316..40561f02d55f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -86,11 +86,6 @@ public abstract class PackageManager { /** {@hide} */ public static final boolean APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE = true; - /** {@hide} */ - @TestApi - // STOPSHIP: Remove this once we get a Play prebuilt. - public static boolean RESTRICTED_PERMISSIONS_ENABLED = false; - /** * This exception is thrown when a given package, application, or component * name cannot be found. diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 232869d7aefc..7dbc16a56a7b 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -71,6 +71,8 @@ public class GraphicsEnvironment { "android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE"; private static final String INTENT_KEY_A4A_TOAST_MESSAGE = "A4A Toast Message"; private static final String GAME_DRIVER_WHITELIST_ALL = "*"; + private static final int VULKAN_1_0 = 0x00400000; + private static final int VULKAN_1_1 = 0x00401000; // GAME_DRIVER_ALL_APPS // 0: Default (Invalid values fallback to default as well) @@ -99,7 +101,8 @@ public class GraphicsEnvironment { Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver"); if (!chooseDriver(context, coreSettings, pm, packageName)) { setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE, - SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), packageName); + SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), packageName, + getVulkanVersion(pm)); } Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } @@ -200,6 +203,20 @@ public class GraphicsEnvironment { return true; } + private static int getVulkanVersion(PackageManager pm) { + // PackageManager doesn't have an API to retrieve the version of a specific feature, and we + // need to avoid retrieving all system features here and looping through them. + if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_1)) { + return VULKAN_1_1; + } + + if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_0)) { + return VULKAN_1_0; + } + + return 0; + } + /** * Check whether application is debuggable */ @@ -791,7 +808,7 @@ public class GraphicsEnvironment { // driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456. // Long.parseLong will throw if the meta-data "driver_build_time" is not set properly. setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode, - Long.parseLong(driverBuildTime.substring(1)), packageName); + Long.parseLong(driverBuildTime.substring(1)), packageName, 0); return true; } @@ -815,7 +832,7 @@ public class GraphicsEnvironment { private static native void setDebugLayersGLES(String layers); private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries); private static native void setGpuStats(String driverPackageName, String driverVersionName, - long driverVersionCode, long driverBuildTime, String appPackageName); + long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion); private static native void setAngleInfo(String path, String appPackage, String devOptIn, FileDescriptor rulesFd, long rulesOffset, long rulesLength); private static native boolean getShouldUseAngle(String packageName); diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 55bb3fe1817c..2a41c2065c46 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -19,22 +19,15 @@ package android.permission; import android.Manifest; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.content.ContentResolver; import android.content.Context; import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.os.Build; import android.os.RemoteException; -import android.provider.Settings; -import android.util.Log; import com.android.internal.annotations.Immutable; -import com.android.internal.util.ArrayUtils; import com.android.server.SystemConfig; import java.util.ArrayList; @@ -49,8 +42,6 @@ import java.util.Objects; @SystemApi @SystemService(Context.PERMISSION_SERVICE) public final class PermissionManager { - private static final String LOG_TAG = PermissionManager.class.getSimpleName(); - /** * {@link android.content.pm.PackageParser} needs access without having a {@link Context}. * @@ -63,119 +54,6 @@ public final class PermissionManager { private final IPackageManager mPackageManager; - /** Permission denials added via {@link addPermissionDenial} */ - private static final ThreadLocal<List<String>> sPermissionDenialHints = new ThreadLocal<>(); - - /** - * Report a hint that might explain why a permission check returned - * {@link PackageManager#PERMISSION_DENIED}. - * - * <p>Hints are only collected if enabled via {@link collectPermissionDenialHints} or - * when a non-null value was passed to {@link resetPermissionDenialHints} - * - * @param hint A description of the reason - * - * @hide - */ - public static void addPermissionDenialHint(@NonNull String hint) { - List<String> hints = sPermissionDenialHints.get(); - if (hints == null) { - return; - } - - hints.add(hint); - } - - /** - * @return hints added via {@link #addPermissionDenialHint(String)} on this thread before. - * - * @hide - */ - public static @Nullable List<String> getPermissionDenialHints() { - if (Build.IS_USER) { - return null; - } - - return sPermissionDenialHints.get(); - } - - /** - * Reset the permission denial hints for this thread. - * - * @param initial The initial values. If not null, enabled collection on this thread. - * - * @return the previously collected hints - * - * @hide - */ - public static @Nullable List<String> resetPermissionDenialHints( - @Nullable List<String> initial) { - List<String> prev = getPermissionDenialHints(); - if (initial == null) { - sPermissionDenialHints.remove(); - } else { - sPermissionDenialHints.set(initial); - } - return prev; - } - - /** - * Enable permission denial hint collection if package is in - * {@link Settings.Secure.DEBUG_PACKAGE_PERMISSION_CHECK} - * - * @param context A context to use - * @param uid The uid the permission check is for. - * - * @return the previously collected hints - * - * @hide - */ - public static @Nullable List<String> collectPermissionDenialHints(@NonNull Context context, - int uid) { - List<String> prev = getPermissionDenialHints(); - - if (Build.IS_USER) { - return prev; - } - - ContentResolver cr = context.getContentResolver(); - if (cr == null) { - return prev; - } - - String debugSetting; - try { - debugSetting = Settings.Secure.getString(cr, - Settings.Secure.DEBUG_PACKAGE_PERMISSION_CHECK); - } catch (IllegalStateException e) { - Log.e(LOG_TAG, "Cannot access settings", e); - return prev; - } - if (debugSetting == null) { - return prev; - } - String[] debugPkgs = debugSetting.split(","); - - PackageManager pm = context.getPackageManager(); - if (pm == null) { - return prev; - } - - String[] packages = pm.getPackagesForUid(uid); - if (packages == null) { - return prev; - } - - for (String pkg : packages) { - if (ArrayUtils.contains(debugPkgs, pkg)) { - sPermissionDenialHints.set(new ArrayList<>(0)); - break; - } - } - - return prev; - } - /** * Creates a new instance. * diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index fa244e480e11..2f68eb475e7a 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -187,6 +187,10 @@ public final class CalendarContract { * {@link android.database.ContentObserver} for this URL in the primary profile will be * notified when there is a change in the managed profile calendar provider. * + * <p>Throw UnsupportedOperationException if another profile doesn't exist or is disabled, or + * if the calling package is not whitelisted to access cross-profile calendar, or if the + * feature has been disabled by the user in Settings. + * * @see Events#ENTERPRISE_CONTENT_URI * @see Calendars#ENTERPRISE_CONTENT_URI * @see Instances#ENTERPRISE_CONTENT_URI diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dbc62f4a12fa..7c5a1fb5f787 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5786,16 +5786,6 @@ public final class Settings { public static final String ANDROID_ID = "android_id"; /** - * Comma separated list packages to enable collection of permission denial hints for. - * - * @hide - * - * @see android.permission.PermissionManager#collectPermissionDenialHints(Context, int) - */ - public static final String DEBUG_PACKAGE_PERMISSION_CHECK = - "debug_package_permission_check"; - - /** * @deprecated Use {@link android.provider.Settings.Global#BLUETOOTH_ON} instead */ @Deprecated diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 656127ad77a9..87e369f20d58 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -15,6 +15,9 @@ */ package android.service.autofill.augmented; +import static android.service.autofill.augmented.Helper.logResponse; +import static android.util.TimeUtils.formatDuration; + import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.CallSuper; @@ -38,9 +41,7 @@ import android.os.SystemClock; import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams; import android.util.Log; import android.util.Pair; -import android.util.Slog; import android.util.SparseArray; -import android.util.TimeUtils; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; @@ -48,6 +49,7 @@ import android.view.autofill.IAugmentedAutofillManagerClient; import android.view.autofill.IAutofillWindowPresenter; import com.android.internal.annotations.GuardedBy; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -84,6 +86,9 @@ public abstract class AugmentedAutofillService extends Service { private SparseArray<AutofillProxy> mAutofillProxies; + // Used for metrics / debug only + private ComponentName mServiceComponentName; + private final IAugmentedAutofillService mInterface = new IAugmentedAutofillService.Stub() { @Override @@ -125,6 +130,7 @@ public abstract class AugmentedAutofillService extends Service { /** @hide */ @Override public final IBinder onBind(Intent intent) { + mServiceComponentName = intent.getComponent(); if (SERVICE_INTERFACE.equals(intent.getAction())) { return mInterface.asBinder(); } @@ -215,8 +221,9 @@ public abstract class AugmentedAutofillService extends Service { final CancellationSignal cancellationSignal = CancellationSignal.fromTransport(transport); AutofillProxy proxy = mAutofillProxies.get(sessionId); if (proxy == null) { - proxy = new AutofillProxy(sessionId, client, taskId, componentName, focusedId, - focusedValue, requestTime, callback, cancellationSignal); + proxy = new AutofillProxy(sessionId, client, taskId, mServiceComponentName, + componentName, focusedId, focusedValue, requestTime, callback, + cancellationSignal); mAutofillProxies.put(sessionId, proxy); } else { // TODO(b/123099468): figure out if it's ok to reuse the proxy; add logging @@ -272,6 +279,8 @@ public abstract class AugmentedAutofillService extends Service { @Override /** @hide */ protected final void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.print("Service component: "); pw.println( + ComponentName.flattenToShortString(mServiceComponentName)); if (mAutofillProxies != null) { final int size = mAutofillProxies.size(); pw.print("Number proxies: "); pw.println(size); @@ -301,12 +310,12 @@ public abstract class AugmentedAutofillService extends Service { /** @hide */ static final class AutofillProxy { - static final int REPORT_EVENT_ON_SUCCESS = 1; + static final int REPORT_EVENT_NO_RESPONSE = 1; static final int REPORT_EVENT_UI_SHOWN = 2; static final int REPORT_EVENT_UI_DESTROYED = 3; @IntDef(prefix = { "REPORT_EVENT_" }, value = { - REPORT_EVENT_ON_SUCCESS, + REPORT_EVENT_NO_RESPONSE, REPORT_EVENT_UI_SHOWN, REPORT_EVENT_UI_DESTROYED }) @@ -319,6 +328,8 @@ public abstract class AugmentedAutofillService extends Service { private final int mSessionId; public final int taskId; public final ComponentName componentName; + // Used for metrics / debug only + private String mServicePackageName; @GuardedBy("mLock") private AutofillId mFocusedId; @GuardedBy("mLock") @@ -349,6 +360,7 @@ public abstract class AugmentedAutofillService extends Service { private CancellationSignal mCancellationSignal; private AutofillProxy(int sessionId, @NonNull IBinder client, int taskId, + @NonNull ComponentName serviceComponentName, @NonNull ComponentName componentName, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, long requestTime, @NonNull IFillCallback callback, @NonNull CancellationSignal cancellationSignal) { @@ -357,6 +369,7 @@ public abstract class AugmentedAutofillService extends Service { mCallback = callback; this.taskId = taskId; this.componentName = componentName; + mServicePackageName = serviceComponentName.getPackageName(); mFocusedId = focusedId; mFocusedValue = focusedValue; mFirstRequestTime = requestTime; @@ -439,9 +452,9 @@ public abstract class AugmentedAutofillService extends Service { mCallback.cancel(); } } catch (RemoteException e) { - Slog.e(TAG, "failed to check current pending request status", e); + Log.e(TAG, "failed to check current pending request status", e); } - Slog.d(TAG, "mCallback is updated."); + Log.d(TAG, "mCallback is updated."); } mCallback = callback; } @@ -463,13 +476,17 @@ public abstract class AugmentedAutofillService extends Service { // Used (mostly) for metrics. public void report(@ReportEvent int event) { + if (sVerbose) Log.v(TAG, "report(): " + event); + long duration = -1; + int type = MetricsEvent.TYPE_UNKNOWN; switch (event) { - case REPORT_EVENT_ON_SUCCESS: + case REPORT_EVENT_NO_RESPONSE: + type = MetricsEvent.TYPE_SUCCESS; if (mFirstOnSuccessTime == 0) { mFirstOnSuccessTime = SystemClock.elapsedRealtime(); + duration = mFirstOnSuccessTime - mFirstRequestTime; if (sDebug) { - Slog.d(TAG, "Service responded in " + TimeUtils.formatDuration( - mFirstOnSuccessTime - mFirstRequestTime)); + Log.d(TAG, "Service responded nothing in " + formatDuration(duration)); } } try { @@ -479,27 +496,25 @@ public abstract class AugmentedAutofillService extends Service { } break; case REPORT_EVENT_UI_SHOWN: + type = MetricsEvent.TYPE_OPEN; if (mUiFirstShownTime == 0) { mUiFirstShownTime = SystemClock.elapsedRealtime(); - if (sDebug) { - Slog.d(TAG, "UI shown in " + TimeUtils.formatDuration( - mUiFirstShownTime - mFirstRequestTime)); - } + duration = mUiFirstShownTime - mFirstRequestTime; + if (sDebug) Log.d(TAG, "UI shown in " + formatDuration(duration)); } break; case REPORT_EVENT_UI_DESTROYED: + type = MetricsEvent.TYPE_CLOSE; if (mUiFirstDestroyedTime == 0) { mUiFirstDestroyedTime = SystemClock.elapsedRealtime(); - if (sDebug) { - Slog.d(TAG, "UI destroyed in " + TimeUtils.formatDuration( - mUiFirstDestroyedTime - mFirstRequestTime)); - } + duration = mUiFirstDestroyedTime - mFirstRequestTime; + if (sDebug) Log.d(TAG, "UI destroyed in " + formatDuration(duration)); } break; default: - Slog.w(TAG, "invalid event reported: " + event); + Log.w(TAG, "invalid event reported: " + event); } - // TODO(b/122858578): log metrics as well + logResponse(type, mServicePackageName, componentName, mSessionId, duration); } public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { @@ -527,19 +542,19 @@ public abstract class AugmentedAutofillService extends Service { if (mFirstOnSuccessTime > 0) { final long responseTime = mFirstOnSuccessTime - mFirstRequestTime; pw.print(prefix); pw.print("response time: "); - TimeUtils.formatDuration(responseTime, pw); pw.println(); + formatDuration(responseTime, pw); pw.println(); } if (mUiFirstShownTime > 0) { final long uiRenderingTime = mUiFirstShownTime - mFirstRequestTime; pw.print(prefix); pw.print("UI rendering time: "); - TimeUtils.formatDuration(uiRenderingTime, pw); pw.println(); + formatDuration(uiRenderingTime, pw); pw.println(); } if (mUiFirstDestroyedTime > 0) { final long uiTotalTime = mUiFirstDestroyedTime - mFirstRequestTime; pw.print(prefix); pw.print("UI life time: "); - TimeUtils.formatDuration(uiTotalTime, pw); pw.println(); + formatDuration(uiTotalTime, pw); pw.println(); } } diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java index 33e6a8c25ba4..0251386bd7ce 100644 --- a/core/java/android/service/autofill/augmented/FillCallback.java +++ b/core/java/android/service/autofill/augmented/FillCallback.java @@ -50,8 +50,10 @@ public final class FillCallback { public void onSuccess(@Nullable FillResponse response) { if (sDebug) Log.d(TAG, "onSuccess(): " + response); - mProxy.report(AutofillProxy.REPORT_EVENT_ON_SUCCESS); - if (response == null) return; + if (response == null) { + mProxy.report(AutofillProxy.REPORT_EVENT_NO_RESPONSE); + return; + } final FillWindow fillWindow = response.getFillWindow(); if (fillWindow != null) { diff --git a/core/java/android/service/autofill/augmented/Helper.java b/core/java/android/service/autofill/augmented/Helper.java new file mode 100644 index 000000000000..501696f99c66 --- /dev/null +++ b/core/java/android/service/autofill/augmented/Helper.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.service.autofill.augmented; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.metrics.LogMaker; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + +/** @hide */ +public final class Helper { + + private static final MetricsLogger sMetricsLogger = new MetricsLogger(); + + /** + * Logs a {@code MetricsEvent.AUTOFILL_AUGMENTED_RESPONSE} event. + */ + public static void logResponse(int type, @NonNull String servicePackageName, + @NonNull ComponentName componentName, int mSessionId, long durationMs) { + final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_AUGMENTED_RESPONSE) + .setType(type) + .setComponentName(componentName) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, durationMs); + System.out.println("LOGGGO: " + log.getEntries()); // felipeal: tmp + sMetricsLogger.write(log); + } + + private Helper() { + throw new UnsupportedOperationException("contains only static methods"); + } +} diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 8819d220e748..0feab4d027a9 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -1947,16 +1947,21 @@ public abstract class Transition implements Cloneable { * @hide */ void forceToEnd(ViewGroup sceneRoot) { - ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators(); + final ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators(); int numOldAnims = runningAnimators.size(); - if (sceneRoot != null) { - WindowId windowId = sceneRoot.getWindowId(); - for (int i = numOldAnims - 1; i >= 0; i--) { - AnimationInfo info = runningAnimators.valueAt(i); - if (info.view != null && windowId != null && windowId.equals(info.windowId)) { - Animator anim = runningAnimators.keyAt(i); - anim.end(); - } + if (sceneRoot == null || numOldAnims == 0) { + return; + } + + WindowId windowId = sceneRoot.getWindowId(); + final ArrayMap<Animator, AnimationInfo> oldAnimators = new ArrayMap(runningAnimators); + runningAnimators.clear(); + + for (int i = numOldAnims - 1; i >= 0; i--) { + AnimationInfo info = oldAnimators.valueAt(i); + if (info.view != null && windowId != null && windowId.equals(info.windowId)) { + Animator anim = oldAnimators.keyAt(i); + anim.end(); } } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index bbb90536e8f8..1812d291dd8f 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -33,6 +33,7 @@ import android.graphics.Region; import android.graphics.RenderNode; import android.os.Build; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; import android.util.AttributeSet; @@ -119,11 +120,10 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb final Rect mScreenRect = new Rect(); SurfaceSession mSurfaceSession; - SurfaceControl mSurfaceControl; + SurfaceControlWithBackground mSurfaceControl; // In the case of format changes we switch out the surface in-place // we need to preserve the old one until the new one has drawn. - SurfaceControl mDeferredDestroySurfaceControl; - SurfaceControl mBackgroundControl; + SurfaceControlWithBackground mDeferredDestroySurfaceControl; final Rect mTmpRect = new Rect(); final Configuration mConfiguration = new Configuration(); @@ -487,29 +487,6 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb } } - private void updateBackgroundVisibilityInTransaction() { - if (mBackgroundControl == null) { - return; - } - if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) { - mBackgroundControl.show(); - mBackgroundControl.setLayer(Integer.MIN_VALUE); - } else { - mBackgroundControl.hide(); - } - } - - private void releaseSurfaces() { - if (mSurfaceControl != null) { - mSurfaceControl.remove(); - mSurfaceControl = null; - } - if (mBackgroundControl != null) { - mBackgroundControl.remove(); - mBackgroundControl = null; - } - } - /** @hide */ protected void updateSurface() { if (!mHaveFrame) { @@ -576,21 +553,14 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb updateOpaqueFlag(); final String name = "SurfaceView - " + viewRoot.getTitle().toString(); - mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) - .setName(name) - .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0) - .setBufferSize(mSurfaceWidth, mSurfaceHeight) - .setFormat(mFormat) - .setParent(viewRoot.getSurfaceControl()) - .setFlags(mSurfaceFlags) - .build(); - mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession) - .setName("Background for -" + name) - .setOpaque(true) - .setColorLayer() - .setParent(mSurfaceControl) - .build(); - + mSurfaceControl = new SurfaceControlWithBackground( + name, + (mSurfaceFlags & SurfaceControl.OPAQUE) != 0, + new SurfaceControl.Builder(mSurfaceSession) + .setBufferSize(mSurfaceWidth, mSurfaceHeight) + .setFormat(mFormat) + .setParent(viewRoot.getSurfaceControl()) + .setFlags(mSurfaceFlags)); } else if (mSurfaceControl == null) { return; } @@ -607,13 +577,11 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb SurfaceControl.openTransaction(); try { mSurfaceControl.setLayer(mSubLayer); - if (mViewVisibility) { mSurfaceControl.show(); } else { mSurfaceControl.hide(); } - updateBackgroundVisibilityInTransaction(); // While creating the surface, we will set it's initial // geometry. Outside of that though, we should generally @@ -699,7 +667,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb } if (creating) { - mSurface.copyFrom(mSurfaceControl); + mSurface.copyFrom(mSurfaceControl.mForegroundControl); } if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion @@ -709,7 +677,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb // existing {@link Surface} will be ignored when the size changes. // Therefore, we must explicitly recreate the {@link Surface} in these // cases. - mSurface.createFrom(mSurfaceControl); + mSurface.createFrom(mSurfaceControl.mForegroundControl); } if (visible && mSurface.isValid()) { @@ -756,7 +724,8 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (mSurfaceControl != null && !mSurfaceCreated) { mSurface.release(); - releaseSurfaces(); + mSurfaceControl.remove(); + mSurfaceControl = null; } } } catch (Exception ex) { @@ -857,7 +826,8 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb private void setParentSpaceRectangle(Rect position, long frameNumber) { final ViewRootImpl viewRoot = getViewRootImpl(); - applySurfaceTransforms(mSurfaceControl, position, frameNumber); + applySurfaceTransforms(mSurfaceControl.mForegroundControl, position, frameNumber); + applySurfaceTransforms(mSurfaceControl.mBackgroundControl, position, frameNumber); applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface, frameNumber); @@ -922,10 +892,13 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (frameNumber > 0) { final ViewRootImpl viewRoot = getViewRootImpl(); - mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface, + mRtTransaction.deferTransactionUntilSurface(mSurfaceControl.mForegroundControl, viewRoot.mSurface, + frameNumber); + mRtTransaction.deferTransactionUntilSurface(mSurfaceControl.mBackgroundControl, viewRoot.mSurface, frameNumber); } - mRtTransaction.hide(mSurfaceControl); + mRtTransaction.hide(mSurfaceControl.mForegroundControl); + mRtTransaction.hide(mSurfaceControl.mBackgroundControl); mRtTransaction.apply(); } }; @@ -972,19 +945,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb * @hide */ public void setResizeBackgroundColor(int bgColor) { - if (mBackgroundControl == null) { - return; - } - - final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f, - Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f }; - - SurfaceControl.openTransaction(); - try { - mBackgroundControl.setColor(colorComponents); - } finally { - SurfaceControl.closeTransaction(); - } + mSurfaceControl.setBackgroundColor(bgColor); } @UnsupportedAppUsage @@ -1168,6 +1129,134 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb * @return The SurfaceControl for this SurfaceView. */ public SurfaceControl getSurfaceControl() { - return mSurfaceControl; + return mSurfaceControl.mForegroundControl; + } + + class SurfaceControlWithBackground { + SurfaceControl mForegroundControl; + SurfaceControl mBackgroundControl; + private boolean mOpaque = true; + public boolean mVisible = false; + + public SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b) + throws Exception { + mForegroundControl = b.setName(name).build(); + mBackgroundControl = b.setName("Background for -" + name) + // Unset the buffer size of the background color layer. + .setBufferSize(0, 0) + .setColorLayer() + .build(); + + mOpaque = opaque; + } + + public void setAlpha(float alpha) { + mForegroundControl.setAlpha(alpha); + mBackgroundControl.setAlpha(alpha); + } + + public void setLayer(int zorder) { + mForegroundControl.setLayer(zorder); + // -3 is below all other child layers as SurfaceView never goes below -2 + mBackgroundControl.setLayer(-3); + } + + public void setPosition(float x, float y) { + mForegroundControl.setPosition(x, y); + mBackgroundControl.setPosition(x, y); + } + + public void setBufferSize(int w, int h) { + mForegroundControl.setBufferSize(w, h); + // The background surface is a color layer so we do not set a size. + } + + public void setWindowCrop(Rect crop) { + mForegroundControl.setWindowCrop(crop); + mBackgroundControl.setWindowCrop(crop); + } + + public void setWindowCrop(int width, int height) { + mForegroundControl.setWindowCrop(width, height); + mBackgroundControl.setWindowCrop(width, height); + } + + public void setLayerStack(int layerStack) { + mForegroundControl.setLayerStack(layerStack); + mBackgroundControl.setLayerStack(layerStack); + } + + public void setOpaque(boolean isOpaque) { + mForegroundControl.setOpaque(isOpaque); + mOpaque = isOpaque; + updateBackgroundVisibility(); + } + + public void setSecure(boolean isSecure) { + mForegroundControl.setSecure(isSecure); + } + + public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + mForegroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy); + mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy); + } + + public void hide() { + mForegroundControl.hide(); + mVisible = false; + updateBackgroundVisibility(); + } + + public void show() { + mForegroundControl.show(); + mVisible = true; + updateBackgroundVisibility(); + } + + public void remove() { + mForegroundControl.remove(); + mBackgroundControl.remove(); + } + + public void release() { + mForegroundControl.release(); + mBackgroundControl.release(); + } + + public void setTransparentRegionHint(Region region) { + mForegroundControl.setTransparentRegionHint(region); + mBackgroundControl.setTransparentRegionHint(region); + } + + public void deferTransactionUntil(IBinder handle, long frame) { + mForegroundControl.deferTransactionUntil(handle, frame); + mBackgroundControl.deferTransactionUntil(handle, frame); + } + + public void deferTransactionUntil(Surface barrier, long frame) { + mForegroundControl.deferTransactionUntil(barrier, frame); + mBackgroundControl.deferTransactionUntil(barrier, frame); + } + + /** Set the color to fill the background with. */ + private void setBackgroundColor(int bgColor) { + final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f, + Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f }; + + SurfaceControl.openTransaction(); + try { + mBackgroundControl.setColor(colorComponents); + } finally { + SurfaceControl.closeTransaction(); + } + } + + void updateBackgroundVisibility() { + if (mOpaque && mVisible) { + mBackgroundControl.show(); + } else { + mBackgroundControl.hide(); + } + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4463e13ca5ee..e8356752f807 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -8271,6 +8271,7 @@ public final class ViewRootImpl implements ViewParent, void changeCanvasOpacity(boolean opaque) { Log.d(mTag, "changeCanvasOpacity: opaque=" + opaque); + opaque = opaque & ((mView.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0); if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.setOpaque(opaque); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 9a34ffae2a2e..b7d838edadc5 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -202,6 +202,9 @@ public class ChooserActivity extends ResolverActivity { private long mChooserShownTime; protected boolean mIsSuccessfullySelected; + private long mQueriedTargetServicesTimeMs; + private long mQueriedSharingShortcutsTimeMs; + private ChooserListAdapter mChooserListAdapter; private ChooserRowAdapter mChooserRowAdapter; private int mChooserRowServiceSpacing; @@ -273,6 +276,8 @@ public class ChooserActivity extends ResolverActivity { sri.connection.destroy(); mServiceConnections.remove(sri.connection); if (mServiceConnections.isEmpty()) { + logDirectShareTargetReceived( + MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE); sendVoiceChoicesIfNeeded(); } break; @@ -283,6 +288,8 @@ public class ChooserActivity extends ResolverActivity { } unbindRemainingServices(); + logDirectShareTargetReceived( + MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE); sendVoiceChoicesIfNeeded(); mChooserListAdapter.completeServiceTargetLoading(); break; @@ -305,6 +312,8 @@ public class ChooserActivity extends ResolverActivity { break; case SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED: + logDirectShareTargetReceived( + MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER); sendVoiceChoicesIfNeeded(); break; @@ -1155,6 +1164,8 @@ public class ChooserActivity extends ResolverActivity { } void queryTargetServices(ChooserListAdapter adapter) { + mQueriedTargetServicesTimeMs = System.currentTimeMillis(); + final PackageManager pm = getPackageManager(); ShortcutManager sm = (ShortcutManager) getSystemService(ShortcutManager.class); int targetsToQuery = 0; @@ -1281,6 +1292,7 @@ public class ChooserActivity extends ResolverActivity { private void queryDirectShareTargets( ChooserListAdapter adapter, boolean skipAppPredictionService) { + mQueriedSharingShortcutsTimeMs = System.currentTimeMillis(); if (!skipAppPredictionService) { AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); if (appPredictor != null) { @@ -1391,6 +1403,14 @@ public class ChooserActivity extends ResolverActivity { // Do nothing. We'll send the voice stuff ourselves. } + private void logDirectShareTargetReceived(int logCategory) { + final long queryTime = + logCategory == MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER + ? mQueriedSharingShortcutsTimeMs : mQueriedTargetServicesTimeMs; + final int apiLatency = (int) (System.currentTimeMillis() - queryTime); + getMetricsLogger().write(new LogMaker(logCategory).setSubtype(apiLatency)); + } + void updateModelAndChooserCounts(TargetInfo info) { if (info != null) { sendClickToAppPredictor(info); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index d50a70ed62ef..31f7d6f51ff8 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1958,8 +1958,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final boolean isFullscreen = config.windowConfiguration.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FULLSCREEN; return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility()) - & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE))); + & View.SYSTEM_UI_FLAG_FULLSCREEN)); } private void updateDecorCaptionStatus(Configuration config) { diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 28c59db6b932..c5fc9b3628df 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -49,8 +49,8 @@ int ifc_disable(const char *ifname); namespace android { constexpr int MAXPACKETSIZE = 8 * 1024; -// FrameworkListener limits the size of commands to 1024 bytes. TODO: fix this. -constexpr int MAXCMDSIZE = 1024; +// FrameworkListener limits the size of commands to 4096 bytes. +constexpr int MAXCMDSIZE = 4096; static void throwErrnoException(JNIEnv* env, const char* functionName, int error) { ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName)); diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 72e3d3495e37..74ebb95b58c7 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -37,14 +37,14 @@ void setDriverPathAndSphalLibraries_native(JNIEnv* env, jobject clazz, jstring p void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, jstring driverVersionName, jlong driverVersionCode, - jlong driverBuildTime, jstring appPackageName) { + jlong driverBuildTime, jstring appPackageName, jint vulkanVersion) { ScopedUtfChars driverPackageNameChars(env, driverPackageName); ScopedUtfChars driverVersionNameChars(env, driverVersionName); ScopedUtfChars appPackageNameChars(env, appPackageName); android::GraphicsEnv::getInstance().setGpuStats(driverPackageNameChars.c_str(), driverVersionNameChars.c_str(), driverVersionCode, driverBuildTime, - appPackageNameChars.c_str()); + appPackageNameChars.c_str(), vulkanVersion); } void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring devOptIn, @@ -88,7 +88,7 @@ void setDebugLayersGLES_native(JNIEnv* env, jobject clazz, jstring layers) { const JNINativeMethod g_methods[] = { { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) }, { "setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPathAndSphalLibraries_native) }, - { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JJLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) }, + { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JJLjava/lang/String;I)V", reinterpret_cast<void*>(setGpuStats_native) }, { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) }, { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, diff --git a/core/proto/android/stats/connectivity/resolv_stats.proto b/core/proto/android/stats/connectivity/resolv_stats.proto deleted file mode 100644 index 43eb67397fa5..000000000000 --- a/core/proto/android/stats/connectivity/resolv_stats.proto +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -syntax = "proto2"; -package android.stats.connectivity; -import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; - -enum EventType { - EVENT_UNKNOWN = 0; - EVENT_GETADDRINFO = 1; - EVENT_GETHOSTBYNAME = 2; - EVENT_GETHOSTBYADDR = 3; - EVENT_RES_NSEND = 4; -} - -enum PrivateDnsModes { - OFF = 0; - OPPORTUNISTIC = 1; - STRICT = 2; -} -// The return value of the DNS resolver for each DNS lookups. -// bionic/libc/include/netdb.h -// system/netd/resolv/include/netd_resolv/resolv.h -enum ReturnCode { - RC_EAI_NO_ERROR = 0; - RC_EAI_ADDRFAMILY = 1; - RC_EAI_AGAIN = 2; - RC_EAI_BADFLAGS = 3; - RC_EAI_FAIL = 4; - RC_EAI_FAMILY = 5; - RC_EAI_MEMORY = 6; - RC_EAI_NODATA = 7; - RC_EAI_NONAME = 8; - RC_EAI_SERVICE = 9; - RC_EAI_SOCKTYPE = 10; - RC_EAI_SYSTEM = 11; - RC_EAI_BADHINTS = 12; - RC_EAI_PROTOCOL = 13; - RC_EAI_OVERFLOW = 14; - RC_RESOLV_TIMEOUT = 255; - RC_EAI_MAX = 256; -} - - -enum NsRcode { - ns_r_noerror = 0; // No error occurred. - ns_r_formerr = 1; // Format error. - ns_r_servfail = 2; // Server failure. - ns_r_nxdomain = 3; // Name error. - ns_r_notimpl = 4; // Unimplemented. - ns_r_refused = 5; // Operation refused. - // these are for BIND_UPDATE - ns_r_yxdomain = 6; // Name exists - ns_r_yxrrset = 7; // RRset exists - ns_r_nxrrset = 8; // RRset does not exist - ns_r_notauth = 9; // Not authoritative for zone - ns_r_notzone = 10; // Zone of record different from zone section - ns_r_max = 11; - // The following are EDNS extended rcodes - ns_r_badvers = 16; - // The following are TSIG errors - //ns_r_badsig = 16, - ns_r_badkey = 17; - ns_r_badtime = 18; -} - -// Currently defined type values for resources and queries. -enum NsType { - ns_t_invalid = 0; // Cookie. - ns_t_a = 1; // Host address. - ns_t_ns = 2; // Authoritative server. - ns_t_md = 3; // Mail destination. - ns_t_mf = 4; // Mail forwarder. - ns_t_cname = 5; // Canonical name. - ns_t_soa = 6; // Start of authority zone. - ns_t_mb = 7; // Mailbox domain name. - ns_t_mg = 8; // Mail group member. - ns_t_mr = 9; // Mail rename name. - ns_t_null = 10; // Null resource record. - ns_t_wks = 11; // Well known service. - ns_t_ptr = 12; // Domain name pointer. - ns_t_hinfo = 13; // Host information. - ns_t_minfo = 14; // Mailbox information. - ns_t_mx = 15; // Mail routing information. - ns_t_txt = 16; // Text strings. - ns_t_rp = 17; // Responsible person. - ns_t_afsdb = 18; // AFS cell database. - ns_t_x25 = 19; // X_25 calling address. - ns_t_isdn = 20; // ISDN calling address. - ns_t_rt = 21; // Router. - ns_t_nsap = 22; // NSAP address. - ns_t_nsap_ptr = 23; // Reverse NSAP lookup (deprecated). - ns_t_sig = 24; // Security signature. - ns_t_key = 25; // Security key. - ns_t_px = 26; // X.400 mail mapping. - ns_t_gpos = 27; // Geographical position (withdrawn). - ns_t_aaaa = 28; // IPv6 Address. - ns_t_loc = 29; // Location Information. - ns_t_nxt = 30; // Next domain (security). - ns_t_eid = 31; // Endpoint identifier. - ns_t_nimloc = 32; // Nimrod Locator. - ns_t_srv = 33; // Server Selection. - ns_t_atma = 34; // ATM Address - ns_t_naptr = 35; // Naming Authority PoinTeR - ns_t_kx = 36; // Key Exchange - ns_t_cert = 37; // Certification record - ns_t_a6 = 38; // IPv6 address (experimental) - ns_t_dname = 39; // Non-terminal DNAME - ns_t_sink = 40; // Kitchen sink (experimentatl) - ns_t_opt = 41; // EDNS0 option (meta-RR) - ns_t_apl = 42; // Address prefix list (RFC 3123) - ns_t_ds = 43; // Delegation Signer - ns_t_sshfp = 44; // SSH Fingerprint - ns_t_ipseckey = 45; // IPSEC Key - ns_t_rrsig = 46; // RRset Signature - ns_t_nsec = 47; // Negative security - ns_t_dnskey = 48; // DNS Key - ns_t_dhcid = 49; // Dynamic host configuratin identifier - ns_t_nsec3 = 50; // Negative security type 3 - ns_t_nsec3param = 51;// Negative security type 3 parameters - ns_t_hip = 55; // Host Identity Protocol - ns_t_spf = 99; // Sender Policy Framework - ns_t_tkey = 249; // Transaction key - ns_t_tsig = 250; // Transaction signature. - ns_t_ixfr = 251; // Incremental zone transfer. - ns_t_axfr = 252; // Transfer zone of authority. - ns_t_mailb = 253; // Transfer mailbox records. - ns_t_maila = 254; // Transfer mail agent records. - ns_t_any = 255; // Wildcard match. - ns_t_zxfr = 256; // BIND-specific, nonstandard. - ns_t_dlv = 32769; // DNSSEC look-aside validatation. - ns_t_max = 65536; -} - -enum IpVersion { - IPV4 = 0; - IPV6 = 1; - MIXED = 2; -} - -enum TransportType { - UDP = 0; - TCP = 1; - DOT = 2; - DOT_UDP = 3; - DOT_TCP = 4; -} - -message DnsQueryEvent { - optional NsRcode rrcode = 1; - optional NsType rrtype = 2; - optional bool cache_hit = 3; - optional IpVersion ipversion = 4; - optional TransportType transport = 5; - optional int32 packet_retransmits = 6; // Used only by the UDP transport - optional int32 reconnects = 7; // Used only by TCP and DOT - optional int32 latency_micros = 8; - optional int32 active_experiments = 9; - optional android.net.NetworkCapabilitiesProto.Transport network_type = 10; -} - -message DnsQueryEventRe { - repeated DnsQueryEvent dns_query_event = 1; -} - - -message DnsCallEvent { - -} - diff --git a/core/proto/android/stats/dnsresolver/Android.bp b/core/proto/android/stats/dnsresolver/Android.bp new file mode 100644 index 000000000000..0b5aa8677a6e --- /dev/null +++ b/core/proto/android/stats/dnsresolver/Android.bp @@ -0,0 +1,25 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +java_library_static { + name: "dnsresolverprotosnano", + proto: { + type: "nano", + }, + srcs: [ + "dns_resolver.proto", + ], + sdk_version: "system_current", + no_framework_libs: true, +} diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto new file mode 100644 index 000000000000..af6fea017bef --- /dev/null +++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto @@ -0,0 +1,214 @@ +/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+package android.stats.dnsresolver;
+
+enum EventType {
+ EVENT_UNKNOWN = 0;
+ EVENT_GETADDRINFO = 1;
+ EVENT_GETHOSTBYNAME = 2;
+ EVENT_GETHOSTBYADDR = 3;
+ EVENT_RES_NSEND = 4;
+}
+
+// The return value of the DNS resolver for each DNS lookups.
+// bionic/libc/include/netdb.h
+// system/netd/resolv/include/netd_resolv/resolv.h
+enum ReturnCode {
+ RC_EAI_NO_ERROR = 0;
+ RC_EAI_ADDRFAMILY = 1;
+ RC_EAI_AGAIN = 2;
+ RC_EAI_BADFLAGS = 3;
+ RC_EAI_FAIL = 4;
+ RC_EAI_FAMILY = 5;
+ RC_EAI_MEMORY = 6;
+ RC_EAI_NODATA = 7;
+ RC_EAI_NONAME = 8;
+ RC_EAI_SERVICE = 9;
+ RC_EAI_SOCKTYPE = 10;
+ RC_EAI_SYSTEM = 11;
+ RC_EAI_BADHINTS = 12;
+ RC_EAI_PROTOCOL = 13;
+ RC_EAI_OVERFLOW = 14;
+ RC_RESOLV_TIMEOUT = 255;
+ RC_EAI_MAX = 256;
+}
+
+enum NsRcode {
+ NS_R_NO_ERROR = 0; // No error occurred.
+ NS_R_FORMERR = 1; // Format error.
+ NS_R_SERVFAIL = 2; // Server failure.
+ NS_R_NXDOMAIN = 3; // Name error.
+ NS_R_NOTIMPL = 4; // Unimplemented.
+ NS_R_REFUSED = 5; // Operation refused.
+ // these are for BIND_UPDATE
+ NS_R_YXDOMAIN = 6; // Name exists
+ NS_R_YXRRSET = 7; // RRset exists
+ NS_R_NXRRSET = 8; // RRset does not exist
+ NS_R_NOTAUTH = 9; // Not authoritative for zone
+ NS_R_NOTZONE = 10; // Zone of record different from zone section
+ NS_R_MAX = 11;
+ // The following are EDNS extended rcodes
+ NS_R_BADVERS = 16;
+ // The following are TSIG errors
+ // NS_R_BADSIG = 16,
+ NS_R_BADKEY = 17;
+ NS_R_BADTIME = 18;
+}
+
+// Currently defined type values for resources and queries.
+enum NsType {
+ NS_T_INVALID = 0; // Cookie.
+ NS_T_A = 1; // Host address.
+ NS_T_NS = 2; // Authoritative server.
+ NS_T_MD = 3; // Mail destination.
+ NS_T_MF = 4; // Mail forwarder.
+ NS_T_CNAME = 5; // Canonical name.
+ NS_T_SOA = 6; // Start of authority zone.
+ NS_T_MB = 7; // Mailbox domain name.
+ NS_T_MG = 8; // Mail group member.
+ NS_T_MR = 9; // Mail rename name.
+ NS_T_NULL = 10; // Null resource record.
+ NS_T_WKS = 11; // Well known service.
+ NS_T_PTR = 12; // Domain name pointer.
+ NS_T_HINFO = 13; // Host information.
+ NS_T_MINFO = 14; // Mailbox information.
+ NS_T_MX = 15; // Mail routing information.
+ NS_T_TXT = 16; // Text strings.
+ NS_T_RP = 17; // Responsible person.
+ NS_T_AFSDB = 18; // AFS cell database.
+ NS_T_X25 = 19; // X_25 calling address.
+ NS_T_ISDN = 20; // ISDN calling address.
+ NS_T_RT = 21; // Router.
+ NS_T_NSAP = 22; // NSAP address.
+ NS_T_NSAP_PTR = 23; // Reverse NSAP lookup (deprecated).
+ NS_T_SIG = 24; // Security signature.
+ NS_T_KEY = 25; // Security key.
+ NS_T_PX = 26; // X.400 mail mapping.
+ NS_T_GPOS = 27; // Geographical position (withdrawn).
+ NS_T_AAAA = 28; // IPv6 Address.
+ NS_T_LOC = 29; // Location Information.
+ NS_T_NXT = 30; // Next domain (security).
+ NS_T_EID = 31; // Endpoint identifier.
+ NS_T_NIMLOC = 32; // Nimrod Locator.
+ NS_T_SRV = 33; // Server Selection.
+ NS_T_ATMA = 34; // ATM Address
+ NS_T_NAPTR = 35; // Naming Authority PoinTeR
+ NS_T_KX = 36; // Key Exchange
+ NS_T_CERT = 37; // Certification record
+ NS_T_A6 = 38; // IPv6 address (experimental)
+ NS_T_DNAME = 39; // Non-terminal DNAME
+ NS_T_SINK = 40; // Kitchen sink (experimentatl)
+ NS_T_OPT = 41; // EDNS0 option (meta-RR)
+ NS_T_APL = 42; // Address prefix list (RFC 3123)
+ NS_T_DS = 43; // Delegation Signer
+ NS_T_SSHFP = 44; // SSH Fingerprint
+ NS_T_IPSECKEY = 45; // IPSEC Key
+ NS_T_RRSIG = 46; // RRset Signature
+ NS_T_NSEC = 47; // Negative security
+ NS_T_DNSKEY = 48; // DNS Key
+ NS_T_DHCID = 49; // Dynamic host configuratin identifier
+ NS_T_NSEC3 = 50; // Negative security type 3
+ NS_T_NSEC3PARAM = 51; // Negative security type 3 parameters
+ NS_T_HIP = 55; // Host Identity Protocol
+ NS_T_SPF = 99; // Sender Policy Framework
+ NS_T_TKEY = 249; // Transaction key
+ NS_T_TSIG = 250; // Transaction signature.
+ NS_T_IXFR = 251; // Incremental zone transfer.
+ NS_T_AXFR = 252; // Transfer zone of authority.
+ NS_T_MAILB = 253; // Transfer mailbox records.
+ NS_T_MAILA = 254; // Transfer mail agent records.
+ NS_T_ANY = 255; // Wildcard match.
+ NS_T_ZXFR = 256; // BIND-specific, nonstandard.
+ NS_T_DLV = 32769; // DNSSEC look-aside validatation.
+ NS_T_MAX = 65536;
+}
+
+enum IpVersion {
+ IV_UNKNOWN = 0;
+ IV_IPV4 = 1;
+ IV_IPV6 = 2;
+}
+
+enum TransportType {
+ TT_UNKNOWN = 0;
+ TT_UDP = 1;
+ TT_TCP = 2;
+ TT_DOT = 3;
+}
+
+enum PrivateDnsModes {
+ PDM_UNKNOWN = 0;
+ PDM_OFF = 1;
+ PDM_OPPORTUNISTIC = 2;
+ PDM_STRICT = 3;
+}
+
+enum Transport {
+ // Indicates this network uses a Cellular transport.
+ TRANSPORT_DEFAULT = 0; // TRANSPORT_CELLULAR
+ // Indicates this network uses a Wi-Fi transport.
+ TRANSPORT_WIFI = 1;
+ // Indicates this network uses a Bluetooth transport.
+ TRANSPORT_BLUETOOTH = 2;
+ // Indicates this network uses an Ethernet transport.
+ TRANSPORT_ETHERNET = 3;
+ // Indicates this network uses a VPN transport.
+ TRANSPORT_VPN = 4;
+ // Indicates this network uses a Wi-Fi Aware transport.
+ TRANSPORT_WIFI_AWARE = 5;
+ // Indicates this network uses a LoWPAN transport.
+ TRANSPORT_LOWPAN = 6;
+}
+
+enum CacheStatus{
+ // the cache can't handle that kind of queries.
+ // or the answer buffer is too small.
+ CS_UNSUPPORTED = 0;
+ // the cache doesn't know about this query.
+ CS_NOTFOUND = 1;
+ // the cache found the answer.
+ CS_FOUND = 2;
+ // Don't do anything on cache.
+ CS_SKIP = 3;
+}
+
+message DnsQueryEvent {
+ optional android.stats.dnsresolver.NsRcode rcode = 1;
+
+ optional android.stats.dnsresolver.NsType type = 2;
+
+ optional android.stats.dnsresolver.CacheStatus cache_hit = 3;
+
+ optional android.stats.dnsresolver.IpVersion ip_version = 4;
+
+ optional android.stats.dnsresolver.TransportType transport = 5;
+
+ // Number of DNS query retry times
+ optional int32 retry_times = 6;
+
+ // Ordinal number of name server.
+ optional int32 dns_server_count = 7;
+
+ // Used only by TCP and DOT. True for new connections.
+ optional bool connected = 8;
+
+ optional int32 latency_micros = 9;
+}
+
+message DnsQueryEvents {
+ repeated DnsQueryEvent dns_query_event = 1;
+}
diff --git a/core/res/res/anim/resolver_close_anim.xml b/core/res/res/anim/resolver_close_anim.xml new file mode 100644 index 000000000000..18a25e985943 --- /dev/null +++ b/core/res/res/anim/resolver_close_anim.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2009, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@anim/accelerate_interpolator" + android:zAdjustment="top"> + + <translate xmlns:android="http://schemas.android.com/apk/res/android" + android:fromYDelta="0" + android:toYDelta="100%" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/core/res/res/anim/resolver_launch_anim.xml b/core/res/res/anim/resolver_launch_anim.xml new file mode 100644 index 000000000000..ebb3701316fa --- /dev/null +++ b/core/res/res/anim/resolver_launch_anim.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Animation for when a dock window at the bottom of the screen is entering. --> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@anim/accelerate_decelerate_interpolator" + android:zAdjustment="top"> + + <translate android:fromYDelta="100%" + android:toYDelta="0" + android:startOffset="@android:integer/config_shortAnimTime" + android:duration="@android:integer/config_mediumAnimTime"/> +</set>
\ No newline at end of file diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ab6f29b19445..4c6ae812f1ee 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3699,7 +3699,7 @@ <item name="config_inCallNotificationVolume" format="float" type="dimen">.10</item> <!-- URI for in call notification sound --> - <string translatable="false" name="config_inCallNotificationSound">/system/media/audio/ui/InCallNotification.ogg</string> + <string translatable="false" name="config_inCallNotificationSound">/product/media/audio/ui/InCallNotification.ogg</string> <!-- Default number of notifications from the same app before they are automatically grouped by the OS --> <integer translatable="false" name="config_autoGroupAtCount">4</integer> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index dd0a6e605f60..b07e7ef0bd05 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1549,11 +1549,11 @@ <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] --> <string name="face_acquired_tilt_too_extreme">Turn your head a little less.</string> <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] --> - <string name="face_acquired_roll_too_extreme">Please straighten your head vertically.</string> + <string name="face_acquired_roll_too_extreme">Turn your head a little less.</string> <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] --> - <string name="face_acquired_obscured">Clear the space between your head and the phone.</string> + <string name="face_acquired_obscured">Remove anything hiding your face.</string> <!-- Message shown during acquisition when the sensor is dirty [CHAR LIMIT=50] --> - <string name="face_acquired_sensor_dirty">Please clean the camera.</string> + <string name="face_acquired_sensor_dirty">Clean the sensor at the top edge of the screen.</string> <!-- Array containing custom messages shown during face acquisition from vendor. Vendor is expected to add and translate these strings --> <string-array name="face_acquired_vendor"> </string-array> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 18a019a3e144..29181c871d1b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3787,6 +3787,8 @@ <java-symbol type="dimen" name="chooser_direct_share_label_placeholder_max_width" /> <java-symbol type="layout" name="chooser_az_label_row" /> <java-symbol type="string" name="chooser_all_apps_button_label" /> - + <java-symbol type="anim" name="resolver_launch_anim" /> + <java-symbol type="style" name="Animation.DeviceDefault.Activity.Resolver" /> + <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 08d6d0621cf4..8015a5d5fbdd 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1667,8 +1667,7 @@ easier. <!-- Theme used for the intent picker activity. --> <style name="Theme.DeviceDefault.ResolverCommon" parent="Theme.DeviceDefault.DayNight"> - <item name="windowEnterTransition">@empty</item> - <item name="windowExitTransition">@empty</item> + <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Activity.Resolver</item> <item name="windowIsTranslucent">true</item> <item name="windowNoTitle">true</item> <item name="windowBackground">@color/transparent</item> @@ -1682,6 +1681,14 @@ easier. <item name="navigationBarDividerColor">@color/chooser_row_divider</item> </style> + <style name="Animation.DeviceDefault.Activity.Resolver" parent="Animation.DeviceDefault.Activity"> + <item name="activityOpenEnterAnimation">@anim/resolver_launch_anim</item> + <item name="taskOpenEnterAnimation">@anim/resolver_launch_anim</item> + <!-- Handle close for profile switching --> + <item name="activityOpenExitAnimation">@anim/resolver_close_anim</item> + <item name="taskOpenExitAnimation">@anim/resolver_close_anim</item> + </style> + <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon"> <item name="windowLightNavigationBar">true</item> </style> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index a71460270252..e76754582fe9 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -622,7 +622,6 @@ public class SettingsBackupTest { Settings.Secure.COMPLETED_CATEGORY_PREFIX, Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, Settings.Secure.CONTENT_CAPTURE_ENABLED, - Settings.Secure.DEBUG_PACKAGE_PERMISSION_CHECK, Settings.Secure.DEFAULT_INPUT_METHOD, Settings.Secure.DEVICE_PAIRED, Settings.Secure.DIALER_DEFAULT_APPLICATION, diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java index 267cb365748e..eec7be22c78b 100644 --- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java +++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java @@ -177,6 +177,23 @@ public class InstallOverlayTests extends BaseHostJUnit4Test { .contains(APP_OVERLAY_PACKAGE_NAME)); } + @Test + public void testAdbShellOMSInterface() throws Exception { + installPackage("OverlayHostTests_AppOverlayV1.apk"); + assertTrue(shell("cmd overlay list " + DEVICE_TEST_PKG).contains(DEVICE_TEST_PKG)); + assertTrue(shell("cmd overlay list " + DEVICE_TEST_PKG).contains(APP_OVERLAY_PACKAGE_NAME)); + assertEquals("[ ] " + APP_OVERLAY_PACKAGE_NAME, + shell("cmd overlay list " + APP_OVERLAY_PACKAGE_NAME).trim()); + assertEquals("STATE_DISABLED", + shell("cmd overlay dump state " + APP_OVERLAY_PACKAGE_NAME).trim()); + + setOverlayEnabled(APP_OVERLAY_PACKAGE_NAME, true); + assertEquals("[x] " + APP_OVERLAY_PACKAGE_NAME, + shell("cmd overlay list " + APP_OVERLAY_PACKAGE_NAME).trim()); + assertEquals("STATE_ENABLED", + shell("cmd overlay dump state " + APP_OVERLAY_PACKAGE_NAME).trim()); + } + private void delay() { try { Thread.sleep(1000); @@ -195,20 +212,24 @@ public class InstallOverlayTests extends BaseHostJUnit4Test { } private void installConvertExistingInstantPackageToFull(String pkg) throws Exception { - getDevice().executeShellCommand("cmd package install-existing --wait --full " + pkg); + shell("cmd package install-existing --wait --full " + pkg); } private void setPackageEnabled(String pkg, boolean enabled) throws Exception { - getDevice().executeShellCommand("cmd package " + (enabled ? "enable " : "disable ") + pkg); + shell("cmd package " + (enabled ? "enable " : "disable ") + pkg); delay(); } private void setOverlayEnabled(String pkg, boolean enabled) throws Exception { - getDevice().executeShellCommand("cmd overlay " + (enabled ? "enable " : "disable ") + pkg); + shell("cmd overlay " + (enabled ? "enable " : "disable ") + pkg); delay(); } private boolean overlayManagerContainsPackage(String pkg) throws Exception { - return getDevice().executeShellCommand("cmd overlay list").contains(pkg); + return shell("cmd overlay list").contains(pkg); + } + + private String shell(final String cmd) throws Exception { + return getDevice().executeShellCommand(cmd); } } diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 77f756baa1f2..68c0a22b30c7 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -33,7 +33,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.AssetFileDescriptor; import android.database.Cursor; -import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.net.Uri; import android.os.Environment; import android.os.FileUtils; @@ -50,7 +49,6 @@ import android.util.Log; import com.android.internal.database.SortCursor; -import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -59,7 +57,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.LinkedBlockingQueue; /** * RingtoneManager provides access to ringtones, notification, and other types @@ -927,11 +924,7 @@ public class RingtoneManager { } // Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}. - try (NewRingtoneScanner scanner = new NewRingtoneScanner(outFile)) { - return scanner.take(); - } catch (InterruptedException e) { - throw new IOException("Audio file failed to scan as a ringtone", e); - } + return MediaStore.scanFile(mContext, outFile); } private static final String getExternalDirectoryForType(final int type) { @@ -1109,53 +1102,6 @@ public class RingtoneManager { } /** - * Creates a {@link android.media.MediaScannerConnection} to scan a ringtone file and add its - * information to the internal database. - * - * It uses a {@link java.util.concurrent.LinkedBlockingQueue} so that the caller can block until - * the scan is completed. - */ - private class NewRingtoneScanner implements Closeable, MediaScannerConnectionClient { - private MediaScannerConnection mMediaScannerConnection; - private File mFile; - private LinkedBlockingQueue<Uri> mQueue = new LinkedBlockingQueue<>(1); - - public NewRingtoneScanner(File file) { - mFile = file; - mMediaScannerConnection = new MediaScannerConnection(mContext, this); - mMediaScannerConnection.connect(); - } - - @Override - public void close() { - mMediaScannerConnection.disconnect(); - } - - @Override - public void onMediaScannerConnected() { - mMediaScannerConnection.scanFile(mFile.getAbsolutePath(), null); - } - - @Override - public void onScanCompleted(String path, Uri uri) { - if (uri == null) { - // There was some issue with scanning. Delete the copied file so it is not oprhaned. - mFile.delete(); - return; - } - try { - mQueue.put(uri); - } catch (InterruptedException e) { - Log.e(TAG, "Unable to put new ringtone Uri in queue", e); - } - } - - public Uri take() throws InterruptedException { - return mQueue.take(); - } - } - - /** * Attempts to create a context for the given user. * * @return created context, or null if package does not exist diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 3500475eeeda..55692437d8d5 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -891,11 +891,16 @@ public final class MediaController { } /** - * Set the playback speed. + * Sets the playback speed. A value of {@code 1.0f} is the default playback value, + * and a negative value indicates reverse playback. {@code 0.0f} is not allowed. * * @param speed The playback speed + * @throws IllegalArgumentException if the {@code speed} is equal to zero. */ public void setPlaybackSpeed(float speed) { + if (speed == 0.0f) { + throw new IllegalArgumentException("speed must not be zero"); + } try { mSessionBinder.setPlaybackSpeed(mContext.getPackageName(), mCbStub, speed); } catch (RemoteException e) { diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index c577469477e0..cee869ba4808 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -1084,6 +1084,9 @@ public final class MediaSession { * To update the new playback speed, create a new {@link PlaybackState} by using {@link * PlaybackState.Builder#setState(int, long, float)}, and set it with * {@link #setPlaybackState(PlaybackState)}. + * <p> + * A value of {@code 1.0f} is the default playback value, and a negative value indicates + * reverse playback. The {@code speed} will not be equal to zero. * * @param speed the playback speed * @see #setPlaybackState(PlaybackState) diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index d420966079fe..348f01eaa004 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -65,19 +65,19 @@ <!-- user interface sound effects --> <integer name="def_power_sounds_enabled">1</integer> - <string name="def_low_battery_sound" translatable="false">/system/media/audio/ui/LowBattery.ogg</string> + <string name="def_low_battery_sound" translatable="false">/product/media/audio/ui/LowBattery.ogg</string> <integer name="def_dock_sounds_enabled">0</integer> <integer name="def_dock_sounds_enabled_when_accessibility">0</integer> - <string name="def_desk_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string> - <string name="def_desk_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string> - <string name="def_car_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string> - <string name="def_car_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string> + <string name="def_desk_dock_sound" translatable="false">/product/media/audio/ui/Dock.ogg</string> + <string name="def_desk_undock_sound" translatable="false">/product/media/audio/ui/Undock.ogg</string> + <string name="def_car_dock_sound" translatable="false">/product/media/audio/ui/Dock.ogg</string> + <string name="def_car_undock_sound" translatable="false">/product/media/audio/ui/Undock.ogg</string> <integer name="def_lockscreen_sounds_enabled">1</integer> - <string name="def_lock_sound" translatable="false">/system/media/audio/ui/Lock.ogg</string> - <string name="def_unlock_sound" translatable="false">/system/media/audio/ui/Unlock.ogg</string> - <string name="def_trusted_sound" translatable="false">/system/media/audio/ui/Trusted.ogg</string> - <string name="def_wireless_charging_started_sound" translatable="false">/system/media/audio/ui/WirelessChargingStarted.ogg</string> - <string name="def_charging_started_sound" translatable="false">/system/media/audio/ui/ChargingStarted.ogg</string> + <string name="def_lock_sound" translatable="false">/product/media/audio/ui/Lock.ogg</string> + <string name="def_unlock_sound" translatable="false">/product/media/audio/ui/Unlock.ogg</string> + <string name="def_trusted_sound" translatable="false">/product/media/audio/ui/Trusted.ogg</string> + <string name="def_wireless_charging_started_sound" translatable="false">/product/media/audio/ui/WirelessChargingStarted.ogg</string> + <string name="def_charging_started_sound" translatable="false">/product/media/audio/ui/ChargingStarted.ogg</string> <!-- sound trigger detection service default values --> <integer name="def_max_sound_trigger_detection_service_ops_per_day" translatable="false">1000</integer> diff --git a/packages/SystemUI/res-keyguard/drawable/analog_frame.xml b/packages/SystemUI/res-keyguard/drawable/analog_frame.xml index a663ac826127..3196169df7cc 100644 --- a/packages/SystemUI/res-keyguard/drawable/analog_frame.xml +++ b/packages/SystemUI/res-keyguard/drawable/analog_frame.xml @@ -1,7 +1,7 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:height="250dp" - android:width="250dp" + android:height="380dp" + android:width="380dp" android:viewportHeight="380" android:viewportWidth="380"> - <path android:fillColor="#000000" android:pathData="M190,190m0,2a2,2 0,1 1,0 -4a2,2 0,1 1,0 4"/> + <path android:fillColor="#000000" android:pathData="M190,190m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> </vector> diff --git a/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml index c7b6d60b319f..a05b16aa0204 100644 --- a/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml +++ b/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml @@ -1,7 +1,4 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:height="250dp" - android:width="250dp" - android:viewportHeight="380" - android:viewportWidth="380"> - <path android:fillColor="#777777" android:fillType="evenOdd" android:pathData="M203,190C203,185.398 200.608,181.354 197,179.044L197,58C197,54.134 193.866,51 190,51C186.134,51 183,54.134 183,58L183,179.043C179.392,181.354 177,185.397 177,190C177,197.18 182.82,203 190,203C197.18,203 203,197.18 203,190Z"/> +<vector android:height="380dp" android:viewportHeight="380" + android:viewportWidth="380" android:width="380dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#777777" android:fillType="evenOdd" android:pathData="m200,190.047c0.018,-3.701 -1.978,-6.942 -4.959,-8.686l0.582,-123.337c0.013,-2.761 -2.215,-5.011 -4.976,-5.024 -2.761,-0.013 -5.01,2.215 -5.024,4.976L185.041,181.314c-2.997,1.715 -5.024,4.937 -5.041,8.639 -0.026,5.523 4.43,10.021 9.953,10.047 5.523,0.026 10.021,-4.43 10.047,-9.953z"/> </vector> diff --git a/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml index 458275bec23a..1277b6281032 100644 --- a/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml +++ b/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml @@ -1,7 +1,4 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:height="250dp" - android:width="250dp" - android:viewportHeight="380" - android:viewportWidth="380"> - <path android:fillColor="#FFFFFF" android:pathData="M192,182.252C195.45,183.14 198,186.272 198,190C198,194.418 194.418,198 190,198C185.582,198 182,194.418 182,190C182,186.272 184.55,183.14 188,182.252L188,10C188,8.895 188.895,8 190,8C191.105,8 192,8.895 192,10L192,182.252Z"/> +<vector android:height="380dp" android:viewportHeight="380" + android:viewportWidth="380" android:width="380dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#aaaaaa" android:fillType="evenOdd" android:pathData="m191,184.083c2.838,0.476 5,2.944 5,5.917 0,3.314 -2.686,6 -6,6 -3.314,0 -6,-2.686 -6,-6 0,-2.973 2.162,-5.441 5,-5.917V9c0,-0.552 0.448,-1 1,-1 0.552,0 1,0.448 1,1z"/> </vector> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 3118ab77bf6f..1967dd1013f9 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -48,16 +48,16 @@ android:id="@+id/default_clock_view_bold" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" + android:layout_gravity="bottom|center_horizontal" android:gravity="center_horizontal" android:letterSpacing="0.03" android:textColor="?attr/wallpaperTextColor" android:singleLine="true" - style="@style/widget_big_bold" + style="@style/widget_title_bold" android:format12Hour="@string/keyguard_widget_12_hours_format" android:format24Hour="@string/keyguard_widget_24_hours_format" android:elegantTextHeight="false" - android:visibility="gone" + android:visibility="invisible" /> </FrameLayout> <include layout="@layout/keyguard_status_area" diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index ab48f1df2ec2..1c8e141a61f8 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -66,16 +66,16 @@ <item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item> <item name="android:ellipsize">none</item> </style> - <style name="widget_big_bold"> + <style name="widget_title_bold"> <item name="android:textStyle">bold</item> - <item name="android:textSize">@dimen/widget_big_font_size</item> - <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item> + <item name="android:textSize">@dimen/widget_title_font_size</item> + <item name="android:paddingBottom">@dimen/widget_vertical_padding_clock</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> <item name="android:ellipsize">none</item> </style> <style name="widget_small_bold"> <item name="android:textStyle">bold</item> - <item name="android:textSize">@dimen/widget_title_font_size</item> + <item name="android:textSize">@dimen/widget_small_font_size</item> <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> <item name="android:ellipsize">none</item> diff --git a/packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml b/packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml new file mode 100644 index 000000000000..c471b38306d5 --- /dev/null +++ b/packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="40dp" + android:height="40dp" + android:viewportWidth="40" + android:viewportHeight="40"> + <path + android:fillColor="#9AA0A6" + android:pathData="M24.6667 16.2733L23.7267 15.3333L20 19.06L16.2734 15.3333L15.3334 16.2733L19.06 20L15.3334 23.7266L16.2734 24.6666L20 20.94L23.7267 24.6666L24.6667 23.7266L20.94 20L24.6667 16.2733Z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml new file mode 100644 index 000000000000..15f14d8af89b --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="40dp" + android:height="40dp" + android:viewportWidth="40" + android:viewportHeight="40"> + <path + android:fillColor="#5F6368" + android:pathData="M24.6667 16.2733L23.7267 15.3333L20 19.06L16.2734 15.3333L15.3334 16.2733L19.06 20L15.3334 23.7267L16.2734 24.6667L20 20.94L23.7267 24.6667L24.6667 23.7267L20.94 20L24.6667 16.2733Z"/> +</vector> diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml new file mode 100644 index 000000000000..2b210064023b --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml @@ -0,0 +1,66 @@ +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Extends FrameLayout --> +<com.android.systemui.statusbar.notification.stack.SectionHeaderView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_section_header_height" + android:focusable="true" + android:clickable="true" + > + <com.android.systemui.statusbar.notification.row.NotificationBackgroundView + android:id="@+id/backgroundNormal" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + <com.android.systemui.statusbar.notification.row.NotificationBackgroundView + android:id="@+id/backgroundDimmed" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <LinearLayout + android:id="@+id/content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="horizontal" + > + <TextView + android:id="@+id/header_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginLeft="@dimen/notification_section_header_padding_left" + android:text="@string/notification_section_header_gentle" + android:textSize="12sp" + android:textColor="@color/notification_section_header_label_color" + /> + <ImageView + android:id="@+id/btn_clear_all" + android:layout_width="@dimen/notification_section_header_height" + android:layout_height="@dimen/notification_section_header_height" + android:layout_marginRight="4dp" + android:src="@drawable/status_bar_notification_section_header_clear_btn" + android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all" + /> + </LinearLayout> + + <com.android.systemui.statusbar.notification.FakeShadowView + android:id="@+id/fake_shadow" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</com.android.systemui.statusbar.notification.stack.SectionHeaderView> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 89d1a19737ee..07d81c037e21 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -49,6 +49,8 @@ <color name="notification_guts_header_text_color">@color/GM2_grey_200</color> <color name="notification_guts_button_color">@color/GM2_blue_200</color> + <color name="notification_section_header_label_color">@color/GM2_grey_200</color> + <!-- The color of the background in the top part of QSCustomizer --> <color name="qs_customize_background">@color/GM2_grey_900</color> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 32879c052063..2200cf0112ac 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -97,6 +97,8 @@ <color name="notification_alert_color">#FFF87B2B</color> <color name="notification_guts_button_color">@color/GM2_blue_700</color> + <color name="notification_section_header_label_color">@color/GM2_grey_900</color> + <color name="assist_orb_color">#ffffff</color> <color name="keyguard_user_switcher_background_gradient_color">#77000000</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 70e4b0d7054b..0fa542c498bc 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -695,6 +695,9 @@ <!-- The top padding of the clear all button --> <dimen name="clear_all_padding_top">12dp</dimen> + <dimen name="notification_section_header_height">40dp</dimen> + <dimen name="notification_section_header_padding_left">16dp</dimen> + <!-- Largest size an avatar might need to be drawn in the user picker, status bar, or quick settings header --> <dimen name="max_avatar_size">48dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index f75f255324a6..e97055f08e32 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -103,6 +103,12 @@ <item type="id" name="action_snooze_assistant_suggestion_1"/> <item type="id" name="action_snooze"/> + <!-- Accessibility actions for bubbles. --> + <item type="id" name="action_move_top_left"/> + <item type="id" name="action_move_top_right"/> + <item type="id" name="action_move_bottom_left"/> + <item type="id" name="action_move_bottom_right"/> + <!-- For StatusIconContainer to tag its icon views --> <item type="id" name="status_bar_view_state_tag" /> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index dc35653e5f7d..6ba72b6b85ad 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1102,6 +1102,12 @@ <!-- The text for the manage notifications link. [CHAR LIMIT=40] --> <string name="manage_notifications_text">Manage</string> + <!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] --> + <string name="notification_section_header_gentle">Gentle notifications</string> + + <!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] --> + <string name="accessibility_notification_section_header_gentle_clear_all">Clear all gentle notifications</string> + <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] --> <string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index f2a961d0b681..21b3a0082319 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -76,6 +76,8 @@ public abstract class TaskStackChangeListener { public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { } public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { } + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { } + /** * Checks that the current user matches the process. Since * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index d250acca32c5..814db19c56b7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -184,6 +184,11 @@ public class TaskStackChangeListeners extends TaskStackListener { } @Override + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) throws RemoteException { + mHandler.obtainMessage(H.ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget(); + } + + @Override public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) throws RemoteException { mHandler.obtainMessage(H.ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId, @@ -214,6 +219,7 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 15; private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16; private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17; + private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18; public H(Looper looper) { @@ -343,6 +349,12 @@ public class TaskStackChangeListeners extends TaskStackListener { } break; } + case ON_BACK_PRESSED_ON_TASK_ROOT: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onBackPressedOnTaskRoot( + (RunningTaskInfo) msg.obj); + } + } } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 8d62bca00f0a..c15c7872b6f8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -3,22 +3,20 @@ package com.android.keyguard; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.animation.Animator; -import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.app.WallpaperManager; import android.content.Context; import android.graphics.Paint; import android.graphics.Paint.Style; import android.os.Build; -import android.transition.ChangeBounds; import android.transition.Transition; import android.transition.TransitionListenerAdapter; import android.transition.TransitionManager; +import android.transition.TransitionSet; import android.transition.TransitionValues; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; -import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -52,6 +50,11 @@ public class KeyguardClockSwitch extends RelativeLayout { private static final String TAG = "KeyguardClockSwitch"; /** + * Animation fraction when text is transitioned to/from bold. + */ + private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f; + + /** * Controller used to track StatusBar state to know when to show the big_clock_container. */ private final StatusBarStateController mStatusBarStateController; @@ -71,10 +74,8 @@ public class KeyguardClockSwitch extends RelativeLayout { */ private final Transition mTransition; - /** - * Listener for layout transitions. - */ - private final Transition.TransitionListener mTransitionListener; + private final ClockVisibilityTransition mClockTransition; + private final ClockVisibilityTransition mBoldClockTransition; /** * Optional/alternative clock injected via plugin. @@ -156,8 +157,19 @@ public class KeyguardClockSwitch extends RelativeLayout { mStatusBarState = mStatusBarStateController.getState(); mSysuiColorExtractor = colorExtractor; mClockManager = clockManager; - mTransition = new ClockBoundsTransition(); - mTransitionListener = new ClockBoundsTransitionListener(); + + mClockTransition = new ClockVisibilityTransition().setCutoff( + 1 - TO_BOLD_TRANSITION_FRACTION); + mClockTransition.addTarget(R.id.default_clock_view); + mBoldClockTransition = new ClockVisibilityTransition().setCutoff( + TO_BOLD_TRANSITION_FRACTION); + mBoldClockTransition.addTarget(R.id.default_clock_view_bold); + mTransition = new TransitionSet() + .setOrdering(TransitionSet.ORDERING_TOGETHER) + .addTransition(mClockTransition) + .addTransition(mBoldClockTransition) + .setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); } /** @@ -182,7 +194,6 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockManager.addOnClockChangedListener(mClockChangedListener); mStatusBarStateController.addCallback(mStateListener); mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener); - mTransition.addListener(mTransitionListener); updateColors(); } @@ -192,7 +203,6 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockManager.removeOnClockChangedListener(mClockChangedListener); mStatusBarStateController.removeCallback(mStateListener); mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener); - mTransition.removeListener(mTransitionListener); setClockPlugin(null); } @@ -287,7 +297,6 @@ public class KeyguardClockSwitch extends RelativeLayout { public void setTextSize(int unit, float size) { mClockView.setTextSize(unit, size); - mClockViewBold.setTextSize(unit, size); } public void setFormat12Hour(CharSequence format) { @@ -390,23 +399,46 @@ public class KeyguardClockSwitch extends RelativeLayout { * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock in * these cases. */ - public void setKeyguardShowingHeader(boolean hasHeader) { + void setKeyguardShowingHeader(boolean hasHeader) { if (mShowingHeader == hasHeader || hasCustomClock()) { return; } mShowingHeader = hasHeader; + float smallFontSize = mContext.getResources().getDimensionPixelSize( + R.dimen.widget_small_font_size); + float bigFontSize = mContext.getResources().getDimensionPixelSize( + R.dimen.widget_big_font_size); + mClockTransition.setScale(smallFontSize / bigFontSize); + mBoldClockTransition.setScale(bigFontSize / smallFontSize); + TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition); - int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader - ? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size); - int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader + mClockView.setVisibility(hasHeader ? View.INVISIBLE : View.VISIBLE); + mClockViewBold.setVisibility(hasHeader ? View.VISIBLE : View.INVISIBLE); + int paddingBottom = mContext.getResources().getDimensionPixelSize(hasHeader ? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding); - mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); - mClockViewBold.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(), mClockView.getPaddingRight(), paddingBottom); mClockViewBold.setPadding(mClockViewBold.getPaddingLeft(), mClockViewBold.getPaddingTop(), mClockViewBold.getPaddingRight(), paddingBottom); + + if (hasHeader) { + // After the transition, make the default clock GONE so that it doesn't make the + // KeyguardStatusView appear taller in KeyguardClockPositionAlgorithm and elsewhere. + mTransition.addListener(new TransitionListenerAdapter() { + @Override + public void onTransitionEnd(Transition transition) { + super.onTransitionEnd(transition); + // Check that header is actually showing. I saw issues where this event was + // fired after the big clock transitioned back to visible, which causes the time + // to completely disappear. + if (mShowingHeader) { + mClockView.setVisibility(View.GONE); + } + transition.removeListener(this); + } + }); + } } @VisibleForTesting(otherwise = VisibleForTesting.NONE) @@ -434,91 +466,114 @@ public class KeyguardClockSwitch extends RelativeLayout { } /** - * Special layout transition that scales the clock view as its bounds change, to make it look - * like the text is shrinking. + * {@link Visibility} transformation that scales the view while it is disappearing/appearing and + * transitions suddenly at a cutoff fraction during the animation. */ - private class ClockBoundsTransition extends ChangeBounds { + private class ClockVisibilityTransition extends android.transition.Visibility { + + private static final String PROPNAME_VISIBILITY = "systemui:keyguard:visibility"; + + private float mCutoff; + private float mScale; /** - * Animation fraction when text is transitioned to/from bold. + * Constructs a transition that switches between visible/invisible at a cutoff and scales in + * size while appearing/disappearing. */ - private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f; - - ClockBoundsTransition() { - setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2); - setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + ClockVisibilityTransition() { + setCutoff(1f); + setScale(1f); } - @Override - public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, - TransitionValues endValues) { - Animator animator = super.createAnimator(sceneRoot, startValues, endValues); - if (animator == null || startValues.view != mClockView) { - return animator; - } + /** + * Sets the transition point between visible/invisible. + * + * @param cutoff The fraction in [0, 1] when the view switches between visible/invisible. + * @return This transition object + */ + public ClockVisibilityTransition setCutoff(float cutoff) { + mCutoff = cutoff; + return this; + } - ValueAnimator boundsAnimator = null; - if (animator instanceof AnimatorSet) { - Animator first = ((AnimatorSet) animator).getChildAnimations().get(0); - if (first instanceof ValueAnimator) { - boundsAnimator = (ValueAnimator) first; - } - } else if (animator instanceof ValueAnimator) { - boundsAnimator = (ValueAnimator) animator; - } + /** + * Sets the scale factor applied while appearing/disappearing. + * + * @param scale Scale factor applied while appearing/disappearing. When factor is less than + * one, the view will shrink while disappearing. When it is greater than one, + * the view will expand while disappearing. + * @return This transition object + */ + public ClockVisibilityTransition setScale(float scale) { + mScale = scale; + return this; + } - if (boundsAnimator != null) { - float bigFontSize = mContext.getResources() - .getDimensionPixelSize(R.dimen.widget_big_font_size); - float smallFontSize = mContext.getResources() - .getDimensionPixelSize(R.dimen.widget_small_font_size); - float startScale = mShowingHeader - ? bigFontSize / smallFontSize : smallFontSize / bigFontSize; - final int normalViewVisibility = mShowingHeader ? View.INVISIBLE : View.VISIBLE; - final int boldViewVisibility = mShowingHeader ? View.VISIBLE : View.INVISIBLE; - final float boldTransitionFraction = mShowingHeader ? TO_BOLD_TRANSITION_FRACTION : - 1f - TO_BOLD_TRANSITION_FRACTION; - boundsAnimator.addUpdateListener(animation -> { - final float fraction = animation.getAnimatedFraction(); - if (fraction > boldTransitionFraction) { - mClockView.setVisibility(normalViewVisibility); - mClockViewBold.setVisibility(boldViewVisibility); - } - float scale = MathUtils.lerp(startScale, 1f /* stop */, - animation.getAnimatedFraction()); - mClockView.setPivotX(mClockView.getWidth() / 2f); - mClockViewBold.setPivotX(mClockViewBold.getWidth() / 2f); - mClockView.setPivotY(0); - mClockViewBold.setPivotY(0); - mClockView.setScaleX(scale); - mClockViewBold.setScaleX(scale); - mClockView.setScaleY(scale); - mClockViewBold.setScaleY(scale); - }); - } + @Override + public void captureStartValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + captureVisibility(transitionValues); + } - return animator; + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + captureVisibility(transitionValues); } - } - /** - * Transition listener for layout transition that scales the clock view. - */ - private class ClockBoundsTransitionListener extends TransitionListenerAdapter { + private void captureVisibility(TransitionValues transitionValues) { + transitionValues.values.put(PROPNAME_VISIBILITY, + transitionValues.view.getVisibility()); + } @Override - public void onTransitionEnd(Transition transition) { - mClockView.setVisibility(mShowingHeader ? View.INVISIBLE : View.VISIBLE); - mClockViewBold.setVisibility(mShowingHeader ? View.VISIBLE : View.INVISIBLE); - mClockView.setScaleX(1f); - mClockViewBold.setScaleX(1f); - mClockView.setScaleY(1f); - mClockViewBold.setScaleY(1f); + public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, + TransitionValues endValues) { + final float cutoff = mCutoff; + final int startVisibility = View.INVISIBLE; + final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY); + final float startScale = mScale; + final float endScale = 1f; + return createAnimator(view, cutoff, startVisibility, endVisibility, startScale, + endScale); } @Override - public void onTransitionCancel(Transition transition) { - onTransitionEnd(transition); + public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, + TransitionValues endValues) { + final float cutoff = 1f - mCutoff; + final int startVisibility = View.VISIBLE; + final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY); + final float startScale = 1f; + final float endScale = mScale; + return createAnimator(view, cutoff, startVisibility, endVisibility, startScale, + endScale); + } + + private Animator createAnimator(View view, float cutoff, int startVisibility, + int endVisibility, float startScale, float endScale) { + view.setPivotY(view.getHeight() - view.getPaddingBottom()); + view.setVisibility(startVisibility); + ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); + animator.addUpdateListener(animation -> { + final float fraction = animation.getAnimatedFraction(); + if (fraction > cutoff) { + view.setVisibility(endVisibility); + } + final float scale = MathUtils.lerp(startScale, endScale, fraction); + view.setScaleX(scale); + view.setScaleY(scale); + }); + addListener(new TransitionListenerAdapter() { + @Override + public void onTransitionEnd(Transition transition) { + view.setVisibility(endVisibility); + view.setScaleX(1f); + view.setScaleY(1f); + transition.removeListener(this); + } + }); + return animator; } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index ae8bc528ab6a..050655c79ffc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -19,6 +19,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import android.app.Presentation; import android.content.Context; +import android.graphics.Color; import android.graphics.Point; import android.hardware.display.DisplayManager; import android.media.MediaRouter; @@ -32,9 +33,11 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; +import com.android.systemui.Dependency; +import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.statusbar.phone.NavigationBarView; import com.android.systemui.util.InjectionInflationController; -// TODO(multi-display): Support multiple external displays public class KeyguardDisplayManager { protected static final String TAG = "KeyguardDisplayManager"; private static boolean DEBUG = KeyguardConstants.DEBUG; @@ -49,6 +52,9 @@ public class KeyguardDisplayManager { private final SparseArray<Presentation> mPresentations = new SparseArray<>(); + private final NavigationBarController mNavBarController = + Dependency.get(NavigationBarController.class); + private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @@ -56,6 +62,7 @@ public class KeyguardDisplayManager { public void onDisplayAdded(int displayId) { final Display display = mDisplayService.getDisplay(displayId); if (mShowing) { + updateNavigationBarVisibility(displayId, false /* navBarVisible */); showPresentation(display); } } @@ -192,11 +199,15 @@ public class KeyguardDisplayManager { if (showing) { final Display[] displays = mDisplayService.getDisplays(); for (Display display : displays) { + int displayId = display.getDisplayId(); + updateNavigationBarVisibility(displayId, false /* navBarVisible */); changed |= showPresentation(display); } } else { changed = mPresentations.size() > 0; for (int i = mPresentations.size() - 1; i >= 0; i--) { + int displayId = mPresentations.keyAt(i); + updateNavigationBarVisibility(displayId, true /* navBarVisible */); mPresentations.valueAt(i).dismiss(); } mPresentations.clear(); @@ -204,6 +215,25 @@ public class KeyguardDisplayManager { return changed; } + // TODO(b/127878649): this logic is from + // {@link StatusBarKeyguardViewManager#updateNavigationBarVisibility}. Try to revisit a long + // term solution in R. + private void updateNavigationBarVisibility(int displayId, boolean navBarVisible) { + // Leave this task to {@link StatusBarKeyguardViewManager} + if (displayId == DEFAULT_DISPLAY) return; + + NavigationBarView navBarView = mNavBarController.getNavigationBarView(displayId); + // We may not have nav bar on a display. + if (navBarView == null) return; + + if (navBarVisible) { + navBarView.getRootView().setVisibility(View.VISIBLE); + } else { + navBarView.getRootView().setVisibility(View.GONE); + } + + } + private final static class KeyguardPresentation extends Presentation { private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s @@ -251,6 +281,15 @@ public class KeyguardDisplayManager { LayoutInflater inflater = mInjectableInflater.injectable( LayoutInflater.from(getContext())); setContentView(inflater.inflate(R.layout.keyguard_presentation, null)); + + // Logic to make the lock screen fullscreen + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + getWindow().setNavigationBarContrastEnforced(false); + getWindow().setNavigationBarColor(Color.TRANSPARENT); + mClock = findViewById(R.id.clock); // Avoid screen burn in diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java index 9a0b1906dd4a..a39d811c93ca 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java @@ -402,6 +402,12 @@ public class FaceDialogView extends BiometricDialogView { } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) { mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); mIconController.startPulsing(); + } else if (oldState == STATE_ERROR && newState == STATE_PENDING_CONFIRMATION) { + mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); + mIconController.animateOnce(R.drawable.face_dialog_wink_from_dark); + } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) { + mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); + mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) { mIconController.animateOnce(R.drawable.face_dialog_dark_to_error); mHandler.postDelayed(mErrorToIdleAnimationRunnable, BiometricPrompt.HIDE_DIALOG_DELAY); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index cff03c9b4b9b..7d189b28aa5e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -719,6 +719,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleData.setExpanded(false); } } + + @Override + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { + if (mStackView != null && taskInfo.displayId == getExpandedDisplayId(mContext)) { + mBubbleData.setExpanded(false); + } + } } private static boolean shouldAutoBubbleMessages(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index d1bc9a91636c..123d73dc6432 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -45,6 +45,7 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; import android.widget.TextView; @@ -391,11 +392,34 @@ public class BubbleStackView extends FrameLayout { @Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); - info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS); + + // Custom actions. + AccessibilityAction moveTopLeft = new AccessibilityAction(R.id.action_move_top_left, + getContext().getResources() + .getString(R.string.bubble_accessibility_action_move_top_left)); + info.addAction(moveTopLeft); + + AccessibilityAction moveTopRight = new AccessibilityAction(R.id.action_move_top_right, + getContext().getResources() + .getString(R.string.bubble_accessibility_action_move_top_right)); + info.addAction(moveTopRight); + + AccessibilityAction moveBottomLeft = new AccessibilityAction(R.id.action_move_bottom_left, + getContext().getResources() + .getString(R.string.bubble_accessibility_action_move_bottom_left)); + info.addAction(moveBottomLeft); + + AccessibilityAction moveBottomRight = new AccessibilityAction(R.id.action_move_bottom_right, + getContext().getResources() + .getString(R.string.bubble_accessibility_action_move_bottom_right)); + info.addAction(moveBottomRight); + + // Default actions. + info.addAction(AccessibilityAction.ACTION_DISMISS); if (mIsExpanded) { - info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE); + info.addAction(AccessibilityAction.ACTION_COLLAPSE); } else { - info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND); + info.addAction(AccessibilityAction.ACTION_EXPAND); } } @@ -404,16 +428,30 @@ public class BubbleStackView extends FrameLayout { if (super.performAccessibilityActionInternal(action, arguments)) { return true; } - switch (action) { - case AccessibilityNodeInfo.ACTION_DISMISS: - mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION); - return true; - case AccessibilityNodeInfo.ACTION_COLLAPSE: - mBubbleData.setExpanded(false); - return true; - case AccessibilityNodeInfo.ACTION_EXPAND: - mBubbleData.setExpanded(true); - return true; + final RectF stackBounds = mStackAnimationController.getAllowableStackPositionRegion(); + + // R constants are not final so we cannot use switch-case here. + if (action == AccessibilityNodeInfo.ACTION_DISMISS) { + mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION); + return true; + } else if (action == AccessibilityNodeInfo.ACTION_COLLAPSE) { + mBubbleData.setExpanded(false); + return true; + } else if (action == AccessibilityNodeInfo.ACTION_EXPAND) { + mBubbleData.setExpanded(true); + return true; + } else if (action == R.id.action_move_top_left) { + mStackAnimationController.springStack(stackBounds.left, stackBounds.top); + return true; + } else if (action == R.id.action_move_top_right) { + mStackAnimationController.springStack(stackBounds.right, stackBounds.top); + return true; + } else if (action == R.id.action_move_bottom_left) { + mStackAnimationController.springStack(stackBounds.left, stackBounds.bottom); + return true; + } else if (action == R.id.action_move_bottom_right) { + mStackAnimationController.springStack(stackBounds.right, stackBounds.bottom); + return true; } return false; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 47f2cd40b5e1..bc249aedc605 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -175,11 +175,33 @@ public class StackAnimationController extends /** Whether the stack is on the left side of the screen. */ public boolean isStackOnLeftSide() { - if (mLayout != null) { - return mStackPosition.x - mIndividualBubbleSize / 2 < mLayout.getWidth() / 2; - } else { + if (mLayout == null) { return false; } + float stackCenter = mStackPosition.x + mIndividualBubbleSize / 2; + float screenCenter = mLayout.getWidth() / 2; + return stackCenter < screenCenter; + } + + /** + * Fling stack to given corner, within allowable screen bounds. + * Note that we need new SpringForce instances per animation despite identical configs because + * SpringAnimation uses SpringForce's internal (changing) velocity while the animation runs. + */ + public void springStack(float destinationX, float destinationY) { + springFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_X, + new SpringForce() + .setStiffness(SPRING_AFTER_FLING_STIFFNESS) + .setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO), + 0 /* startXVelocity */, + destinationX); + + springFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_Y, + new SpringForce() + .setStiffness(SPRING_AFTER_FLING_STIFFNESS) + .setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO), + 0 /* startYVelocity */, + destinationY); } /** @@ -352,6 +374,7 @@ public class StackAnimationController extends float destinationY = Float.MIN_VALUE; if (imeVisible) { + // Stack is lower than it should be and overlaps the now-visible IME. if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) { mPreImeY = mStackPosition.y; destinationY = maxBubbleY; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java index c83c74f69f90..95e497e6574c 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java @@ -26,6 +26,9 @@ import java.util.ArrayList; * example, provide information on the current touch state. */ public class ClassifierData { + private static final long MINIMUM_DT_NANOS = 16666666; // 60Hz + private static final long MINIMUM_DT_SMEAR_NANOS = 2500000; // 2.5ms + private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>(); private ArrayList<Stroke> mEndingStrokes = new ArrayList<>(); private final float mDpi; @@ -34,7 +37,18 @@ public class ClassifierData { mDpi = dpi; } - public void update(MotionEvent event) { + /** Returns true if the event should be considered, false otherwise. */ + public boolean update(MotionEvent event) { + // We limit to 60hz sampling. Drop anything happening faster than that. + // Legacy code was created with an assumed sampling rate. As devices increase their + // sampling rate, this creates potentialy false positives. + if (event.getActionMasked() == MotionEvent.ACTION_MOVE + && mCurrentStrokes.size() != 0 + && event.getEventTimeNano() - mCurrentStrokes.get(0).getLastEventTimeNano() + < MINIMUM_DT_NANOS - MINIMUM_DT_SMEAR_NANOS) { + return false; + } + mEndingStrokes.clear(); int action = event.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { @@ -54,6 +68,8 @@ public class ClassifierData { mEndingStrokes.add(getStroke(id)); } } + + return true; } public void cleanUp(MotionEvent event) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java index 0cc50cddbfc6..86dccb222875 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java @@ -151,7 +151,9 @@ public class HumanInteractionClassifier extends Classifier { } private void addTouchEvent(MotionEvent event) { - mClassifierData.update(event); + if (!mClassifierData.update(event)) { + return; + } for (StrokeClassifier c : mStrokeClassifiers) { c.onTouchEvent(event); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java index fb04d3e73a2d..977a2d0b528a 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java @@ -68,4 +68,12 @@ public class Stroke { public ArrayList<Point> getPoints() { return mPoints; } + + public long getLastEventTimeNano() { + if (mPoints.isEmpty()) { + return mStartTimeNano; + } + + return mPoints.get(mPoints.size() - 1).timeOffsetNano; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index bd25209d5c0f..7bcbd3683130 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -32,6 +32,8 @@ import android.view.IWindowManager; import android.view.View; import android.view.WindowManagerGlobal; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.systemui.Dependency; @@ -213,8 +215,17 @@ public class NavigationBarController implements Callbacks { } /** @return {@link NavigationBarView} on the default display. */ - public NavigationBarView getDefaultNavigationBarView() { - NavigationBarFragment navBar = mNavigationBars.get(DEFAULT_DISPLAY); + public @Nullable NavigationBarView getDefaultNavigationBarView() { + return getNavigationBarView(DEFAULT_DISPLAY); + } + + /** + * @param displayId the ID of display which Navigation bar is on + * @return {@link NavigationBarView} on the display with {@code displayId}. + * {@code null} if no navigation bar on that display. + */ + public @Nullable NavigationBarView getNavigationBarView(int displayId) { + NavigationBarFragment navBar = mNavigationBars.get(displayId); return (navBar == null) ? null : (NavigationBarView) navBar.getView(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java index 0a2e04fd9430..b732966b32db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java @@ -97,7 +97,6 @@ public class MessagingLayoutTransformState extends TransformState { HashMap<MessagingGroup, MessagingGroup> pairs = findPairs(ownGroups, otherGroups); MessagingGroup lastPairedGroup = null; float currentTranslation = 0; - float transformationDistanceRemaining = 0; for (int i = ownGroups.size() - 1; i >= 0; i--) { MessagingGroup ownGroup = ownGroups.get(i); MessagingGroup matchingGroup = pairs.get(ownGroup); @@ -108,13 +107,10 @@ public class MessagingLayoutTransformState extends TransformState { lastPairedGroup = ownGroup; if (to){ float totalTranslation = ownGroup.getTop() - matchingGroup.getTop(); - transformationDistanceRemaining - = matchingGroup.getAvatar().getTranslationY(); - currentTranslation = transformationDistanceRemaining - totalTranslation; + currentTranslation = matchingGroup.getAvatar().getTranslationY() + - totalTranslation; } else { - float totalTranslation = matchingGroup.getTop() - ownGroup.getTop(); currentTranslation = ownGroup.getAvatar().getTranslationY(); - transformationDistanceRemaining = currentTranslation - totalTranslation; } } } else { @@ -122,14 +118,20 @@ public class MessagingLayoutTransformState extends TransformState { if (lastPairedGroup != null) { adaptGroupAppear(ownGroup, transformationAmount, currentTranslation, to); - int distance = lastPairedGroup.getTop() - ownGroup.getTop(); - float transformationDistance = mTransformInfo.isAnimating() - ? distance - : ownGroup.getHeight() * 0.75f; - float translationProgress = transformationDistanceRemaining - - (distance - transformationDistance); - groupTransformationAmount = - translationProgress / transformationDistance; + float newPosition = ownGroup.getTop() + currentTranslation; + + if (!mTransformInfo.isAnimating()) { + // We fade the group away as soon as 1/2 of it is translated away on top + float fadeStart = -ownGroup.getHeight() * 0.5f; + groupTransformationAmount = (newPosition - fadeStart) + / Math.abs(fadeStart); + } else { + float fadeStart = -ownGroup.getHeight() * 0.75f; + // We want to fade out as soon as the animation starts, let's add the + // complete top in addition + groupTransformationAmount = (newPosition - fadeStart) + / (Math.abs(fadeStart) + ownGroup.getTop()); + } groupTransformationAmount = Math.max(0.0f, Math.min(1.0f, groupTransformationAmount)); if (to) { @@ -175,7 +177,8 @@ public class MessagingLayoutTransformState extends TransformState { relativeOffset *= 0.5f; } ownGroup.getMessageContainer().setTranslationY(relativeOffset); - ownGroup.setTranslationY(overallTranslation * 0.85f); + ownGroup.getSenderView().setTranslationY(relativeOffset); + ownGroup.setTranslationY(overallTranslation * 0.9f); } private void disappear(MessagingGroup ownGroup, float transformationAmount) { @@ -256,6 +259,9 @@ public class MessagingLayoutTransformState extends TransformState { float distanceToTop = child.getTop() + child.getHeight() + previousTranslation; transformationAmount = distanceToTop / child.getHeight(); transformationAmount = Math.max(0.0f, Math.min(1.0f, transformationAmount)); + if (to) { + transformationAmount = 1.0f - transformationAmount; + } } transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */ useLinearTransformation); @@ -400,6 +406,7 @@ public class MessagingLayoutTransformState extends TransformState { setClippingDeactivated(ownGroup.getSenderView(), false); ownGroup.setTranslationY(0); ownGroup.getMessageContainer().setTranslationY(0); + ownGroup.getSenderView().setTranslationY(0); } ownGroup.setTransformingImages(false); ownGroup.updateClipRect(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index f9a98ad297f5..91c43a142db5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -206,7 +206,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi long actions = mMediaController.getPlaybackState().getActions(); Log.d(TAG, "Playback state actions are " + actions); - return (actions == 0 || (actions & PlaybackState.ACTION_SEEK_TO) != 0); + return ((actions & PlaybackState.ACTION_SEEK_TO) != 0); } protected final Runnable mUpdatePlaybackUi = new Runnable() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index ce922801551b..8c6d1015bd4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.stack; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.util.MathUtils; @@ -30,18 +31,18 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider; import java.util.ArrayList; -import java.util.List; /** * A global state to track all input states for the algorithm. */ public class AmbientState { - private static final int NO_SECTION_BOUNDARY = -1; private static final float MAX_PULSE_HEIGHT = 100000f; + private final SectionProvider mSectionProvider; private ArrayList<ExpandableView> mDraggedViews = new ArrayList<>(); private int mScrollY; private int mAnchorViewIndex; @@ -51,7 +52,6 @@ public class AmbientState { private float mOverScrollTopAmount; private float mOverScrollBottomAmount; private int mSpeedBumpIndex = -1; - private final List<Integer> mSectionBoundaryIndices = new ArrayList<>(); private boolean mDark; private boolean mHideSensitive; private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); @@ -84,8 +84,10 @@ public class AmbientState { private float mPulseHeight = MAX_PULSE_HEIGHT; private float mDozeAmount = 0.0f; - public AmbientState(Context context) { - mSectionBoundaryIndices.add(NO_SECTION_BOUNDARY); + public AmbientState( + Context context, + @NonNull SectionProvider sectionProvider) { + mSectionProvider = sectionProvider; reload(context); } @@ -245,25 +247,8 @@ public class AmbientState { mSpeedBumpIndex = shelfIndex; } - /** - * Returns the index of the boundary between two sections, where the first section is at index - * {@code boundaryNum}. - */ - public int getSectionBoundaryIndex(int boundaryNum) { - return mSectionBoundaryIndices.get(boundaryNum); - } - - /** Returns true if the item at {@code index} is directly below a section boundary. */ - public boolean beginsNewSection(int index) { - return mSectionBoundaryIndices.contains(index); - } - - /** - * Sets the index of the boundary between the section at {@code boundaryNum} and the following - * section to {@code boundaryIndex}. - */ - public void setSectionBoundaryIndex(int boundaryNum, int boundaryIndex) { - mSectionBoundaryIndices.set(boundaryNum, boundaryIndex); + public SectionProvider getSectionProvider() { + return mSectionProvider; } public float getStackTranslation() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java new file mode 100644 index 000000000000..82599f02ddf1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack; + +import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.provider.Settings; +import android.view.LayoutInflater; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; + +/** + * Manages the boundaries of the two notification sections (high priority and low priority). Also + * shows/hides the headers for those sections where appropriate. + * + * TODO: Move remaining sections logic from NSSL into this class. + */ +class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvider { + private final NotificationStackScrollLayout mParent; + private final ActivityStarter mActivityStarter; + private final boolean mUseMultipleSections; + + private SectionHeaderView mGentleHeader; + private boolean mGentleHeaderVisible = false; + @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener; + + NotificationSectionsManager( + NotificationStackScrollLayout parent, + ActivityStarter activityStarter, + boolean useMultipleSections) { + mParent = parent; + mActivityStarter = activityStarter; + mUseMultipleSections = useMultipleSections; + } + + /** + * Must be called before use. Should be called again whenever inflation-related things change, + * such as density or theme changes. + */ + void inflateViews(Context context) { + int oldPos = -1; + if (mGentleHeader != null) { + if (mGentleHeader.getTransientContainer() != null) { + mGentleHeader.getTransientContainer().removeView(mGentleHeader); + } else if (mGentleHeader.getParent() != null) { + oldPos = mParent.indexOfChild(mGentleHeader); + mParent.removeView(mGentleHeader); + } + } + + mGentleHeader = (SectionHeaderView) LayoutInflater.from(context).inflate( + R.layout.status_bar_notification_section_header, mParent, false); + mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick); + mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick); + + if (oldPos != -1) { + mParent.addView(mGentleHeader, oldPos); + } + } + + /** Listener for when the "clear all" buttton is clciked on the gentle notification header. */ + void setOnClearGentleNotifsClickListener(View.OnClickListener listener) { + mOnClearGentleNotifsClickListener = listener; + } + + /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */ + void onUiModeChanged() { + mGentleHeader.onUiModeChanged(); + } + + @Override + public boolean beginsSection(View view) { + return view == mGentleHeader; + } + + /** + * Should be called whenever notifs are added, removed, or updated. Updates section boundary + * bookkeeping and adds/moves/removes section headers if appropriate. + */ + void updateSectionBoundaries() { + if (!mUseMultipleSections) { + return; + } + + int firstGentleNotifIndex = -1; + + final int n = mParent.getChildCount(); + for (int i = 0; i < n; i++) { + View child = mParent.getChildAt(i); + if (child instanceof ExpandableNotificationRow + && child.getVisibility() != View.GONE) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (!row.getEntry().isHighPriority()) { + firstGentleNotifIndex = i; + break; + } + } + } + + adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex); + + mGentleHeader.setAreThereDismissableGentleNotifs( + mParent.hasActiveClearableNotifications(ROWS_GENTLE)); + } + + private void adjustGentleHeaderVisibilityAndPosition(int firstGentleNotifIndex) { + final int currentHeaderIndex = mParent.indexOfChild(mGentleHeader); + + if (firstGentleNotifIndex == -1) { + if (mGentleHeaderVisible) { + mGentleHeaderVisible = false; + mParent.removeView(mGentleHeader); + } + } else { + if (!mGentleHeaderVisible) { + mGentleHeaderVisible = true; + // If the header is animating away, it will still have a parent, so detach it first + // TODO: We should really cancel the active animations here. This will happen + // automatically when the view's intro animation starts, but it's a fragile link. + if (mGentleHeader.getTransientContainer() != null) { + mGentleHeader.getTransientContainer().removeTransientView(mGentleHeader); + mGentleHeader.setTransientContainer(null); + } + mParent.addView(mGentleHeader, firstGentleNotifIndex); + } else if (currentHeaderIndex != firstGentleNotifIndex - 1) { + // Relocate the header to be immediately before the first child in the section + int targetIndex = firstGentleNotifIndex; + if (currentHeaderIndex < firstGentleNotifIndex) { + // Adjust the target index to account for the header itself being temporarily + // removed during the position change. + targetIndex--; + } + + mParent.changeViewPosition(mGentleHeader, targetIndex); + } + } + } + + /** + * Updates the boundaries (as tracked by their first and last views) of the high and low + * priority sections. + * + * @return {@code true} If the last view in the top section changed (so we need to animate). + */ + boolean updateFirstAndLastViewsInSections( + final NotificationSection highPrioritySection, + final NotificationSection lowPrioritySection, + ActivatableNotificationView firstChild, + ActivatableNotificationView lastChild) { + if (mUseMultipleSections) { + ActivatableNotificationView previousLastHighPriorityChild = + highPrioritySection.getLastVisibleChild(); + ActivatableNotificationView previousFirstLowPriorityChild = + lowPrioritySection.getFirstVisibleChild(); + ActivatableNotificationView lastHighPriorityChild = getLastHighPriorityChild(); + ActivatableNotificationView firstLowPriorityChild = getFirstLowPriorityChild(); + if (lastHighPriorityChild != null && firstLowPriorityChild != null) { + highPrioritySection.setFirstVisibleChild(firstChild); + highPrioritySection.setLastVisibleChild(lastHighPriorityChild); + lowPrioritySection.setFirstVisibleChild(firstLowPriorityChild); + lowPrioritySection.setLastVisibleChild(lastChild); + } else if (lastHighPriorityChild != null) { + highPrioritySection.setFirstVisibleChild(firstChild); + highPrioritySection.setLastVisibleChild(lastChild); + lowPrioritySection.setFirstVisibleChild(null); + lowPrioritySection.setLastVisibleChild(null); + } else { + highPrioritySection.setFirstVisibleChild(null); + highPrioritySection.setLastVisibleChild(null); + lowPrioritySection.setFirstVisibleChild(firstChild); + lowPrioritySection.setLastVisibleChild(lastChild); + } + return lastHighPriorityChild != previousLastHighPriorityChild + || firstLowPriorityChild != previousFirstLowPriorityChild; + } else { + highPrioritySection.setFirstVisibleChild(firstChild); + highPrioritySection.setLastVisibleChild(lastChild); + return false; + } + } + + @VisibleForTesting + SectionHeaderView getGentleHeaderView() { + return mGentleHeader; + } + + @Nullable + private ActivatableNotificationView getFirstLowPriorityChild() { + return mGentleHeaderVisible ? mGentleHeader : null; + } + + @Nullable + private ActivatableNotificationView getLastHighPriorityChild() { + ActivatableNotificationView lastChildBeforeGap = null; + int childCount = mParent.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = mParent.getChildAt(i); + if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (!row.getEntry().isHighPriority()) { + break; + } else { + lastChildBeforeGap = row; + } + } + } + return lastChildBeforeGap; + } + + private void onGentleHeaderClick(View v) { + Intent intent = new Intent(Settings.ACTION_NOTIFICATION_SETTINGS); + mActivityStarter.startActivity( + intent, + true, + true, + Intent.FLAG_ACTIVITY_SINGLE_TOP); + } + + private void onClearGentleNotifsClick(View v) { + if (mOnClearGentleNotifsClickListener != null) { + mOnClearGentleNotifsClickListener.onClick(v); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 642e2e483d89..5bd6cab1809b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -23,10 +23,13 @@ import static com.android.systemui.statusbar.notification.stack.StackStateAnimat import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; +import static java.lang.annotation.RetentionPolicy.SOURCE; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.TimeAnimator; import android.animation.ValueAnimator; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WallpaperManager; @@ -87,6 +90,7 @@ import com.android.systemui.SwipeHelper; import com.android.systemui.classifier.FalsingManagerFactory; import com.android.systemui.classifier.FalsingManagerFactory.FalsingManager; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; @@ -142,6 +146,7 @@ import com.android.systemui.tuner.TunerService; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -498,6 +503,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private final NotificationGutsManager mNotificationGutsManager = Dependency.get(NotificationGutsManager.class); + private final NotificationSectionsManager mSectionsManager; /** * If the {@link NotificationShelf} should be visible when dark. */ @@ -511,7 +517,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, NotificationRoundnessManager notificationRoundnessManager, AmbientPulseManager ambientPulseManager, - DynamicPrivacyController dynamicPrivacyController) { + DynamicPrivacyController dynamicPrivacyController, + ActivityStarter activityStarter) { super(context, attrs, 0, 0); Resources res = getResources(); @@ -522,7 +529,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } mAmbientPulseManager = ambientPulseManager; - mAmbientState = new AmbientState(context); + + mSectionsManager = + new NotificationSectionsManager( + this, + activityStarter, + NotificationUtils.useNewInterruptionModel(context)); + mSectionsManager.inflateViews(context); + mSectionsManager.setOnClearGentleNotifsClickListener(v -> { + // Leave the shade open if there will be other notifs left over to clear + final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY); + clearNotifications(ROWS_GENTLE, closeShade); + }); + + mAmbientState = new AmbientState(context, mSectionsManager); mRoundnessManager = notificationRoundnessManager; mBgColor = context.getColor(R.color.notification_shade_background_color); int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); @@ -629,6 +649,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd inflateFooterView(); inflateEmptyShadeView(); updateFooter(); + mSectionsManager.inflateViews(mContext); } @Override @@ -662,7 +683,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @VisibleForTesting @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void updateFooter() { - boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(); + boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL); boolean showFooterView = (showDismissView || mEntryManager.getNotificationData().getActiveNotifications().size() != 0) && mStatusBarState != StatusBarState.KEYGUARD @@ -675,14 +696,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * Return whether there are any clearable notifications */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public boolean hasActiveClearableNotifications() { + public boolean hasActiveClearableNotifications(@SelectedRows int selection) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (!(child instanceof ExpandableNotificationRow)) { continue; } - if (((ExpandableNotificationRow) child).canViewBeDismissed()) { + final ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (row.canViewBeDismissed() && matchesSelection(row, selection)) { return true; } } @@ -739,6 +761,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mBgColor = mContext.getColor(R.color.notification_shade_background_color); updateBackgroundDimming(); mShelf.onUiModeChanged(); + mSectionsManager.onUiModeChanged(); } @ShadeViewRefactor(RefactorComponent.DECORATOR) @@ -1684,11 +1707,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return mScrollingEnabled; } - @ShadeViewRefactor(RefactorComponent.ADAPTER) - private boolean canChildBeDismissed(View v) { - return StackScrollAlgorithm.canChildBeDismissed(v); - } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private boolean onKeyguard() { return mStatusBarState == StatusBarState.KEYGUARD; @@ -2580,41 +2598,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return null; } - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - @Nullable - private ActivatableNotificationView getLastHighPriorityChild() { - ActivatableNotificationView lastChildBeforeGap = null; - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if (!row.getEntry().isHighPriority()) { - break; - } else { - lastChildBeforeGap = row; - } - } - } - return lastChildBeforeGap; - } - - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - @Nullable - private ActivatableNotificationView getFirstLowPriorityChild() { - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if (!row.getEntry().isHighPriority()) { - return row; - } - } - } - return null; - } - /** * Fling the scroll view * @@ -3180,7 +3163,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd ActivatableNotificationView firstChild = getFirstChildWithBackground(); ActivatableNotificationView lastChild = getLastChildWithBackground(); - boolean sectionViewsChanged = updateFirstAndLastViewsInSectionsByPriority( + boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsInSections( mSections[0], mSections[1], firstChild, lastChild); if (mAnimationsEnabled && mIsExpanded) { @@ -3198,44 +3181,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd invalidate(); } - /** @return {@code true} if the last view in the top section changed (so we need to animate). */ - private boolean updateFirstAndLastViewsInSectionsByPriority( - final NotificationSection highPrioritySection, - final NotificationSection lowPrioritySection, - ActivatableNotificationView firstChild, - ActivatableNotificationView lastChild) { - if (NotificationUtils.useNewInterruptionModel(mContext)) { - ActivatableNotificationView previousLastHighPriorityChild = - highPrioritySection.getLastVisibleChild(); - ActivatableNotificationView previousFirstLowPriorityChild = - lowPrioritySection.getFirstVisibleChild(); - ActivatableNotificationView lastHighPriorityChild = getLastHighPriorityChild(); - ActivatableNotificationView firstLowPriorityChild = getFirstLowPriorityChild(); - if (lastHighPriorityChild != null && firstLowPriorityChild != null) { - highPrioritySection.setFirstVisibleChild(firstChild); - highPrioritySection.setLastVisibleChild(lastHighPriorityChild); - lowPrioritySection.setFirstVisibleChild(firstLowPriorityChild); - lowPrioritySection.setLastVisibleChild(lastChild); - } else if (lastHighPriorityChild != null) { - highPrioritySection.setFirstVisibleChild(firstChild); - highPrioritySection.setLastVisibleChild(lastChild); - lowPrioritySection.setFirstVisibleChild(null); - lowPrioritySection.setLastVisibleChild(null); - } else { - highPrioritySection.setFirstVisibleChild(null); - highPrioritySection.setLastVisibleChild(null); - lowPrioritySection.setFirstVisibleChild(firstChild); - lowPrioritySection.setLastVisibleChild(lastChild); - } - return lastHighPriorityChild != previousLastHighPriorityChild - || firstLowPriorityChild != previousFirstLowPriorityChild; - } else { - highPrioritySection.setFirstVisibleChild(firstChild); - highPrioritySection.setLastVisibleChild(lastChild); - return false; - } - } - @ShadeViewRefactor(RefactorComponent.COORDINATOR) private void onViewAddedInternal(ExpandableView child) { updateHideSensitiveForChild(child); @@ -4595,11 +4540,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return mAmbientState.isDimmed(); } - @VisibleForTesting - int getSectionBoundaryIndex(int boundaryNum) { - return mAmbientState.getSectionBoundaryIndex(boundaryNum); - } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private void setDimAmount(float dimAmount) { mDimAmount = dimAmount; @@ -4984,7 +4924,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } else { child.setMinClipTopAmount(0); } - previousChildWillBeDismissed = canChildBeDismissed(child); + previousChildWillBeDismissed = StackScrollAlgorithm.canChildBeDismissed(child); } } @@ -5540,7 +5480,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void clearAllNotifications() { + private void clearNotifications( + @SelectedRows int selection, + boolean closeShade) { // animate-swipe all dismissable notifications, then animate the shade closed int numChildren = getChildCount(); @@ -5552,7 +5494,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd ExpandableNotificationRow row = (ExpandableNotificationRow) child; boolean parentVisible = false; boolean hasClipBounds = child.getClipBounds(mTmpRect); - if (canChildBeDismissed(child)) { + if (includeChildInDismissAll(row, selection)) { viewsToRemove.add(row); if (child.getVisibility() == View.VISIBLE && (!hasClipBounds || mTmpRect.height() > 0)) { @@ -5566,51 +5508,94 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd List<ExpandableNotificationRow> children = row.getNotificationChildren(); if (children != null) { for (ExpandableNotificationRow childRow : children) { - viewsToRemove.add(childRow); - if (parentVisible && row.areChildrenExpanded() - && canChildBeDismissed(childRow)) { - hasClipBounds = childRow.getClipBounds(mTmpRect); - if (childRow.getVisibility() == View.VISIBLE - && (!hasClipBounds || mTmpRect.height() > 0)) { - viewsToHide.add(childRow); + if (includeChildInDismissAll(row, selection)) { + viewsToRemove.add(childRow); + if (parentVisible && row.areChildrenExpanded()) { + hasClipBounds = childRow.getClipBounds(mTmpRect); + if (childRow.getVisibility() == View.VISIBLE + && (!hasClipBounds || mTmpRect.height() > 0)) { + viewsToHide.add(childRow); + } } } } } } } + if (viewsToRemove.isEmpty()) { - mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + if (closeShade) { + mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + } return; } - mShadeController.addPostCollapseAction(() -> { - setDismissAllInProgress(false); + performDismissAllAnimations(viewsToHide, closeShade, () -> { for (ExpandableNotificationRow rowToRemove : viewsToRemove) { - if (canChildBeDismissed(rowToRemove)) { - mEntryManager.removeNotification(rowToRemove.getEntry().key, null /* ranking */, - NotificationListenerService.REASON_CANCEL_ALL); + if (StackScrollAlgorithm.canChildBeDismissed(rowToRemove)) { + if (selection == ROWS_ALL) { + // TODO: This is a listener method; we shouldn't be calling it. Can we just + // call performRemoveNotification as below? + mEntryManager.removeNotification( + rowToRemove.getEntry().key, + null /* ranking */, + NotificationListenerService.REASON_CANCEL_ALL); + } else { + mEntryManager.performRemoveNotification( + rowToRemove.getEntry().notification, + NotificationListenerService.REASON_CANCEL_ALL); + } } else { rowToRemove.resetTranslation(); } } - try { - mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId()); - } catch (Exception ex) { + if (selection == ROWS_ALL) { + try { + mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId()); + } catch (Exception ex) { + } } }); + } - performDismissAllAnimations(viewsToHide); + private boolean includeChildInDismissAll( + ExpandableNotificationRow row, + @SelectedRows int selection) { + return StackScrollAlgorithm.canChildBeDismissed(row) && matchesSelection(row, selection); } + /** + * Given a list of rows, animates them away in a staggered fashion as if they were dismissed. + * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete + * handler. + * + * @param hideAnimatedList List of rows to animated away. Should only be views that are + * currently visible, or else the stagger will look funky. + * @param closeShade Whether to close the shade after the stagger animation completes. + * @param onAnimationComplete Called after the entire animation completes (including the shade + * closing if appropriate). The rows must be dismissed for real here. + */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void performDismissAllAnimations(ArrayList<View> hideAnimatedList) { - Runnable animationFinishAction = () -> { - mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + private void performDismissAllAnimations( + final ArrayList<View> hideAnimatedList, + final boolean closeShade, + final Runnable onAnimationComplete) { + + final Runnable onSlideAwayAnimationComplete = () -> { + if (closeShade) { + mShadeController.addPostCollapseAction(() -> { + setDismissAllInProgress(false); + onAnimationComplete.run(); + }); + mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + } else { + setDismissAllInProgress(false); + onAnimationComplete.run(); + } }; if (hideAnimatedList.isEmpty()) { - animationFinishAction.run(); + onSlideAwayAnimationComplete.run(); return; } @@ -5627,7 +5612,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd View view = hideAnimatedList.get(i); Runnable endRunnable = null; if (i == 0) { - endRunnable = animationFinishAction; + endRunnable = onSlideAwayAnimationComplete; } dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE); currentDelay = Math.max(50, currentDelay - rowDelayDecrement); @@ -5642,7 +5627,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd R.layout.status_bar_notification_footer, this, false); footerView.setDismissButtonClickListener(v -> { mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES); - clearAllNotifications(); + clearNotifications(ROWS_ALL, true /* closeShade */); }); footerView.setManageButtonClickListener(this::manageNotifications); setFooterView(footerView); @@ -5813,27 +5798,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd /** Updates the indices of the boundaries between sections. */ @ShadeViewRefactor(RefactorComponent.INPUT) public void updateSectionBoundaries() { - int gapIndex = -1; - if (NotificationUtils.useNewInterruptionModel(mContext)) { - int currentIndex = 0; - final int n = getChildCount(); - for (int i = 0; i < n; i++) { - View view = getChildAt(i); - if (view.getVisibility() == View.GONE - || !(view instanceof ExpandableNotificationRow)) { - continue; - } - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - if (!row.getEntry().isHighPriority()) { - if (currentIndex > 0) { - gapIndex = currentIndex; - } - break; - } - currentIndex++; - } - } - mAmbientState.setSectionBoundaryIndex(0, gapIndex); + mSectionsManager.updateSectionBoundaries(); } private void updateContinuousBackgroundDrawing() { @@ -5869,6 +5834,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mSwipeHelper.resetExposedMenuView(animate, force); } + private static boolean matchesSelection( + ExpandableNotificationRow row, + @SelectedRows int selection) { + switch (selection) { + case ROWS_ALL: + return true; + case ROWS_HIGH_PRIORITY: + return row.getEntry().isHighPriority(); + case ROWS_GENTLE: + return !row.getEntry().isHighPriority(); + default: + throw new IllegalArgumentException("Unknown selection: " + selection); + } + } + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) static class AnimationEvent { @@ -6353,7 +6333,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public boolean canChildBeDismissed(View v) { - return NotificationStackScrollLayout.this.canChildBeDismissed(v); + return StackScrollAlgorithm.canChildBeDismissed(v); } @Override @@ -6560,4 +6540,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd public ExpandHelper.Callback getExpandHelperCallback() { return mExpandHelperCallback; } + + /** Enum for selecting some or all notification rows (does not included non-notif views). */ + @Retention(SOURCE) + @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE}) + public @interface SelectedRows {} + /** All rows representing notifs. */ + public static final int ROWS_ALL = 0; + /** Only rows where entry.isHighPriority() is true. */ + public static final int ROWS_HIGH_PRIORITY = 1; + /** Only rows where entry.isHighPriority() is false. */ + public static final int ROWS_GENTLE = 2; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java new file mode 100644 index 000000000000..e2f702dcb732 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack; + +import android.content.Context; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.systemui.R; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; + +/** + * Similar in size and appearance to the NotificationShelf, appears at the beginning of some + * notification sections. Currently only used for gentle notifications. + */ +public class SectionHeaderView extends ActivatableNotificationView { + private View mContents; + private TextView mLabelView; + private ImageView mClearAllButton; + + private final RectF mTmpRect = new RectF(); + + public SectionHeaderView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mContents = findViewById(R.id.content); + mLabelView = findViewById(R.id.header_label); + mClearAllButton = findViewById(R.id.btn_clear_all); + } + + @Override + protected View getContentView() { + return mContents; + } + + /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */ + void onUiModeChanged() { + updateBackgroundColors(); + mLabelView.setTextColor( + getContext().getColor(R.color.notification_section_header_label_color)); + mClearAllButton.setImageResource( + R.drawable.status_bar_notification_section_header_clear_btn); + } + + void setAreThereDismissableGentleNotifs(boolean areThereDismissableGentleNotifs) { + mClearAllButton.setVisibility(areThereDismissableGentleNotifs ? View.VISIBLE : View.GONE); + } + + @Override + protected boolean disallowSingleClick(MotionEvent event) { + // Disallow single click on lockscreen if user is tapping on clear all button + mTmpRect.set( + mClearAllButton.getLeft(), + mClearAllButton.getTop(), + mClearAllButton.getLeft() + mClearAllButton.getWidth(), + mClearAllButton.getTop() + mClearAllButton.getHeight()); + return mTmpRect.contains(event.getX(), event.getY()); + } + + /** + * Fired whenever the user clicks on the body of the header (e.g. no sub-buttons or anything). + */ + void setOnHeaderClickListener(View.OnClickListener listener) { + mContents.setOnClickListener(listener); + } + + /** Fired when the user clicks on the "X" button on the far right of the header. */ + void setOnClearAllClickListener(View.OnClickListener listener) { + mClearAllButton.setOnClickListener(listener); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index f97a7e653104..60061c6a9ad2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -58,7 +58,9 @@ public class StackScrollAlgorithm { private float mHeadsUpInset; private int mPinnedZTranslationExtra; - public StackScrollAlgorithm(Context context, ViewGroup hostView) { + public StackScrollAlgorithm( + Context context, + ViewGroup hostView) { mHostView = hostView; initView(context); } @@ -364,22 +366,15 @@ public class StackScrollAlgorithm { */ private void updatePositionsForState(StackScrollAlgorithmState algorithmState, AmbientState ambientState) { - if (ANCHOR_SCROLLING) { float currentYPosition = algorithmState.anchorViewY; int childCount = algorithmState.visibleChildren.size(); for (int i = algorithmState.anchorViewIndex; i < childCount; i++) { - if (i > algorithmState.anchorViewIndex && ambientState.beginsNewSection(i)) { - currentYPosition += mGapHeight; - } currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition, false /* reverse */); } currentYPosition = algorithmState.anchorViewY; for (int i = algorithmState.anchorViewIndex - 1; i >= 0; i--) { - if (ambientState.beginsNewSection(i + 1)) { - currentYPosition -= mGapHeight; - } currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition, true /* reverse */); } @@ -388,9 +383,6 @@ public class StackScrollAlgorithm { float currentYPosition = -algorithmState.scrollY; int childCount = algorithmState.visibleChildren.size(); for (int i = 0; i < childCount; i++) { - if (ambientState.beginsNewSection(i)) { - currentYPosition += mGapHeight; - } currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition, false /* reverse */); } @@ -421,8 +413,15 @@ public class StackScrollAlgorithm { float currentYPosition, boolean reverse) { ExpandableView child = algorithmState.visibleChildren.get(i); + final boolean applyGapHeight = + childNeedsGapHeight(ambientState.getSectionProvider(), algorithmState, i, child); ExpandableViewState childViewState = child.getViewState(); childViewState.location = ExpandableViewState.LOCATION_UNKNOWN; + + if (applyGapHeight && !reverse) { + currentYPosition += mGapHeight; + } + int paddingAfterChild = getPaddingAfterChild(algorithmState, child); int childHeight = getMaxAllowedChildHeight(child); if (reverse) { @@ -459,6 +458,9 @@ public class StackScrollAlgorithm { if (reverse) { currentYPosition = childViewState.yTranslation; + if (applyGapHeight) { + currentYPosition -= mGapHeight; + } } else { currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild; if (currentYPosition <= 0) { @@ -473,6 +475,18 @@ public class StackScrollAlgorithm { return currentYPosition; } + private boolean childNeedsGapHeight( + SectionProvider sectionProvider, + StackScrollAlgorithmState algorithmState, + int visibleIndex, + View child) { + boolean needsGapHeight = sectionProvider.beginsSection(child) && visibleIndex > 0; + if (ANCHOR_SCROLLING) { + needsGapHeight &= visibleIndex != algorithmState.anchorViewIndex; + } + return needsGapHeight; + } + protected int getPaddingAfterChild(StackScrollAlgorithmState algorithmState, ExpandableView child) { return algorithmState.getPaddingAfterChild(child); @@ -727,4 +741,15 @@ public class StackScrollAlgorithm { } } + /** + * Interface for telling the SSA when a new notification section begins (so it can add in + * appropriate margins). + */ + public interface SectionProvider { + /** + * True if this view starts a new "section" of notifications, such as the gentle + * notifications section. False if sections are not enabled. + */ + boolean beginsSection(View view); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 9d24e1efc66a..168813a02252 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.SysUiServiceProvider.getComponent; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; +import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.animation.Animator; @@ -2992,7 +2993,7 @@ public class NotificationPanelView extends PanelView implements } public boolean hasActiveClearableNotifications() { - return mNotificationStackScroller.hasActiveClearableNotifications(); + return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c01367a4d213..776be00f8ea4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3487,7 +3487,7 @@ public class StatusBar extends SystemUI implements DemoMode, // TODO: Figure out way to remove these. public NavigationBarView getNavigationBarView() { - return mNavigationBarController.getDefaultNavigationBarView(); + return mNavigationBarController.getNavigationBarView(mDisplayId); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java new file mode 100644 index 000000000000..67c4a92bbb02 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack; + +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.ActivityStarterDelegate; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class NotificationSectionsManagerTest extends SysuiTestCase { + + @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock private NotificationStackScrollLayout mNssl; + @Mock private ActivityStarterDelegate mActivityStarterDelegate; + + private NotificationSectionsManager mSectionsManager; + + @Before + public void setUp() { + mSectionsManager = new NotificationSectionsManager(mNssl, mActivityStarterDelegate, true); + // Required in order for the header inflation to work properly + when(mNssl.generateLayoutParams(any(AttributeSet.class))) + .thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + mSectionsManager.inflateViews(mContext); + when(mNssl.indexOfChild(any(View.class))).thenReturn(-1); + } + + @Test + public void testInsertHeader() { + // GIVEN a stack with HI and LO rows but no section headers + setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); + + // WHEN we update the section headers + mSectionsManager.updateSectionBoundaries(); + + // THEN a LO section header is added + verify(mNssl).addView(mSectionsManager.getGentleHeaderView(), 3); + } + + @Test + public void testRemoveHeader() { + // GIVEN a stack that originally had a header between the HI and LO sections + setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); + mSectionsManager.updateSectionBoundaries(); + + // WHEN the last LO row is replaced with a HI row + setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HEADER, ChildType.HIPRI); + clearInvocations(mNssl); + mSectionsManager.updateSectionBoundaries(); + + // THEN the LO section header is removed + verify(mNssl).removeView(mSectionsManager.getGentleHeaderView()); + } + + @Test + public void testDoNothingIfHeaderAlreadyRemoved() { + // GIVEN a stack with only HI rows + setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI); + + // WHEN we update the sections headers + mSectionsManager.updateSectionBoundaries(); + + // THEN we don't add any section headers + verify(mNssl, never()).addView(eq(mSectionsManager.getGentleHeaderView()), anyInt()); + } + + @Test + public void testMoveHeaderForward() { + // GIVEN a stack that originally had a header between the HI and LO sections + setStackState( + ChildType.HIPRI, + ChildType.HIPRI, + ChildType.HIPRI, + ChildType.LOPRI); + mSectionsManager.updateSectionBoundaries(); + + // WHEN the LO section moves forward + setStackState( + ChildType.HIPRI, + ChildType.HIPRI, + ChildType.LOPRI, + ChildType.HEADER, + ChildType.LOPRI); + mSectionsManager.updateSectionBoundaries(); + + // THEN the LO section header is also moved forward + verify(mNssl).changeViewPosition(mSectionsManager.getGentleHeaderView(), 2); + } + + @Test + public void testMoveHeaderBackward() { + // GIVEN a stack that originally had a header between the HI and LO sections + setStackState( + ChildType.HIPRI, + ChildType.LOPRI, + ChildType.LOPRI, + ChildType.LOPRI); + mSectionsManager.updateSectionBoundaries(); + + // WHEN the LO section moves backward + setStackState( + ChildType.HIPRI, + ChildType.HEADER, + ChildType.HIPRI, + ChildType.HIPRI, + ChildType.LOPRI); + mSectionsManager.updateSectionBoundaries(); + + // THEN the LO section header is also moved backward (with appropriate index shifting) + verify(mNssl).changeViewPosition(mSectionsManager.getGentleHeaderView(), 3); + } + + @Test + public void testHeaderRemovedFromTransientParent() { + // GIVEN a stack where the header is animating away + setStackState( + ChildType.HIPRI, + ChildType.LOPRI, + ChildType.LOPRI, + ChildType.LOPRI); + mSectionsManager.updateSectionBoundaries(); + setStackState( + ChildType.HIPRI, + ChildType.HEADER); + mSectionsManager.updateSectionBoundaries(); + clearInvocations(mNssl); + + ViewGroup transientParent = mock(ViewGroup.class); + mSectionsManager.getGentleHeaderView().setTransientContainer(transientParent); + + // WHEN the LO section reappears + setStackState( + ChildType.HIPRI, + ChildType.LOPRI); + mSectionsManager.updateSectionBoundaries(); + + // THEN the header is first removed from the transient parent before being added to the + // NSSL. + verify(transientParent).removeTransientView(mSectionsManager.getGentleHeaderView()); + verify(mNssl).addView(mSectionsManager.getGentleHeaderView(), 1); + } + + private enum ChildType { HEADER, HIPRI, LOPRI } + + private void setStackState(ChildType... children) { + when(mNssl.getChildCount()).thenReturn(children.length); + for (int i = 0; i < children.length; i++) { + View child; + switch (children[i]) { + case HEADER: + child = mSectionsManager.getGentleHeaderView(); + break; + case HIPRI: + case LOPRI: + ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class, + RETURNS_DEEP_STUBS); + when(notifRow.getVisibility()).thenReturn(View.VISIBLE); + when(notifRow.getEntry().isHighPriority()) + .thenReturn(children[i] == ChildType.HIPRI); + when(notifRow.getParent()).thenReturn(mNssl); + child = notifRow; + break; + default: + throw new RuntimeException("Unknown ChildType: " + children[i]); + } + when(mNssl.getChildAt(i)).thenReturn(child); + when(mNssl.indexOfChild(child)).thenReturn(i); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 04f911a2b0e8..09b8062390bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -46,6 +46,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.systemui.ActivityStarterDelegate; import com.android.systemui.Dependency; import com.android.systemui.ExpandHelper; import com.android.systemui.InitController; @@ -118,6 +119,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Before @UiThreadTest public void setUp() throws Exception { + mOriginalInterruptionModelSetting = Settings.Secure.getInt(mContext.getContentResolver(), + NOTIFICATION_NEW_INTERRUPTION_MODEL, 0); + Settings.Secure.putInt(mContext.getContentResolver(), + NOTIFICATION_NEW_INTERRUPTION_MODEL, 1); + // Inject dependencies before initializing the layout mDependency.injectTestDependency( NotificationBlockingHelperManager.class, @@ -146,7 +152,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null, true /* allowLongPress */, mNotificationRoundnessManager, new AmbientPulseManager(mContext), - mock(DynamicPrivacyController.class)); + mock(DynamicPrivacyController.class), + mock(ActivityStarterDelegate.class)); mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelf(notificationShelf); mStackScroller.setStatusBar(mBar); @@ -166,11 +173,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { doNothing().when(mExpandHelper).cancelImmediately(); doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean()); doNothing().when(notificationShelf).fadeInTranslating(); - - mOriginalInterruptionModelSetting = Settings.Secure.getInt(mContext.getContentResolver(), - NOTIFICATION_NEW_INTERRUPTION_MODEL, 0); - Settings.Secure.putInt(mContext.getContentResolver(), - NOTIFICATION_NEW_INTERRUPTION_MODEL, 1); } @After @@ -350,62 +352,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void testUpdateGapIndex_allHighPriority() { - when(mStackScroller.getChildCount()).thenReturn(3); - for (int i = 0; i < 3; i++) { - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, - RETURNS_DEEP_STUBS); - String key = Integer.toString(i); - when(row.getStatusBarNotification().getKey()).thenReturn(key); - when(row.getEntry().isHighPriority()).thenReturn(true); - when(mStackScroller.getChildAt(i)).thenReturn(row); - } - - mStackScroller.updateSectionBoundaries(); - assertEquals(-1, mStackScroller.getSectionBoundaryIndex(0)); - } - - @Test - public void testUpdateGapIndex_allLowPriority() { - when(mStackScroller.getChildCount()).thenReturn(3); - for (int i = 0; i < 3; i++) { - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, - RETURNS_DEEP_STUBS); - String key = Integer.toString(i); - when(row.getStatusBarNotification().getKey()).thenReturn(key); - when(row.getEntry().isHighPriority()).thenReturn(false); - when(mStackScroller.getChildAt(i)).thenReturn(row); - } - - mStackScroller.updateSectionBoundaries(); - assertEquals(-1, mStackScroller.getSectionBoundaryIndex(0)); - } - - @Test - public void testUpdateGapIndex_gapExists() { - when(mStackScroller.getChildCount()).thenReturn(6); - for (int i = 0; i < 6; i++) { - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, - RETURNS_DEEP_STUBS); - String key = Integer.toString(i); - when(row.getStatusBarNotification().getKey()).thenReturn(key); - when(row.getEntry().isHighPriority()).thenReturn(i < 3); - when(mStackScroller.getChildAt(i)).thenReturn(row); - } - - mStackScroller.updateSectionBoundaries(); - assertEquals(3, mStackScroller.getSectionBoundaryIndex(0)); - } - - @Test - public void testUpdateGapIndex_empty() { - when(mStackScroller.getChildCount()).thenReturn(0); - - mStackScroller.updateSectionBoundaries(); - assertEquals(-1, mStackScroller.getSectionBoundaryIndex(0)); - } - - @Test public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() { clearInvocations(mStackScroller); mStackScroller.onDensityOrFontScaleChanged(); diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 65338cb2126f..e30e166a46b5 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -263,6 +263,14 @@ message MetricsEvent { PREVIOUSLY_VISIBLE = 2; } + // Types for ACTION_SHORTCUTS_CHANGED + enum ShortcutsChangesInfo { + SHORTCUTS_CHANGED_UNKNOWN = 0; + SHORTCUTS_CHANGED_USER_ID = 1; + SHORTCUTS_CHANGED_PACKAGE_COUNT = 2; + SHORTCUTS_CHANGED_SHORTCUT_COUNT = 3; + } + // Explanations for notification importance, derived from // NotificationRecord.mImportanceExplanation. enum NotificationImportanceExplanation { @@ -4183,6 +4191,8 @@ message MetricsEvent { // Tag FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode. // Tag FIELD_AUTOFILL_NUMBER_REQUESTS: number of requests made to the service (each request // is logged by a separate AUTOFILL_REQUEST metric) + // NOTE: starting on OS Q, it also added the following fields: + // TAg FIELD_AUTOFILL_AUGMENTED_ONLY: if the session was used just for augmented autofill AUTOFILL_SESSION_FINISHED = 919; // meta-event: a reader has checkpointed the log here. @@ -7222,6 +7232,64 @@ message MetricsEvent { // OS: Q ASSISTANT = 1716; + // ACTION: Published shortcuts in ShortcutManager changed + // TYPE: All the SHORTCUTS_CHANGED_* values in ShortcutsChangesInfo + // OS: Q + ACTION_SHORTCUTS_CHANGED = 1717; + + // ACTION: Direct share targets loaded via ShortcutManager + // OS: Q + ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER = 1718; + + // ACTION: Direct share targets loaded via ChooserService + // OS: Q + ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE = 1719; + + // Field indicating that an autofill session was created just for augmented autofill purposes. + // OS: Q + // Value: 1 for true, absent when false + FIELD_AUTOFILL_AUGMENTED_ONLY = 1720; + + // The augmented autofill service set its whitelisted packages and activities. + // OS: Q + // Tag FIELD_AUTOFILL_SERVICE: Package of the augmented autofill service that processed the + // request + // Tag FIELD_AUTOFILL_NUMBER_PACKAGES: Number of whitelisted packages. + // Tag FIELD_AUTOFILL_NUMBER_ACTIVITIES: Number of whitelisted activities. + AUTOFILL_AUGMENTED_WHITELIST_REQUEST = 1721; + + // Generic field used to indicate the number of packages in an Autofill metric (typically a + // whitelist request). + // OS: Q + FIELD_AUTOFILL_NUMBER_PACKAGES = 1722; + + // Generic field used to indicate the number of activities in an Autofill metric (typically a + // whitelist request). + // OS: Q + FIELD_AUTOFILL_NUMBER_ACTIVITIES = 1723; + + // Reports the result of a request made to the augmented autofill service + // OS: Q + // Type TYPE_UNKNOWN: if the type of response could not be determined + // Type TYPE_SUCCESS: service called onSucess() passing null + // Type TYPE_OPEN: service shown the UI + // Type TYPE_CLOSE: service hid the UI + // Type TYPE_ERROR: service timed out responding + + // Tag FIELD_CLASS_NAME: Class name of the activity that is autofilled. + // Tag FIELD_AUTOFILL_SERVICE: Package of the augmented autofill service that processed the + // request + // Tag FIELD_AUTOFILL_SESSION_ID: id of the autofill session associated with this metric + // Tag FIELD_AUTOFILL_DURATION: how long it took (in ms) to the service to respond, or -1 if the + // type of response could not be determined + AUTOFILL_AUGMENTED_RESPONSE = 1724; + + // ---- Skipping ahead to avoid conflicts between master and release branches. + // OPEN: Settings > System > Gestures > Global Actions Panel + // CATEGORY: SETTINGS + // OS: Q + GLOBAL_ACTIONS_PANEL_SETTINGS = 1800; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 386dec472019..6bb1cfaf11c2 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -1177,8 +1177,8 @@ final class AutofillManagerServiceImpl * @return whether caller UID is the augmented autofill service for the user */ @GuardedBy("mLock") - boolean setAugmentedAutofillWhitelistLocked(List<String> packages, - List<ComponentName> activities, int callingUid) { + boolean setAugmentedAutofillWhitelistLocked(@Nullable List<String> packages, + @Nullable List<ComponentName> activities, int callingUid) { if (!isCalledByAugmentedAutofillServiceLocked("setAugmentedAutofillWhitelistLocked", callingUid)) { @@ -1189,8 +1189,25 @@ final class AutofillManagerServiceImpl + activities + ")"); } whitelistForAugmentedAutofillPackages(packages, activities); + final String serviceName; + if (mRemoteAugmentedAutofillServiceInfo != null) { + serviceName = mRemoteAugmentedAutofillServiceInfo.getComponentName() + .flattenToShortString(); + } else { + Slog.e(TAG, "setAugmentedAutofillWhitelistLocked(): no service"); + serviceName = "N/A"; + } + + final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_AUGMENTED_WHITELIST_REQUEST) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, serviceName); + if (packages != null) { + log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_PACKAGES, packages.size()); + } + if (activities != null) { + log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_ACTIVITIES, activities.size()); + } + mMetricsLogger.write(log); - // TODO(b/122858578): log metrics return true; } @@ -1233,7 +1250,6 @@ final class AutofillManagerServiceImpl } /** - * * @throws IllegalArgumentException if packages or components are empty. */ private void whitelistForAugmentedAutofillPackages(@Nullable List<String> packages, diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 609904b32230..d2b71e591b22 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -16,6 +16,8 @@ package com.android.server.autofill; +import static android.service.autofill.augmented.Helper.logResponse; + import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; @@ -43,6 +45,7 @@ import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import com.android.internal.infra.AbstractSinglePendingRequestRemoteService; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IResultReceiver; final class RemoteAugmentedAutofillService @@ -173,6 +176,7 @@ final class RemoteAugmentedAutofillService private final @Nullable AutofillValue mFocusedValue; private final @NonNull IAutoFillManagerClient mClient; private final @NonNull ComponentName mActivityComponent; + private final int mSessionId; private final int mTaskId; private final long mRequestTime = SystemClock.elapsedRealtime(); private final @NonNull IFillCallback mCallback; @@ -184,6 +188,7 @@ final class RemoteAugmentedAutofillService @Nullable AutofillValue focusedValue) { super(service, sessionId); mClient = client; + mSessionId = sessionId; mTaskId = taskId; mActivityComponent = activityComponent; mFocusedId = focusedId; @@ -283,6 +288,8 @@ final class RemoteAugmentedAutofillService remoteService.dispatchOnFillTimeout(cancellation); } finish(); + logResponse(MetricsEvent.TYPE_ERROR, remoteService.getComponentName().getPackageName(), + mActivityComponent, mSessionId, remoteService.mRequestTimeoutMs); } @Override diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 1a0353cde8ce..e61fa326215f 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -570,7 +570,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState, int flags) { if (mForAugmentedAutofillOnly) { - // TODO(b/122858578): log metrics if (sVerbose) { Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead " + "(mForAugmentedAutofillOnly=" + mForAugmentedAutofillOnly @@ -3253,6 +3252,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, totalAugmentedRequests); } + if (mForAugmentedAutofillOnly) { + log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_AUGMENTED_ONLY, 1); + } mMetricsLogger.write(log); return mRemoteFillService; diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 1220e82485e7..b66de22201b8 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1764,8 +1764,7 @@ class AlarmManagerService extends SystemService { + ", callingPackage: " + callingPackage; // STOPSHIP (b/128866264): Just to catch breakages. Remove before final release. Slog.wtf(TAG, errorMsg); - // TODO b/129995049: Resume throwing after some soak time without errors - // throw new UnsupportedOperationException(errorMsg); + throw new UnsupportedOperationException(errorMsg); } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, interval, operation, directReceiver, listenerTag, flags, true, workSource, diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 7b5b419a7aa0..103a0c7bafdb 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -529,8 +529,7 @@ public class PackageWatchdog { while (pit.hasNext()) { MonitoredPackage monitoredPackage = pit.next(); String packageName = monitoredPackage.getName(); - if (monitoredPackage.getHealthCheckStateLocked() - != MonitoredPackage.STATE_PASSED) { + if (monitoredPackage.isPendingHealthChecksLocked()) { packages.add(packageName); } } @@ -1093,7 +1092,10 @@ public class PackageWatchdog { */ @GuardedBy("mLock") public long getShortestScheduleDurationMsLocked() { - return Math.min(toPositive(mDurationMs), toPositive(mHealthCheckDurationMs)); + // Consider health check duration only if #isPendingHealthChecksLocked is true + return Math.min(toPositive(mDurationMs), + isPendingHealthChecksLocked() + ? toPositive(mHealthCheckDurationMs) : Long.MAX_VALUE); } /** @@ -1106,6 +1108,15 @@ public class PackageWatchdog { } /** + * Returns {@code true} if the package, {@link #getName} is expecting health check results + * {@code false} otherwise. + */ + @GuardedBy("mLock") + public boolean isPendingHealthChecksLocked() { + return mHealthCheckState == STATE_ACTIVE || mHealthCheckState == STATE_INACTIVE; + } + + /** * Updates the health check state based on {@link #mHasPassedHealthCheck} * and {@link #mHealthCheckDurationMs}. * diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 28bc34859e6c..225c08092b49 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -342,7 +342,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { Integer newDefaultSubIdObj = new Integer(intent.getIntExtra( PhoneConstants.SUBSCRIPTION_KEY, SubscriptionManager.getDefaultSubscriptionId())); - int newDefaultPhoneId = intent.getIntExtra(PhoneConstants.SLOT_KEY, + int newDefaultPhoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, SubscriptionManager.getPhoneId(mDefaultSubId)); if (DBG) { log("onReceive:current mDefaultSubId=" + mDefaultSubId @@ -1935,8 +1935,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mEmergencyNumberList=" + mEmergencyNumberList); pw.println("mCallQuality=" + mCallQuality); pw.println("mCallAttributes=" + mCallAttributes); - pw.println("mDefaultPhoneId" + mDefaultPhoneId); - pw.println("mDefaultSubId" + mDefaultSubId); + pw.println("mDefaultPhoneId=" + mDefaultPhoneId); + pw.println("mDefaultSubId=" + mDefaultSubId); pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 46c8ce77d7cf..4d0d3d2dc578 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -272,7 +272,6 @@ import android.os.UserManager; import android.os.WorkSource; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; -import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.Settings; import android.sysprop.VoldProperties; @@ -5737,17 +5736,6 @@ public class ActivityManagerService extends IActivityManager.Stub owningUid, exported); } - @Override - public int checkPermissionWithDenialHintForwarding(String permission, int pid, int uid, - List<String> permissionDenialHints) { - List<String> prev = PermissionManager.resetPermissionDenialHints(permissionDenialHints); - try { - return checkPermission(permission, pid, uid); - } finally { - PermissionManager.resetPermissionDenialHints(prev); - } - } - /** * As the only public entry point for permissions checking, this method * can enforce the semantic that requesting a check on a null global @@ -5760,7 +5748,6 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkPermission(String permission, int pid, int uid) { if (permission == null) { - PermissionManager.addPermissionDenialHint("Permission is null"); return PackageManager.PERMISSION_DENIED; } return checkComponentPermission(permission, pid, uid, -1, true); diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index f599adb5118a..85fb1e0f4bdf 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -473,6 +473,20 @@ public final class ColorDisplayService extends SystemService { onDisplayColorModeChanged(getColorModeInternal()); } + private boolean isAccessiblityDaltonizerEnabled() { + return Secure.getIntForUser(getContext().getContentResolver(), + Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0; + } + + private boolean isAccessiblityInversionEnabled() { + return Secure.getIntForUser(getContext().getContentResolver(), + Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0; + } + + private boolean isAccessibilityEnabled() { + return isAccessiblityDaltonizerEnabled() || isAccessiblityInversionEnabled(); + } + /** * Apply the accessibility daltonizer transform based on the settings value. */ @@ -480,11 +494,10 @@ public final class ColorDisplayService extends SystemService { if (mCurrentUser == UserHandle.USER_NULL) { return; } - final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(), - Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0; - final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(), - Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser) + final int daltonizerMode = isAccessiblityDaltonizerEnabled() + ? Secure.getIntForUser(getContext().getContentResolver(), + Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, + AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser) : AccessibilityManager.DALTONIZER_DISABLED; final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); @@ -506,11 +519,9 @@ public final class ColorDisplayService extends SystemService { if (mCurrentUser == UserHandle.USER_NULL) { return; } - final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(), - Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0; final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR, - enabled ? MATRIX_INVERT_COLOR : null); + isAccessiblityInversionEnabled() ? MATRIX_INVERT_COLOR : null); } /** @@ -598,6 +609,7 @@ public final class ColorDisplayService extends SystemService { boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated(); mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() && !mNightDisplayTintController.isActivated() + && !isAccessibilityEnabled() && DisplayTransformManager.needsLinearColorMatrix()); boolean activated = mDisplayWhiteBalanceTintController.isActivated(); @@ -761,10 +773,7 @@ public final class ColorDisplayService extends SystemService { private @ColorMode int getColorModeInternal() { final ContentResolver cr = getContext().getContentResolver(); - if (Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, - 0, mCurrentUser) == 1 - || Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, - 0, mCurrentUser) == 1) { + if (isAccessibilityEnabled()) { // There are restrictions on the available color modes combined with a11y transforms. if (isColorModeAvailable(COLOR_MODE_SATURATED)) { return COLOR_MODE_SATURATED; diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java index 97c9c7939129..d2c6cd9f1007 100644 --- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java +++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java @@ -32,6 +32,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.lang.System; final class DisplayWhiteBalanceTintController extends TintController { @@ -39,6 +40,7 @@ final class DisplayWhiteBalanceTintController extends TintController { private static final int NUM_VALUES_PER_PRIMARY = 3; // Four colors: red, green, blue, and white private static final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY; + private static final int COLORSPACE_MATRIX_LENGTH = 9; private final Object mLock = new Object(); @VisibleForTesting @@ -46,14 +48,16 @@ final class DisplayWhiteBalanceTintController extends TintController { @VisibleForTesting int mTemperatureMax; private int mTemperatureDefault; - private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + @VisibleForTesting + float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; @VisibleForTesting ColorSpace.Rgb mDisplayColorSpaceRGB; private float[] mChromaticAdaptationMatrix; @VisibleForTesting int mCurrentColorTemperature; private float[] mCurrentColorTemperatureXYZ; - private boolean mSetUp = false; + @VisibleForTesting + boolean mSetUp = false; private float[] mMatrixDisplayWhiteBalance = new float[16]; private Boolean mIsAvailable; @@ -73,6 +77,16 @@ final class DisplayWhiteBalanceTintController extends TintController { } } + // Make sure display color space is valid + if (!isColorMatrixValid(displayColorSpaceRGB.getTransform())) { + Slog.e(ColorDisplayService.TAG, "Invalid display color space RGB-to-XYZ transform"); + return; + } + if (!isColorMatrixValid(displayColorSpaceRGB.getInverseTransform())) { + Slog.e(ColorDisplayService.TAG, "Invalid display color space XYZ-to-RGB transform"); + return; + } + final String[] nominalWhiteValues = res.getStringArray( R.array.config_displayWhiteBalanceDisplayNominalWhite); float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; @@ -157,11 +171,16 @@ final class DisplayWhiteBalanceTintController extends TintController { final float adaptedMaxG = result[1] + result[4] + result[7]; final float adaptedMaxB = result[2] + result[5] + result[8]; final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB); + + Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); for (int i = 0; i < result.length; i++) { result[i] /= denum; + if (!isColorMatrixCoeffValid(result[i])) { + Slog.e(ColorDisplayService.TAG, "Invalid DWB color matrix"); + return; + } } - Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3); java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3); java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3); @@ -277,4 +296,27 @@ final class DisplayWhiteBalanceTintController extends TintController { return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ); } + + private boolean isColorMatrixCoeffValid(float coeff) { + if (Float.isNaN(coeff) || Float.isInfinite(coeff)) { + return false; + } + + return true; + } + + private boolean isColorMatrixValid(float[] matrix) { + if (matrix == null || matrix.length != COLORSPACE_MATRIX_LENGTH) { + return false; + } + + for (int i = 0; i < matrix.length; i++) { + if (!isColorMatrixCoeffValid(matrix[i])) { + return false; + } + } + + return true; + } + } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 44228eec8e94..5f1f20294bc1 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -1597,8 +1597,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private void reportGnssServiceDied() { if (DEBUG) Log.d(TAG, "reportGnssServiceDied"); mHandler.post(() -> { - class_init_native(); - setupNativeGnssService(); + setupNativeGnssService(/* reinitializeGnssServiceHandle = */ true); if (isEnabled()) { synchronized (mLock) { mEnabled = false; @@ -2052,7 +2051,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements * this handler. */ private void handleInitialize() { - setupNativeGnssService(); + // class_init_native() already initializes the GNSS service handle during class loading. + setupNativeGnssService(/* reinitializeGnssServiceHandle = */ false); if (native_is_gnss_visibility_control_supported()) { mGnssVisibilityControl = new GnssVisibilityControl(mContext, mLooper); @@ -2214,8 +2214,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements pw.append(s); } - private void setupNativeGnssService() { - native_init_once(); + private void setupNativeGnssService(boolean reinitializeGnssServiceHandle) { + native_init_once(reinitializeGnssServiceHandle); /* * A cycle of native_init() and native_cleanup() is needed so that callbacks are @@ -2244,7 +2244,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private static native boolean native_is_gnss_visibility_control_supported(); - private static native void native_init_once(); + private static native void native_init_once(boolean reinitializeGnssServiceHandle); private native boolean native_init(); diff --git a/services/core/java/com/android/server/om/DumpState.java b/services/core/java/com/android/server/om/DumpState.java new file mode 100644 index 000000000000..1e2e054bbc6c --- /dev/null +++ b/services/core/java/com/android/server/om/DumpState.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.om; + +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.os.UserHandle; + +/** + * State for dumps performed by the OverlayManagerService. + */ +public final class DumpState { + @UserIdInt private int mUserId = UserHandle.USER_ALL; + @Nullable private String mPackageName; + @Nullable private String mField; + private boolean mVerbose; + + /** Sets the user to dump the state for */ + public void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + @UserIdInt public int getUserId() { + return mUserId; + } + + /** Sets the name of the package to dump the state for */ + public void setPackageName(String packageName) { + mPackageName = packageName; + } + @Nullable public String getPackageName() { + return mPackageName; + } + + /** Sets the name of the field to dump the state of */ + public void setField(String field) { + mField = field; + } + @Nullable public String getField() { + return mField; + } + + /** Enables verbose dump state */ + public void setVerbose(boolean verbose) { + mVerbose = verbose; + } + public boolean isVerbose() { + return mVerbose; + } +} diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 209ccdae75d0..da69986cd59f 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -749,15 +749,77 @@ public final class OverlayManagerService extends SystemService { } @Override - protected void dump(@NonNull final FileDescriptor fd, @NonNull final PrintWriter pw, - @NonNull final String[] argv) { - enforceDumpPermission("dump"); - - final boolean verbose = argv.length > 0 && "--verbose".equals(argv[0]); + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + final DumpState dumpState = new DumpState(); + dumpState.setUserId(UserHandle.getUserId(Binder.getCallingUid())); + + int opti = 0; + while (opti < args.length) { + final String opt = args[opti]; + if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { + break; + } + opti++; + + if ("-h".equals(opt)) { + pw.println("dump [-h] [--verbose] [--user USER_ID] [[FIELD] PACKAGE]"); + pw.println(" Print debugging information about the overlay manager."); + pw.println(" With optional parameter PACKAGE, limit output to the specified"); + pw.println(" package. With optional parameter FIELD, limit output to"); + pw.println(" the value of that SettingsItem field. Field names are"); + pw.println(" case insensitive and out.println the m prefix can be omitted,"); + pw.println(" so the following are equivalent: mState, mstate, State, state."); + return; + } else if ("--user".equals(opt)) { + opti++; + if (opti >= args.length) { + pw.println("Error: user missing argument"); + return; + } + try { + dumpState.setUserId(Integer.parseInt(args[opti])); + } catch (NumberFormatException e) { + pw.println("Error: user argument is not a number: " + args[opti]); + return; + } + } else if ("--verbose".equals(opt)) { + dumpState.setVerbose(true); + } else { + pw.println("Unknown argument: " + opt + "; use -h for help"); + } + } + if (opti < args.length) { + final String arg = args[opti]; + opti++; + switch (arg) { + case "packagename": + case "userid": + case "targetpackagename": + case "targetoverlayablename": + case "basecodepath": + case "state": + case "isenabled": + case "isstatic": + case "priority": + case "category": + dumpState.setField(arg); + break; + default: + dumpState.setPackageName(arg); + break; + } + } + if (dumpState.getPackageName() == null && opti < args.length) { + dumpState.setPackageName(args[opti]); + opti++; + } + enforceDumpPermission("dump"); synchronized (mLock) { - mImpl.onDump(pw); - mPackageManager.dump(pw, verbose); + mImpl.dump(pw, dumpState); + if (dumpState.getPackageName() == null) { + mPackageManager.dump(pw, dumpState); + } } } @@ -1046,10 +1108,10 @@ public final class OverlayManagerService extends SystemService { private static final String TAB1 = " "; private static final String TAB2 = TAB1 + TAB1; - public void dump(@NonNull final PrintWriter pw, final boolean verbose) { + public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) { pw.println("PackageInfo cache"); - if (!verbose) { + if (!dumpState.isVerbose()) { int count = 0; final int n = mCache.size(); for (int i = 0; i < n; i++) { diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 092dbc817e32..6f28675d051b 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -636,9 +636,11 @@ final class OverlayManagerServiceImpl { return true; } - void onDump(@NonNull final PrintWriter pw) { - mSettings.dump(pw); - pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays)); + void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) { + mSettings.dump(pw, dumpState); + if (dumpState.getPackageName() == null) { + pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays)); + } } @NonNull String[] getDefaultOverlayPackages() { diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index f35c70780db9..b7346d455319 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -22,6 +22,7 @@ import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.om.OverlayInfo; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Slog; import android.util.Xml; @@ -288,35 +289,78 @@ final class OverlayManagerSettings { return true; } - void dump(@NonNull final PrintWriter p) { + void dump(@NonNull final PrintWriter p, @NonNull DumpState dumpState) { + // select items to display + Stream<SettingsItem> items = mItems.stream(); + if (dumpState.getUserId() != UserHandle.USER_ALL) { + items = items.filter(item -> item.mUserId == dumpState.getUserId()); + } + if (dumpState.getPackageName() != null) { + items = items.filter(item -> item.mPackageName.equals(dumpState.getPackageName())); + } + + // display items final IndentingPrintWriter pw = new IndentingPrintWriter(p, " "); - pw.println("Settings"); + if (dumpState.getField() != null) { + items.forEach(item -> dumpSettingsItemField(pw, item, dumpState.getField())); + } else { + items.forEach(item -> dumpSettingsItem(pw, item)); + } + } + + private void dumpSettingsItem(@NonNull final IndentingPrintWriter pw, + @NonNull final SettingsItem item) { + pw.println(item.mPackageName + ":" + item.getUserId() + " {"); pw.increaseIndent(); - if (mItems.isEmpty()) { - pw.println("<none>"); - return; - } + pw.println("mPackageName...........: " + item.mPackageName); + pw.println("mUserId................: " + item.getUserId()); + pw.println("mTargetPackageName.....: " + item.getTargetPackageName()); + pw.println("mTargetOverlayableName.: " + item.getTargetOverlayableName()); + pw.println("mBaseCodePath..........: " + item.getBaseCodePath()); + pw.println("mState.................: " + OverlayInfo.stateToString(item.getState())); + pw.println("mIsEnabled.............: " + item.isEnabled()); + pw.println("mIsStatic..............: " + item.isStatic()); + pw.println("mPriority..............: " + item.mPriority); + pw.println("mCategory..............: " + item.mCategory); + + pw.decreaseIndent(); + pw.println("}"); + } - final int n = mItems.size(); - for (int i = 0; i < n; i++) { - final SettingsItem item = mItems.get(i); - pw.println(item.mPackageName + ":" + item.getUserId() + " {"); - pw.increaseIndent(); - - pw.println("mPackageName...........: " + item.mPackageName); - pw.println("mUserId................: " + item.getUserId()); - pw.println("mTargetPackageName.....: " + item.getTargetPackageName()); - pw.println("mTargetOverlayableName.: " + item.getTargetOverlayableName()); - pw.println("mBaseCodePath..........: " + item.getBaseCodePath()); - pw.println("mState.................: " + OverlayInfo.stateToString(item.getState())); - pw.println("mIsEnabled.............: " + item.isEnabled()); - pw.println("mIsStatic..............: " + item.isStatic()); - pw.println("mPriority..............: " + item.mPriority); - pw.println("mCategory..............: " + item.mCategory); - - pw.decreaseIndent(); - pw.println("}"); + private void dumpSettingsItemField(@NonNull final IndentingPrintWriter pw, + @NonNull final SettingsItem item, @NonNull final String field) { + switch (field) { + case "packagename": + pw.println(item.mPackageName); + break; + case "userid": + pw.println(item.mUserId); + break; + case "targetpackagename": + pw.println(item.mTargetPackageName); + break; + case "targetoverlayablename": + pw.println(item.mTargetOverlayableName); + break; + case "basecodepath": + pw.println(item.mBaseCodePath); + break; + case "state": + pw.println(OverlayInfo.stateToString(item.mState)); + break; + case "isenabled": + pw.println(item.mIsEnabled); + break; + case "isstatic": + pw.println(item.mIsStatic); + break; + case "priority": + pw.println(item.mPriority); + break; + case "category": + pw.println(item.mCategory); + break; } } diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index e2b1cba3819f..7ee167adfd38 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -77,13 +77,17 @@ final class OverlayManagerShellCommand extends ShellCommand { out.println("Overlay manager (overlay) commands:"); out.println(" help"); out.println(" Print this help text."); - out.println(" dump [--verbose] [--user USER_ID] [PACKAGE [PACKAGE [...]]]"); + out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE]"); out.println(" Print debugging information about the overlay manager."); - out.println(" list [--user USER_ID] [PACKAGE [PACKAGE [...]]]"); + out.println(" With optional parameter PACKAGE, limit output to the specified"); + out.println(" package. With optional parameter FIELD, limit output to"); + out.println(" the value of that SettingsItem field. Field names are"); + out.println(" case insensitive and out.println the m prefix can be omitted,"); + out.println(" so the following are equivalent: mState, mstate, State, state."); + out.println(" list [--user USER_ID] [PACKAGE]"); out.println(" Print information about target and overlay packages."); out.println(" Overlay packages are printed in priority order. With optional"); - out.println(" parameters PACKAGEs, limit output to the specified packages"); - out.println(" but include more information about each package."); + out.println(" parameter PACKAGE, limit output to the specified package."); out.println(" enable [--user USER_ID] PACKAGE"); out.println(" Enable overlay package PACKAGE."); out.println(" disable [--user USER_ID] PACKAGE"); @@ -116,14 +120,20 @@ final class OverlayManagerShellCommand extends ShellCommand { return 1; } } + final String packageName = getNextArg(); final Map<String, List<OverlayInfo>> allOverlays = mInterface.getAllOverlays(userId); for (final String targetPackageName : allOverlays.keySet()) { - out.println(targetPackageName); + if (targetPackageName.equals(packageName)) { + out.println(targetPackageName); + } List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName); final int n = overlaysForTarget.size(); for (int i = 0; i < n; i++) { final OverlayInfo oi = overlaysForTarget.get(i); + if (!targetPackageName.equals(packageName) && !oi.packageName.equals(packageName)) { + continue; + } String status; switch (oi.state) { case OverlayInfo.STATE_ENABLED_STATIC: @@ -139,7 +149,9 @@ final class OverlayManagerShellCommand extends ShellCommand { } out.println(String.format("%s %s", status, oi.packageName)); } - out.println(); + if (targetPackageName.equals(packageName)) { + out.println(); + } } return 0; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index e6313d9dcbe4..35f21496f2cc 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -531,16 +531,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag"); } - // Only system components can circumvent restricted whitelisting when installing. - if ((params.installFlags - & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0 - && mContext.checkCallingOrSelfPermission(Manifest.permission - .WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_DENIED) { - throw new SecurityException("You need the " - + "android.permission.WHITELIST_RESTRICTED_PERMISSIONS permission to" - + " use the PackageManager.INSTALL_WHITELIST_RESTRICTED_PERMISSIONS flag"); - } - // Defensively resize giant app icons if (params.appIcon != null) { final ActivityManager am = (ActivityManager) mContext.getSystemService( diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 648522a61dbc..ed83cbced49f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -240,7 +240,6 @@ import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; -import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings.Global; @@ -4331,19 +4330,13 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { - int newCallingUid = mIsolatedOwners.get(callingUid); - PermissionManager.addPermissionDenialHint( - "callingUid=" + callingUid + " is changed to " + newCallingUid - + " as process is isolated"); - callingUid = newCallingUid; + callingUid = mIsolatedOwners.get(callingUid); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final boolean callerIsInstantApp = instantAppPkgName != null; if (ps == null) { if (callerIsInstantApp) { // pretend the application exists, but, needs to be filtered - PermissionManager.addPermissionDenialHint( - "No package setting but caller is instant app"); return true; } return false; @@ -4355,7 +4348,6 @@ public class PackageManagerService extends IPackageManager.Stub if (callerIsInstantApp) { // both caller and target are both instant, but, different applications, filter if (ps.getInstantApp(userId)) { - PermissionManager.addPermissionDenialHint("Apps are different instant apps"); return true; } // request for a specific component; if it hasn't been explicitly exposed through @@ -4367,23 +4359,10 @@ public class PackageManagerService extends IPackageManager.Stub && isCallerSameApp(instrumentation.info.targetPackage, callingUid)) { return false; } - if (!isComponentVisibleToInstantApp(component, componentType)) { - PermissionManager.addPermissionDenialHint( - "Component is not visible to instant app: " - + component.flattenToShortString()); - return true; - } else { - return false; - } + return !isComponentVisibleToInstantApp(component, componentType); } // request for application; if no components have been explicitly exposed, filter - if (!ps.pkg.visibleToInstantApps) { - PermissionManager.addPermissionDenialHint( - "Package is not visible to instant app: " + ps.pkg.packageName); - return true; - } else { - return false; - } + return !ps.pkg.visibleToInstantApps; } if (ps.getInstantApp(userId)) { // caller can see all components of all instant applications, don't filter @@ -4392,19 +4371,11 @@ public class PackageManagerService extends IPackageManager.Stub } // request for a specific instant application component, filter if (component != null) { - PermissionManager.addPermissionDenialHint( - "Component is not null: " + component.flattenToShortString()); return true; } // request for an instant application; if the caller hasn't been granted access, filter - if (!mInstantAppRegistry.isInstantAccessGranted( - userId, UserHandle.getAppId(callingUid), ps.appId)) { - PermissionManager.addPermissionDenialHint( - "Instant access is not granted: " + ps.appId); - return true; - } else { - return false; - } + return !mInstantAppRegistry.isInstantAccessGranted( + userId, UserHandle.getAppId(callingUid), ps.appId); } return false; } @@ -5649,17 +5620,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public int checkUidPermissionWithDenialHintForwarding(String permName, int uid, - List<String> permissionDenialHints) { - List<String> prev = PermissionManager.resetPermissionDenialHints(permissionDenialHints); - try { - return checkUidPermission(permName, uid); - } finally { - PermissionManager.resetPermissionDenialHints(prev); - } - } - - @Override public int checkUidPermission(String permName, int uid) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { @@ -11766,7 +11726,12 @@ public class PackageManagerService extends IPackageManager.Stub "Code and resource paths haven't been set correctly"); } - if (mApexManager.isApexPackage(pkg.packageName)) { + // Check that there is an APEX package with the same name only during install/first boot + // after OTA. + final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0; + final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0; + if ((isUserInstall || isFirstBootOrUpgrade) + && mApexManager.isApexPackage(pkg.packageName)) { throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, pkg.packageName + " is an APEX package and can't be installed as an APK."); } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 33dd48a1ac6a..fbf074e3ba15 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2351,9 +2351,10 @@ class PackageManagerShellCommand extends ShellCommand { break; case "-g": sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS; - case "-w": - sessionParams.installFlags |= - PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; + break; + case "--restrict-permissions": + sessionParams.installFlags &= + ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; break; case "--dont-kill": sessionParams.installFlags |= PackageManager.INSTALL_DONT_KILL_APP; @@ -3004,10 +3005,10 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" -d: allow version code downgrade (debuggable packages only)"); pw.println(" -p: partial application install (new split on top of existing pkg)"); pw.println(" -g: grant all runtime permissions"); - pw.println(" -w: whitelist all restricted permissions"); pw.println(" -S: size in bytes of package, required for stdin"); pw.println(" --user: install under the given user."); pw.println(" --dont-kill: installing a new feature split, don't kill running app"); + pw.println(" --restrict-permissions: don't whitelist restricted permissions at install"); pw.println(" --originating-uri: set URI where app was downloaded from"); pw.println(" --referrer: set URI that instigated the install of the app"); pw.println(" --pkg: specify expected package name of app being installed"); diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 9782648efb6b..eec4b70880a5 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -680,20 +680,23 @@ class ShortcutPackage extends ShortcutPackageItem { final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>(); for (int i = 0; i < shortcuts.size(); i++) { - final ShortcutInfo si = shortcuts.get(i); + final Set<String> categories = shortcuts.get(i).getCategories(); + if (categories == null || categories.isEmpty()) { + continue; + } for (int j = 0; j < matchedTargets.size(); j++) { // Shortcut must have all of share target categories boolean hasAllCategories = true; final ShareTargetInfo target = matchedTargets.get(j); for (int q = 0; q < target.mCategories.length; q++) { - if (!si.getCategories().contains(target.mCategories[q])) { + if (!categories.contains(target.mCategories[q])) { hasAllCategories = false; break; } } if (hasAllCategories) { - result.add(new ShortcutManager.ShareShortcutInfo(si, new ComponentName( - getPackageName(), target.mTargetClass))); + result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i), + new ComponentName(getPackageName(), target.mTargetClass))); break; } } @@ -706,6 +709,45 @@ class ShortcutPackage extends ShortcutPackageItem { } /** + * Returns the number of shortcuts that can be used as a share target in the ShareSheet. Such + * shortcuts must have a matching category with at least one of the defined ShareTargets from + * the app's Xml resource. + */ + int getSharingShortcutCount() { + if (mShortcuts.isEmpty() || mShareTargets.isEmpty()) { + return 0; + } + + // Get the list of all dynamic shortcuts in this package + final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + findAll(shortcuts, ShortcutInfo::isDynamicVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); + + int sharingShortcutCount = 0; + for (int i = 0; i < shortcuts.size(); i++) { + final Set<String> categories = shortcuts.get(i).getCategories(); + if (categories == null || categories.isEmpty()) { + continue; + } + for (int j = 0; j < mShareTargets.size(); j++) { + // A SharingShortcut must have all of share target categories + boolean hasAllCategories = true; + final ShareTargetInfo target = mShareTargets.get(j); + for (int q = 0; q < target.mCategories.length; q++) { + if (!categories.contains(target.mCategories[q])) { + hasAllCategories = false; + break; + } + } + if (hasAllCategories) { + sharingShortcutCount++; + break; + } + } + } + return sharingShortcutCount; + } + + /** * Return the filenames (excluding path names) of icon bitmap files from this package. */ public ArraySet<String> getUsedBitmapFiles() { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index daef4e064004..2d8a2acd575f 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -95,6 +95,7 @@ import android.view.IWindowManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; @@ -413,6 +414,9 @@ public class ShortcutService extends IShortcutService.Stub { @GuardedBy("mLock") private Exception mLastWtfStacktrace; + @GuardedBy("mLock") + private final MetricsLogger mMetricsLogger = new MetricsLogger(); + static class InvalidFileFormatException extends Exception { public InvalidFileFormatException(String message, Throwable cause) { super(message, cause); @@ -981,6 +985,8 @@ public class ShortcutService extends IShortcutService.Stub { Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); file.failWrite(os); } + + getUserShortcutsLocked(userId).logSharingShortcutStats(mMetricsLogger); } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index 1fd9b69e521d..8c207a8e1a6f 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.pm.ShortcutManager; +import android.metrics.LogMaker; import android.text.TextUtils; import android.text.format.Formatter; import android.util.ArrayMap; @@ -27,6 +28,8 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.Preconditions; import com.android.server.pm.ShortcutService.DumpFilter; import com.android.server.pm.ShortcutService.InvalidFileFormatException; @@ -647,4 +650,23 @@ class ShortcutUser { return result; } + + void logSharingShortcutStats(MetricsLogger logger) { + int packageWithShareTargetsCount = 0; + int totalSharingShortcutCount = 0; + for (int i = 0; i < mPackages.size(); i++) { + if (mPackages.valueAt(i).hasShareTargets()) { + packageWithShareTargetsCount++; + totalSharingShortcutCount += mPackages.valueAt(i).getSharingShortcutCount(); + } + } + + final LogMaker logMaker = new LogMaker(MetricsEvent.ACTION_SHORTCUTS_CHANGED); + logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_USER_ID) + .setSubtype(mUserId)); + logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_PACKAGE_COUNT) + .setSubtype(packageWithShareTargetsCount)); + logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_SHORTCUT_COUNT) + .setSubtype(totalSharingShortcutCount)); + } } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index e0256460042a..4fdf1bc58e05 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1134,7 +1134,7 @@ public final class DefaultPermissionGrantPolicy { private void grantRuntimePermissions(PackageInfo pkg, Set<String> permissionsWithoutSplits, boolean systemFixed, boolean ignoreSystemPackage, boolean whitelistRestrictedPermissions, int userId) { - UserHandle user = UserHandle.of(userId); + UserHandle user = UserHandle.of(userId); if (pkg == null) { return; } @@ -1203,7 +1203,7 @@ public final class DefaultPermissionGrantPolicy { if (ArrayUtils.isEmpty(disabledPkg.requestedPermissions)) { return; } - if (!requestedPermissions.equals(disabledPkg.requestedPermissions)) { + if (!Arrays.equals(requestedPermissions, disabledPkg.requestedPermissions)) { grantablePermissions = new ArraySet<>(Arrays.asList(requestedPermissions)); requestedPermissions = disabledPkg.requestedPermissions; } @@ -1213,7 +1213,7 @@ public final class DefaultPermissionGrantPolicy { final int numRequestedPermissions = requestedPermissions.length; // Sort requested permissions so that all permissions that are a foreground permission (i.e. - // permisions that have background permission) are before their background permissions. + // permissions that have a background permission) are before their background permissions. final String[] sortedRequestedPermissions = new String[numRequestedPermissions]; int numForeground = 0; int numOther = 0; @@ -1258,9 +1258,16 @@ public final class DefaultPermissionGrantPolicy { continue; } - int uid = UserHandle.getUid(userId, - UserHandle.getAppId(pkg.applicationInfo.uid)); - String op = AppOpsManager.permissionToOp(permission); + // Preserve whitelisting flags. + newFlags |= (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT); + + // If we are whitelisting the permission, update the exempt flag before grant. + if (whitelistRestrictedPermissions && isPermissionRestricted(permission)) { + mContext.getPackageManager().updatePermissionFlags(permission, + pkg.packageName, + PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, + PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user); + } if (pm.checkPermission(permission, pkg.packageName) != PackageManager.PERMISSION_GRANTED) { @@ -1268,13 +1275,12 @@ public final class DefaultPermissionGrantPolicy { .grantRuntimePermission(pkg.packageName, permission, user); } - if (whitelistRestrictedPermissions && isPermissionRestricted(permission)) { - newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } - mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName, newFlags, newFlags, user); + int uid = UserHandle.getUid(userId, + UserHandle.getAppId(pkg.applicationInfo.uid)); + List<String> fgPerms = mPermissionManager.getBackgroundPermissions() .get(permission); if (fgPerms != null) { @@ -1285,6 +1291,7 @@ public final class DefaultPermissionGrantPolicy { if (pm.checkPermission(fgPerm, pkg.packageName) == PackageManager.PERMISSION_GRANTED) { // Upgrade the app-op state of the fg permission to allow bg access + // TODO: Dont' call app ops from package manager code. mContext.getSystemService(AppOpsManager.class).setUidMode( AppOpsManager.permissionToOp(fgPerm), uid, AppOpsManager.MODE_ALLOWED); @@ -1295,8 +1302,10 @@ public final class DefaultPermissionGrantPolicy { } String bgPerm = getBackgroundPermission(permission); + String op = AppOpsManager.permissionToOp(permission); if (bgPerm == null) { if (op != null) { + // TODO: Dont' call app ops from package manager code. mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid, AppOpsManager.MODE_ALLOWED); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 448e595014bc..37c1aaa45b8b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -32,7 +32,6 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTAL import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM; import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE; import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS_ALL; -import static android.content.pm.PackageManager.RESTRICTED_PERMISSIONS_ENABLED; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; @@ -333,22 +332,15 @@ public class PermissionManagerService { mPackageManagerInt.getInstantAppPackageName(uid) != null; final int userId = UserHandle.getUserId(uid); if (!mUserManagerInt.exists(userId)) { - PermissionManager.addPermissionDenialHint("User does not exist. userId=" + userId); return PackageManager.PERMISSION_DENIED; } if (pkg != null) { if (pkg.mSharedUserId != null) { if (isCallerInstantApp) { - PermissionManager.addPermissionDenialHint( - "Caller is instant app. Pkg is shared. callingUid=" + callingUid - + " pkg=" + pkg.packageName); return PackageManager.PERMISSION_DENIED; } } else if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) { - PermissionManager.addPermissionDenialHint( - "Access is filtered. pkg=" + pkg + " callingUid=" + callingUid - + " callingUserId=" + callingUserId); return PackageManager.PERMISSION_DENIED; } final PermissionsState permissionsState = @@ -358,8 +350,6 @@ public class PermissionManagerService { if (mSettings.isPermissionInstant(permName)) { return PackageManager.PERMISSION_GRANTED; } - PermissionManager.addPermissionDenialHint( - "Caller instant app, but perm is not instant"); } else { return PackageManager.PERMISSION_GRANTED; } @@ -367,7 +357,6 @@ public class PermissionManagerService { if (isImpliedPermissionGranted(permissionsState, permName, userId)) { return PackageManager.PERMISSION_GRANTED; } - PermissionManager.addPermissionDenialHint("Does not have permission " + permName); } else { ArraySet<String> perms = mSystemPermissions.get(uid); if (perms != null) { @@ -379,8 +368,6 @@ public class PermissionManagerService { return PackageManager.PERMISSION_GRANTED; } } - PermissionManager.addPermissionDenialHint( - "System permissions do not contain " + permName); } return PackageManager.PERMISSION_DENIED; } @@ -1070,8 +1057,8 @@ public class PermissionManagerService { boolean wasChanged = false; - boolean restrictionExempt = !RESTRICTED_PERMISSIONS_ENABLED - || (origPermissions.getPermissionFlags(bp.name, userId) + boolean restrictionExempt = + (origPermissions.getPermissionFlags(bp.name, userId) & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; boolean restrictionApplied = (origPermissions.getPermissionFlags( bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; @@ -1189,8 +1176,8 @@ public class PermissionManagerService { for (int userId : currentUserIds) { boolean wasChanged = false; - boolean restrictionExempt = !RESTRICTED_PERMISSIONS_ENABLED - || (origPermissions.getPermissionFlags(bp.name, userId) + boolean restrictionExempt = + (origPermissions.getPermissionFlags(bp.name, userId) & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; boolean restrictionApplied = (origPermissions.getPermissionFlags( bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; @@ -2066,7 +2053,7 @@ public class PermissionManagerService { return; } - if (RESTRICTED_PERMISSIONS_ENABLED && bp.isHardOrSoftRestricted() + if (bp.isHardOrSoftRestricted() && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) { Log.e(TAG, "Cannot grant restricted non-exempt permission " + permName + " for package " + packageName); diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index a280d83fac27..250f3313e3c4 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -326,8 +326,8 @@ public final class PermissionPolicyService extends SystemService { return; } - final boolean applyRestriction = PackageManager.RESTRICTED_PERMISSIONS_ENABLED - && (mPackageManager.getPermissionFlags(permission, pkg.packageName, + final boolean applyRestriction = + (mPackageManager.getPermissionFlags(permission, pkg.packageName, mContext.getUser()) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; if (permissionInfo.isHardRestricted()) { diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 9decb58aff2e..feef5e27d26a 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -326,7 +326,7 @@ class ActivityMetricsLogger { intent)); } - if (!isAnyTransitionActive()) { + if (mCurrentTransitionStartTime == INVALID_START_TIME) { mCurrentTransitionStartTime = SystemClock.uptimeMillis(); mLastTransitionStartTime = mCurrentTransitionStartTime; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 802683a67f80..39eda044a5b3 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2322,7 +2322,9 @@ final class ActivityRecord extends ConfigurationContainer { return; } - if (configChanges == 0 && mAppWindowToken.okToDisplay()) { + // Window configuration changes only effect windows, so don't require a screen freeze. + int freezableConfigChanges = configChanges & ~(CONFIG_WINDOW_CONFIGURATION); + if (freezableConfigChanges == 0 && mAppWindowToken.okToDisplay()) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken); return; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 4a6aa336e36f..b234bc6f77c3 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -141,6 +141,7 @@ import android.app.IActivityTaskManager; import android.app.IApplicationThread; import android.app.IAssistDataReceiver; import android.app.INotificationManager; +import android.app.IRequestFinishCallback; import android.app.ITaskStackListener; import android.app.Notification; import android.app.NotificationManager; @@ -2288,6 +2289,32 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @Override + public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) { + synchronized (mGlobalLock) { + ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return; + } + ActivityStack stack = r.getActivityStack(); + if (stack != null && stack.isSingleTaskInstance()) { + // Single-task stacks are used for activities which are presented in floating + // windows above full screen activities. Instead of directly finishing the + // task, a task change listener is used to notify SystemUI so the action can be + // handled specially. + final TaskRecord task = r.getTaskRecord(); + mTaskChangeNotificationController + .notifyBackPressedOnTaskRoot(task.getTaskInfo()); + } else { + try { + callback.requestFinish(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to invoke request finish callback", e); + } + } + } + } + /** * TODO: Add mController hook */ diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 649949701419..fd90f0339e63 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -509,6 +509,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final DisplayContent displayContent = getDisplayContent(); displayContent.mOpeningApps.remove(this); displayContent.mClosingApps.remove(this); + if (isInChangeTransition()) { + clearChangeLeash(getPendingTransaction(), true /* cancel */); + } + displayContent.mChangingApps.remove(this); waitingToShow = false; hiddenRequested = !visible; mDeferHidingClient = deferHidingClient; @@ -1310,7 +1314,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void onDisplayChanged(DisplayContent dc) { DisplayContent prevDc = mDisplayContent; super.onDisplayChanged(dc); - if (prevDc == null) { + if (prevDc == null || prevDc == mDisplayContent) { return; } if (prevDc.mChangingApps.contains(this)) { @@ -1333,7 +1337,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } - if (prevDc != mDisplayContent && mLetterbox != null) { + if (mLetterbox != null) { mLetterbox.onMovedToDisplay(mDisplayContent.getDisplayId()); } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index b6295e194aac..b8504db8e810 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -70,6 +70,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; @@ -2007,7 +2008,8 @@ public class DisplayPolicy { pf.set(displayFrames.mOverscan); } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW - || type == TYPE_VOLUME_OVERLAY)) { + || type == TYPE_VOLUME_OVERLAY + || type == TYPE_KEYGUARD_DIALOG)) { // Asking for layout as if the nav bar is hidden, lets the application // extend into the unrestricted overscan screen area. We only do this for // application windows and certain system windows to ensure no window that diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 3d57219209d7..66200e39938b 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -53,6 +53,7 @@ class TaskChangeNotificationController { private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19; private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20; + private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21; // Delay in notifying task stack change listeners (in millis) private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -92,6 +93,10 @@ class TaskChangeNotificationController { l.onTaskDescriptionChanged((RunningTaskInfo) m.obj); }; + private final TaskStackConsumer mNotifyBackPressedOnTaskRoot = (l, m) -> { + l.onBackPressedOnTaskRoot((RunningTaskInfo) m.obj); + }; + private final TaskStackConsumer mNotifyActivityRequestedOrientationChanged = (l, m) -> { l.onActivityRequestedOrientationChanged(m.arg1, m.arg2); }; @@ -225,6 +230,9 @@ class TaskChangeNotificationController { case NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG: forAllRemoteListeners(mOnSizeCompatModeActivityChanged, msg); break; + case NOTIFY_BACK_PRESSED_ON_TASK_ROOT: + forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg); + break; } } } @@ -458,4 +466,15 @@ class TaskChangeNotificationController { forAllLocalListeners(mOnSizeCompatModeActivityChanged, msg); msg.sendToTarget(); } + + /** + * Notify listeners that an activity received a back press when there are no other activities + * in the back stack. + */ + void notifyBackPressedOnTaskRoot(TaskInfo taskInfo) { + final Message msg = mHandler.obtainMessage(NOTIFY_BACK_PRESSED_ON_TASK_ROOT, + taskInfo); + forAllLocalListeners(mNotifyBackPressedOnTaskRoot, msg); + msg.sendToTarget(); + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index eac771639688..088d2f0fd288 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -137,6 +137,7 @@ import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Matrix; +import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; @@ -2604,9 +2605,11 @@ public class WindowManagerService extends IWindowManager.Stub final AppWindowToken wtoken = mRoot.getAppWindowToken(token); if (wtoken != null) { final WindowState win = wtoken.findMainWindow(); - if (win != null) { - win.mWinAnimator.setOpaqueLocked(isOpaque); + if (win == null) { + return; } + isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format); + win.mWinAnimator.setOpaqueLocked(isOpaque); } } @@ -6828,9 +6831,26 @@ public class WindowManagerService extends IWindowManager.Stub return; } + int lastWindowingMode = displayContent.getWindowingMode(); mDisplayWindowSettings.setWindowingModeLocked(displayContent, mode); reconfigureDisplayLocked(displayContent); + + if (lastWindowingMode != displayContent.getWindowingMode()) { + // reconfigure won't detect this change in isolation because the windowing mode is + // already set on the display, so fire off a new config now. + mH.removeMessages(H.SEND_NEW_CONFIGURATION); + + final long origId = Binder.clearCallingIdentity(); + try { + // direct call since lock is shared. + sendNewConfiguration(displayId); + } finally { + Binder.restoreCallingIdentity(origId); + } + // Now that all configurations are updated, execute pending transitions + displayContent.executeAppTransition(); + } } } @@ -7415,8 +7435,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean shouldShowIme(int displayId) { synchronized (mGlobalLock) { - final DisplayContent displayContent = mRoot.getDisplayContent(displayId); - return mDisplayWindowSettings.shouldShowImeLocked(displayContent); + return WindowManagerService.this.shouldShowIme(displayId); } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3834d57c3525..dd3c6004dcad 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1078,7 +1078,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + mRequestedWidth + ", mRequestedheight=" + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph + "): frame=" + mWindowFrames.mFrame.toShortString() - + " " + mWindowFrames.getInsetsInfo()); + + " " + mWindowFrames.getInsetsInfo() + + " " + mAttrs.getTitle()); } // TODO: Look into whether this override is still necessary. diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 89a1ec8507d6..da175792268f 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -1495,7 +1495,8 @@ struct GnssBatchingCallback_V2_0 : public IGnssBatchingCallback_V2_0 { } }; -static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { +/* Initializes the GNSS service handle. */ +static void android_location_GnssLocationProvider_set_gps_service_handle() { gnssHal_V2_0 = IGnss_V2_0::getService(); if (gnssHal_V2_0 != nullptr) { gnssHal = gnssHal_V2_0; @@ -1514,7 +1515,12 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, gnssHal = IGnss_V1_0::getService(); } -static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz) { +/* One time initialization at system boot */ +static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { + // Initialize the top level gnss HAL handle. + android_location_GnssLocationProvider_set_gps_service_handle(); + + // Cache methodIDs and class IDs. method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(ZLandroid/location/Location;)V"); method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); @@ -1638,7 +1644,11 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass); method_halInterfaceVersionCtor = env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V"); +} +/* Initialization needed at system boot and whenever GNSS service dies. */ +static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz, + jboolean reinitializeGnssServiceHandle) { /* * Save a pointer to JVM. */ @@ -1647,6 +1657,10 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus); } + if (reinitializeGnssServiceHandle) { + android_location_GnssLocationProvider_set_gps_service_handle(); + } + if (gnssHal == nullptr) { ALOGE("Unable to get GPS service\n"); return; @@ -1871,6 +1885,7 @@ static jobject android_location_GnssConfiguration_get_gnss_configuration_version return createHalInterfaceVersionJavaObject(env, major, minor); } +/* Initialization needed each time the GPS service is shutdown. */ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject obj) { /* * This must be set before calling into the HAL library. @@ -3026,7 +3041,7 @@ static const JNINativeMethod sMethods[] = { android_location_GnssLocationProvider_class_init_native)}, {"native_is_supported", "()Z", reinterpret_cast<void *>( android_location_GnssLocationProvider_is_supported)}, - {"native_init_once", "()V", reinterpret_cast<void *>( + {"native_init_once", "(Z)V", reinterpret_cast<void *>( android_location_GnssLocationProvider_init_once)}, {"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)}, {"native_cleanup", "()V", reinterpret_cast<void *>( diff --git a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java new file mode 100644 index 000000000000..4538cacbf9c4 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.color; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + +import static com.google.common.truth.Truth.assertWithMessage; + +import android.content.Context; +import android.content.res.Resources; +import android.os.Binder; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.view.SurfaceControl.DisplayPrimaries; +import android.view.SurfaceControl.CieXyz; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.R; +import com.android.dx.mockito.inline.extended.ExtendedMockito; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +@RunWith(AndroidJUnit4.class) +public class DisplayWhiteBalanceTintControllerTest { + @Mock + private Context mMockedContext; + @Mock + private Resources mMockedResources; + + private MockitoSession mSession; + private Resources mResources; + IBinder mDisplayToken; + DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController; + + @Before + public void setUp() { + mSession = ExtendedMockito.mockitoSession() + .initMocks(this) + .mockStatic(SurfaceControl.class) + .strictness(Strictness.LENIENT) + .startMocking(); + + mResources = InstrumentationRegistry.getContext().getResources(); + // These Resources are common to all tests. + doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMin)) + .when(mMockedResources) + .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMin); + doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMax)) + .when(mMockedResources) + .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMax); + doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault)) + .when(mMockedResources) + .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault); + doReturn(mResources.getStringArray(R.array.config_displayWhiteBalanceDisplayNominalWhite)) + .when(mMockedResources) + .getStringArray(R.array.config_displayWhiteBalanceDisplayNominalWhite); + doReturn(mMockedResources).when(mMockedContext).getResources(); + + mDisplayToken = new Binder(); + doReturn(mDisplayToken).when(() -> SurfaceControl.getInternalDisplayToken()); + } + + @After + public void tearDown() throws Exception { + if (mSession != null) { + mSession.finishMocking(); + } + } + + /** + * Setup should succeed when SurfaceControl setup results in a valid color transform. + */ + @Test + public void displayWhiteBalance_setupWithSurfaceControl() { + // Make SurfaceControl return sRGB primaries + DisplayPrimaries displayPrimaries = new DisplayPrimaries(); + displayPrimaries.red = new CieXyz(); + displayPrimaries.red.X = 0.412315f; + displayPrimaries.red.Y = 0.212600f; + displayPrimaries.red.Z = 0.019327f; + displayPrimaries.green = new CieXyz(); + displayPrimaries.green.X = 0.357600f; + displayPrimaries.green.Y = 0.715200f; + displayPrimaries.green.Z = 0.119200f; + displayPrimaries.blue = new CieXyz(); + displayPrimaries.blue.X = 0.180500f; + displayPrimaries.blue.Y = 0.072200f; + displayPrimaries.blue.Z = 0.950633f; + displayPrimaries.white = new CieXyz(); + displayPrimaries.white.X = 0.950456f; + displayPrimaries.white.Y = 1.000000f; + displayPrimaries.white.Z = 1.089058f; + doReturn(displayPrimaries) + .when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken)); + + setUpTintController(); + assertWithMessage("Setup with valid SurfaceControl failed") + .that(mDisplayWhiteBalanceTintController.mSetUp) + .isTrue(); + } + + /** + * Setup should fail when SurfaceControl setup results in an invalid color transform. + */ + @Test + public void displayWhiteBalance_setupWithInvalidSurfaceControlData() { + // Make SurfaceControl return invalid display primaries + DisplayPrimaries displayPrimaries = new DisplayPrimaries(); + displayPrimaries.red = new CieXyz(); + displayPrimaries.green = new CieXyz(); + displayPrimaries.blue = new CieXyz(); + displayPrimaries.white = new CieXyz(); + doReturn(displayPrimaries) + .when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken)); + + setUpTintController(); + assertWithMessage("Setup with invalid SurfaceControl succeeded") + .that(mDisplayWhiteBalanceTintController.mSetUp) + .isFalse(); + } + + /** + * Setup should succeed when SurfaceControl setup fails and Resources result in a valid color + * transform. + */ + @Test + public void displayWhiteBalance_setupWithResources() { + // Use default (valid) Resources + doReturn(mResources.getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries)) + .when(mMockedResources) + .getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries); + // Make SurfaceControl setup fail + doReturn(null).when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken)); + + setUpTintController(); + assertWithMessage("Setup with valid Resources failed") + .that(mDisplayWhiteBalanceTintController.mSetUp) + .isTrue(); + } + + /** + * Setup should fail when SurfaceControl setup fails and Resources result in an invalid color + * transform. + */ + @Test + public void displayWhiteBalance_setupWithInvalidResources() { + // Use Resources with invalid color data + doReturn(new String[] { + "0", "0", "0", // Red X, Y, Z + "0", "0", "0", // Green X, Y, Z + "0", "0", "0", // Blue X, Y, Z + "0", "0", "0", // White X, Y, Z + }) + .when(mMockedResources) + .getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries); + // Make SurfaceControl setup fail + doReturn(null).when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken)); + + setUpTintController(); + assertWithMessage("Setup with invalid Resources succeeded") + .that(mDisplayWhiteBalanceTintController.mSetUp) + .isFalse(); + } + + private void setUpTintController() { + mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController(); + mDisplayWhiteBalanceTintController.setUp(mMockedContext, true); + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java index de841a0ac4ec..8bb8aae76849 100644 --- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java @@ -1029,6 +1029,34 @@ public class ColorDisplayServiceTest { assertDwbActive(true); } + @Test + public void displayWhiteBalance_disabledWhileAccessibilityColorCorrectionEnabled() { + setDisplayWhiteBalanceEnabled(true); + startService(); + setAccessibilityColorCorrection(true); + + mCds.updateDisplayWhiteBalanceStatus(); + assertDwbActive(false); + + setAccessibilityColorCorrection(false); + mCds.updateDisplayWhiteBalanceStatus(); + assertDwbActive(true); + } + + @Test + public void displayWhiteBalance_disabledWhileAccessibilityColorInversionEnabled() { + setDisplayWhiteBalanceEnabled(true); + startService(); + setAccessibilityColorInversion(true); + + mCds.updateDisplayWhiteBalanceStatus(); + assertDwbActive(false); + + setAccessibilityColorInversion(false); + mCds.updateDisplayWhiteBalanceStatus(); + assertDwbActive(true); + } + /** * Configures Night display to use a custom schedule. * diff --git a/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java index 1dd4e1579866..4ef156e239d3 100644 --- a/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java @@ -18,6 +18,11 @@ package com.android.server.display.color; import static com.google.common.truth.Truth.assertWithMessage; +import androidx.test.InstrumentationRegistry; + +import java.lang.System; +import java.util.Arrays; + import org.junit.Before; import org.junit.Test; @@ -28,6 +33,8 @@ public class DisplayWhiteBalanceTintControllerTest { @Before public void setUp() { mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController(); + mDisplayWhiteBalanceTintController.setUp(InstrumentationRegistry.getContext(), true); + mDisplayWhiteBalanceTintController.setActivated(true); } @Test @@ -59,4 +66,31 @@ public class DisplayWhiteBalanceTintControllerTest { .isEqualTo(colorTemperature); } + @Test + public void displayWhiteBalance_setMatrixValidDwbCalculation() { + float[] currentMatrix = mDisplayWhiteBalanceTintController.getMatrix(); + float[] oldMatrix = Arrays.copyOf(currentMatrix, currentMatrix.length); + + mDisplayWhiteBalanceTintController + .setMatrix(mDisplayWhiteBalanceTintController.mCurrentColorTemperature + 1); + assertWithMessage("DWB matrix did not change when setting a new temperature") + .that(Arrays.equals(oldMatrix, currentMatrix)) + .isFalse(); + } + + @Test + public void displayWhiteBalance_setMatrixInvalidDwbCalculation() { + Arrays.fill(mDisplayWhiteBalanceTintController.mDisplayNominalWhiteXYZ, 0); + mDisplayWhiteBalanceTintController + .setMatrix(mDisplayWhiteBalanceTintController.mCurrentColorTemperature + 1); + assertWithMessage("DWB matrix not set to identity after an invalid DWB calculation") + .that(Arrays.equals(mDisplayWhiteBalanceTintController.getMatrix(), + new float[] { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }) + ).isTrue(); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 6845f15f6a28..b806180a8584 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -1584,6 +1584,22 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } /** + * Make a shortcut with an ID and Category. + */ + protected ShortcutInfo makeShortcutWithCategory(String id, Set<String> categories) { + final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id) + .setActivity(new ComponentName(mClientContext.getPackageName(), "main")) + .setShortLabel("title-" + id) + .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class)) + .setCategories(categories); + final ShortcutInfo s = b.build(); + + s.setTimestamp(mInjectedCurrentTimeMillis); // HACK + + return s; + } + + /** * Make an intent. */ protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) { @@ -1818,6 +1834,17 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } /** + * @return the number of shortcuts stored internally for the caller that can be used as a share + * target in the ShareSheet. Such shortcuts have a matching category with at least one of the + * defined ShareTargets from the app's Xml resource. + */ + protected int getCallerSharingShortcutCount() { + final ShortcutPackage p = mService.getPackageShortcutForTest( + getCallingPackage(), getCallingUserId()); + return p == null ? 0 : p.getSharingShortcutCount(); + } + + /** * @return all shortcuts owned by caller that are actually visible via ShortcutManager. * See also {@link #getCallerShortcuts}. */ diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java index eb4db7a0f4af..ba26f7930dcb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set; import android.content.ComponentName; import android.content.pm.ShortcutInfo; @@ -25,6 +26,8 @@ import android.test.suitebuilder.annotation.SmallTest; import com.android.frameworks.servicestests.R; import com.android.server.pm.ShortcutService.ConfigConstants; +import java.util.Set; + /** * Tests related to shortcut rank auto-adjustment. */ @@ -50,6 +53,10 @@ public class ShortcutManagerTest3 extends BaseShortcutManagerTest { return makeShortcutWithActivityAndRank(id, activity, ShortcutInfo.RANK_NOT_SET); } + private ShortcutInfo shortcut(String id, Set<String> categories) { + return makeShortcutWithCategory(id, categories); + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -502,4 +509,30 @@ public class ShortcutManagerTest3 extends BaseShortcutManagerTest { runTestWithManifestShortcuts(() -> testDisableShortcuts_noManifestShortcuts()); } + public void testGetSharingShortcutCount() { + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_share_targets); + updatePackageVersion(CALLING_PACKAGE_1, 1); + mService.mPackageMonitor.onReceive(getTestContext(), + genPackageAddIntent(CALLING_PACKAGE_1, USER_0)); + + // There are two valid <share-target> definitions in the test manifest with two different + // categories: {"com.test.category.CATEGORY1", "com.test.category.CATEGORY2"} and + // {"com.test.category.CATEGORY5", "com.test.category.CATEGORY6"}. + // + // Note that a shortcut is a match, only if it has ALL of the categories of at least one + // of the share-target definitions from the manifest. + + mManager.addDynamicShortcuts(list( + shortcut("s1", set("com.test.category.CATEGORY1", "com.test.category.CATEGORY2")), + shortcut("s2", set("com.test.category.CATEGORY5")), + shortcut("s3", set("com.test.category.CATEGORY5", "com.test.category.CATEGORY6")), + shortcut("s4", set("com.test.category.CATEGORY1", "com.test.category.CATEGORY2", + "com.test.category.CATEGORY5", "com.test.category.CATEGORY6")), + shortcut("s5", A1) + )); + + assertEquals(3, getCallerSharingShortcutCount()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 777e4f4915b8..035568f489be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -158,4 +158,22 @@ public class AppChangeTransitionTests extends WindowTestsBase { waitUntilHandlersIdle(); mToken.removeImmediately(); } + + @Test + public void testCancelPendingChangeOnHide() { + // setup currently defaults to no snapshot. + setUpOnDisplay(mDisplayContent); + + mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(1, mDisplayContent.mChangingApps.size()); + assertTrue(mToken.isInChangeTransition()); + + // Changing visibility should cancel the change transition and become closing + mToken.setVisibility(false, false); + assertEquals(0, mDisplayContent.mChangingApps.size()); + assertFalse(mToken.isInChangeTransition()); + + waitUntilHandlersIdle(); + mToken.removeImmediately(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index e90f094636f7..4a87aa46db58 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -21,6 +21,7 @@ import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; @@ -32,6 +33,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; @@ -160,6 +162,25 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_keyguardDialog_hideNav() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.type = TYPE_KEYGUARD_DIALOG; + mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* uiMode */); + mDisplayPolicy.layoutWindowLw(mWindow, null /* attached */, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + } + } + + @Test public void layoutWindowLw_withDisplayCutout() { synchronized (mWm.mGlobalLock) { addDisplayCutout(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java index 652ea7d32953..336fa041dea8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -51,6 +51,7 @@ import android.view.Surface; import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; import org.junit.After; @@ -588,6 +589,23 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { getStoredDisplayAttributeValue("shouldShowIme")); } + @Test + public void testShouldShowImeWithinForceDesktopMode() { + try { + // Presume display enabled force desktop mode from developer options. + final DisplayContent dc = createMockSimulatedDisplay(); + mWm.setForceDesktopModeOnExternalDisplays(true); + final WindowManagerInternal wmInternal = LocalServices.getService( + WindowManagerInternal.class); + // Make sure WindowManagerInter#shouldShowIme as true is due to + // mForceDesktopModeOnExternalDisplays as true. + assertFalse(mWm.mDisplayWindowSettings.shouldShowImeLocked(dc)); + assertTrue(wmInternal.shouldShowIme(dc.getDisplayId())); + } finally { + mWm.setForceDesktopModeOnExternalDisplays(false); + } + } + /** * Prepares display settings and stores in {@link #mStorage}. Uses provided display identifier * and stores windowingMode=WINDOWING_MODE_PINNED. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 8c37ca5e309e..3a702cb9521c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.AppOpsManager.OP_NONE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.os.Process.SYSTEM_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.View.VISIBLE; @@ -445,4 +446,13 @@ class WindowTestsBase { return new WindowTestUtils.TestWindowState(mWm, mMockSession, mIWindow, attrs, token); } } + + /** Creates a {@link DisplayContent} as parts of simulate display info for test. */ + DisplayContent createMockSimulatedDisplay() { + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.copyFrom(mDisplayInfo); + displayInfo.type = Display.TYPE_VIRTUAL; + displayInfo.ownerUid = SYSTEM_UID; + return createNewDisplay(displayInfo); + } } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 63d427a6f3c4..6a71c4fa74b3 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -337,12 +337,12 @@ public final class SmsManager { String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - true /* persistMessage*/); + true /* persistMessage*/, ActivityThread.currentPackageName()); } private void sendTextMessageInternal(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, - boolean persistMessage) { + boolean persistMessage, String packageName) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } @@ -355,9 +355,8 @@ public final class SmsManager { // If the subscription is invalid or default, we will use the default phone to send the // SMS and possibly fail later in the SMS sending process. ISms iSms = getISmsServiceOrThrow(); - iSms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), - destinationAddress, - scAddress, text, sentIntent, deliveryIntent, + iSms.sendTextForSubscriber(getSubscriptionId(), packageName, + destinationAddress, scAddress, text, sentIntent, deliveryIntent, persistMessage); } catch (RemoteException ex) { // ignore it @@ -389,7 +388,7 @@ public final class SmsManager { String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - false /* persistMessage */); + false /* persistMessage */, ActivityThread.currentPackageName()); } /** @@ -630,13 +629,30 @@ public final class SmsManager { String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, true /* persistMessage*/); + deliveryIntents, true /* persistMessage*/, ActivityThread.currentPackageName()); + } + + /** + * @hide + * Similar method as #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList) + * With an additional argument + * @param packageName serves as the default package name if ActivityThread.currentpackageName is + * null. + */ + public void sendMultipartTextMessageExternal( + String destinationAddress, String scAddress, ArrayList<String> parts, + ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents, + String packageName) { + sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, + deliveryIntents, true /* persistMessage*/, + ActivityThread.currentPackageName() == null + ? packageName : ActivityThread.currentPackageName()); } private void sendMultipartTextMessageInternal( String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents, - boolean persistMessage) { + boolean persistMessage, String packageName) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } @@ -648,8 +664,7 @@ public final class SmsManager { try { ISms iSms = getISmsServiceOrThrow(); iSms.sendMultipartTextForSubscriber(getSubscriptionId(), - ActivityThread.currentPackageName(), - destinationAddress, scAddress, parts, + packageName, destinationAddress, scAddress, parts, sentIntents, deliveryIntents, persistMessage); } catch (RemoteException ex) { // ignore it @@ -663,8 +678,8 @@ public final class SmsManager { if (deliveryIntents != null && deliveryIntents.size() > 0) { deliveryIntent = deliveryIntents.get(0); } - sendTextMessage(destinationAddress, scAddress, parts.get(0), - sentIntent, deliveryIntent); + sendTextMessageInternal(destinationAddress, scAddress, parts.get(0), + sentIntent, deliveryIntent, true, packageName); } } @@ -685,7 +700,7 @@ public final class SmsManager { String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, false /* persistMessage*/); + deliveryIntents, false /* persistMessage*/, ActivityThread.currentPackageName()); } /** diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index 9a8d7cd35cb0..785d7aea2a6f 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -140,10 +140,6 @@ public class UiccSlotInfo implements Parcelable { return mIsEuicc; } - /** - * Returns the ICCID of a the UICC in the given slot, or the EID if it is an eUICC. Note that if - * the value is unavailble this will return null. - */ public String getCardId() { return mCardId; } diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index 22f078fc0c05..7b5145740b08 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -558,6 +558,24 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu } else if (this.getDisplayPriorityScore() < emergencyNumber.getDisplayPriorityScore()) { return 1; + } else if (this.getNumber().compareTo(emergencyNumber.getNumber()) != 0) { + return this.getNumber().compareTo(emergencyNumber.getNumber()); + } else if (this.getCountryIso().compareTo(emergencyNumber.getCountryIso()) != 0) { + return this.getCountryIso().compareTo(emergencyNumber.getCountryIso()); + } else if (this.getMnc().compareTo(emergencyNumber.getMnc()) != 0) { + return this.getMnc().compareTo(emergencyNumber.getMnc()); + } else if (this.getEmergencyServiceCategoryBitmask() + != emergencyNumber.getEmergencyServiceCategoryBitmask()) { + return this.getEmergencyServiceCategoryBitmask() + > emergencyNumber.getEmergencyServiceCategoryBitmask() ? -1 : 1; + } else if (this.getEmergencyUrns().toString().compareTo( + emergencyNumber.getEmergencyUrns().toString()) != 0) { + return this.getEmergencyUrns().toString().compareTo( + emergencyNumber.getEmergencyUrns().toString()); + } else if (this.getEmergencyCallRouting() + != emergencyNumber.getEmergencyCallRouting()) { + return this.getEmergencyCallRouting() + > emergencyNumber.getEmergencyCallRouting() ? -1 : 1; } else { return 0; } @@ -579,13 +597,9 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu if (emergencyNumberList == null) { return; } - Set<EmergencyNumber> mergedEmergencyNumber = new HashSet<>(); + Set<Integer> duplicatedEmergencyNumberPosition = new HashSet<>(); for (int i = 0; i < emergencyNumberList.size(); i++) { - // Skip the check because it was merged. - if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) { - continue; - } - for (int j = i + 1; j < emergencyNumberList.size(); j++) { + for (int j = 0; j < i; j++) { if (areSameEmergencyNumbers( emergencyNumberList.get(i), emergencyNumberList.get(j))) { Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: " @@ -594,14 +608,15 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu emergencyNumberList.set(i, mergeSameEmergencyNumbers( emergencyNumberList.get(i), emergencyNumberList.get(j))); // Mark the emergency number has been merged - mergedEmergencyNumber.add(emergencyNumberList.get(j)); + duplicatedEmergencyNumberPosition.add(j); } } } - // Remove the marked emergency number in the orignal list - for (int i = 0; i < emergencyNumberList.size(); i++) { - if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) { - emergencyNumberList.remove(i--); + + // Remove the marked emergency number in the original list + for (int i = emergencyNumberList.size() - 1; i >= 0; i--) { + if (duplicatedEmergencyNumberPosition.contains(i)) { + emergencyNumberList.remove(i); } } Collections.sort(emergencyNumberList); diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 326c88d47064..53372bff3e67 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -41,6 +41,7 @@ cc_defaults { windows: { enabled: true, cflags: ["-Wno-maybe-uninitialized"], + ldflags: ["-static"], }, darwin: { cflags: ["-D_DARWIN_UNLIMITED_STREAMS"], diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp index c4a1108ac62a..dbf51143f720 100644 --- a/tools/aapt2/ResourceValues_test.cpp +++ b/tools/aapt2/ResourceValues_test.cpp @@ -284,8 +284,58 @@ TEST(ResourcesValuesTest, AttributeIsCompatible) { EXPECT_FALSE(attr_three.IsCompatibleWith(attr_one)); EXPECT_FALSE(attr_three.IsCompatibleWith(attr_two)); - EXPECT_FALSE(attr_three.IsCompatibleWith(attr_three)); + EXPECT_TRUE(attr_three.IsCompatibleWith(attr_three)); EXPECT_FALSE(attr_three.IsCompatibleWith(attr_four)); + + EXPECT_FALSE(attr_four.IsCompatibleWith(attr_one)); + EXPECT_FALSE(attr_four.IsCompatibleWith(attr_two)); + EXPECT_FALSE(attr_four.IsCompatibleWith(attr_three)); + EXPECT_TRUE(attr_four.IsCompatibleWith(attr_four)); +} + +TEST(ResourcesValuesTest, AttributeEnumIsCompatible) { + Attribute attr_one(TYPE_ENUM); + attr_one.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u}); + attr_one.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x07u}); + + Attribute attr_two(TYPE_ENUM); + attr_two.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u}); + attr_two.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x07u}); + EXPECT_TRUE(attr_one.IsCompatibleWith(attr_two)); +} + +TEST(ResourcesValuesTest, DifferentAttributeEnumDifferentNameIsNotCompatible) { + Attribute attr_one(TYPE_ENUM); + attr_one.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u}); + attr_one.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x07u}); + + Attribute attr_two(TYPE_ENUM); + attr_two.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u}); + attr_one.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/baz")), 0x07u}); + EXPECT_FALSE(attr_one.IsCompatibleWith(attr_two)); +} + +TEST(ResourcesValuesTest, DifferentAttributeEnumDifferentValueIsNotCompatible) { + Attribute attr_one(TYPE_ENUM); + attr_one.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u}); + attr_one.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x07u}); + + Attribute attr_two(TYPE_ENUM); + attr_two.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u}); + attr_two.symbols.push_back( + Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x09u}); + EXPECT_FALSE(attr_one.IsCompatibleWith(attr_two)); } } // namespace aapt diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp index be9c84b3f8a6..78d42a160e21 100644 --- a/tools/aapt2/link/TableMerger_test.cpp +++ b/tools/aapt2/link/TableMerger_test.cpp @@ -408,54 +408,6 @@ TEST_F(TableMergerTest, FailToOverrideConflictingAttributeFormatsWithOverlay) { ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/)); } -TEST_F(TableMergerTest, FailToOverrideConflictingFlagsAndEnumsWithOverlay) { - std::unique_ptr<ResourceTable> base = - test::ResourceTableBuilder() - .SetPackageId("", 0x7f) - .AddValue("attr/foo", test::AttributeBuilder() - .SetTypeMask(android::ResTable_map::TYPE_FLAGS) - .Build()) - .Build(); - - std::unique_ptr<ResourceTable> overlay = - test::ResourceTableBuilder() - .SetPackageId("", 0x7f) - .AddValue("attr/foo", test::AttributeBuilder() - .SetTypeMask(android::ResTable_map::TYPE_FLAGS) - .SetWeak(false) - .Build()) - .Build(); - - ResourceTable final_table; - TableMergerOptions options; - options.auto_add_overlay = false; - TableMerger merger(context_.get(), &final_table, options); - - ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/)); - ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/)); - - base = test::ResourceTableBuilder() - .SetPackageId("", 0x7f) - .AddValue("attr/foo", test::AttributeBuilder() - .SetTypeMask(android::ResTable_map::TYPE_ENUM) - .Build()) - .Build(); - - overlay = test::ResourceTableBuilder() - .SetPackageId("", 0x7f) - .AddValue("attr/foo", test::AttributeBuilder() - .SetTypeMask(android::ResTable_map::TYPE_ENUM) - .SetWeak(false) - .Build()) - .Build(); - - ResourceTable final_table2; - TableMerger merger2(context_.get(), &final_table2, options); - - ASSERT_TRUE(merger2.Merge({}, base.get(), false /*overlay*/)); - ASSERT_FALSE(merger2.Merge({}, overlay.get(), true /*overlay*/)); -} - TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) { std::unique_ptr<ResourceTable> table_a = test::ResourceTableBuilder().SetPackageId("", 0x7f).Build(); diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index a7793785fb9b..cd659e2a934a 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -93,7 +93,9 @@ import java.util.Map; * {@link WifiP2pInfo} contains the address of the group owner * {@link WifiP2pInfo#groupOwnerAddress} and a flag {@link WifiP2pInfo#isGroupOwner} to indicate * if the current device is a p2p group owner. A p2p client can thus communicate with - * the p2p group owner through a socket connection. + * the p2p group owner through a socket connection. If the current device is the p2p group owner, + * {@link WifiP2pInfo#groupOwnerAddress} is anonymized unless the caller holds the + * {@code android.Manifest.permission#LOCAL_MAC_ADDRESS} permission. * * <p> With peer discovery using {@link #discoverPeers}, an application discovers the neighboring * peers, but has no good way to figure out which peer to establish a connection with. For example, @@ -300,6 +302,11 @@ public class WifiP2pManager { * To get information notifications on P2P getting enabled refers * {@link #WIFI_P2P_STATE_ENABLED}. * + * <p> The {@link #EXTRA_WIFI_P2P_DEVICE} extra contains an anonymized version of the device's + * MAC address. Callers holding the {@code android.Manifest.permission#LOCAL_MAC_ADDRESS} + * permission can use {@link #requestDeviceInfo} to obtain the actual MAC address of this + * device. + * * All of these permissions are required to receive this broadcast: * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and * {@link android.Manifest.permission#ACCESS_WIFI_STATE} @@ -1881,6 +1888,10 @@ public class WifiP2pManager { * <p> This {@link android.net.wifi.p2p.WifiP2pDevice} is returned using the * {@link DeviceInfoListener} listener. * + * <p> {@link android.net.wifi.p2p.WifiP2pDevice#deviceAddress} is only available if the caller + * holds the {@code android.Manifest.permission#LOCAL_MAC_ADDRESS} permission, and holds the + * anonymized MAC address (02:00:00:00:00:00) otherwise. + * * <p> This information is also included in the {@link #WIFI_P2P_THIS_DEVICE_CHANGED_ACTION} * broadcast event with extra {@link #EXTRA_WIFI_P2P_DEVICE}. * |