diff options
200 files changed, 4803 insertions, 1994 deletions
diff --git a/Android.bp b/Android.bp index 661b1ccf988e..7b6d145e14e5 100644 --- a/Android.bp +++ b/Android.bp @@ -253,6 +253,8 @@ filegroup { ":libcamera_client_aidl", ":libcamera_client_framework_aidl", ":libupdate_engine_aidl", + // TODO: this needs to be removed when statsd-framework.jar is separated out + ":statsd_aidl", ":storaged_aidl", ":vold_aidl", @@ -413,18 +415,6 @@ filegroup { } filegroup { - name: "statsd_aidl", - srcs: [ - "core/java/android/os/IPullAtomCallback.aidl", - "core/java/android/os/IPullAtomResultReceiver.aidl", - "core/java/android/os/IStatsCompanionService.aidl", - "core/java/android/os/IStatsManager.aidl", - "core/java/android/os/IStatsPullerCallback.aidl", - ], - path: "core/java", -} - -filegroup { name: "libvibrator_aidl", srcs: [ "core/java/android/os/IExternalVibrationController.aidl", @@ -1690,16 +1680,13 @@ filegroup { } filegroup { - name: "framework-wifistack-shared-srcs", + name: "framework-wifi-service-shared-srcs", srcs: [ ":framework-annotations", "core/java/android/os/HandlerExecutor.java", "core/java/android/util/BackupUtils.java", - "core/java/android/util/KeyValueListParser.java", "core/java/android/util/LocalLog.java", "core/java/android/util/Rational.java", - "core/java/android/util/proto/ProtoStream.java", - "core/java/android/util/proto/ProtoOutputStream.java", "core/java/com/android/internal/util/FastXmlSerializer.java", "core/java/com/android/internal/util/HexDump.java", "core/java/com/android/internal/util/IState.java", diff --git a/TEST_MAPPING b/TEST_MAPPING index 55fa5ed13d79..b1c4cad72dc9 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -7,6 +7,22 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] + }, + { + "name": "ExtServicesUnitTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "TestablesTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] } ], "postsubmit-managedprofile-stress": [ diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 3204013042c6..1ec96ecc561b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -2689,7 +2689,7 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override - protected int handleShellCommand(@NonNull ParcelFileDescriptor in, + public int handleShellCommand(@NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args) { return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec( diff --git a/apex/sdkext/TEST_MAPPING b/apex/sdkext/TEST_MAPPING new file mode 100644 index 000000000000..8dc732d36c79 --- /dev/null +++ b/apex/sdkext/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "framework-sdkext-tests" + } + ] +} diff --git a/apex/sdkext/framework/tests/Android.bp b/apex/sdkext/framework/tests/Android.bp index 3d5dbb3d8a2d..ab6327582efd 100644 --- a/apex/sdkext/framework/tests/Android.bp +++ b/apex/sdkext/framework/tests/Android.bp @@ -6,5 +6,6 @@ android_test { "android.test.runner", ], static_libs: [ "framework-sdkext" ], + test_suites: [ "general-tests" ], platform_apis: true, } diff --git a/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java b/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java index 688511096a43..d7dca90e0b8f 100644 --- a/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java +++ b/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java @@ -29,11 +29,6 @@ public class SdkExtensionsTest extends TestCase { SdkExtensions.getExtensionVersion(Build.VERSION_CODES.Q); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } - - try { - SdkExtensions.getExtensionVersion(999999); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { } } @SmallTest diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp new file mode 100644 index 000000000000..e6ca544c04be --- /dev/null +++ b/apex/statsd/aidl/Android.bp @@ -0,0 +1,35 @@ +// +// 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. +// + +// TODO(b/145815909): move StatsDimensionsValue.aidl and StatsLogEventWrapper.aidl here +filegroup { + name: "statsd_aidl", + srcs: ["**/*.aidl"], +} + +// This library is currently unused +aidl_interface { + name: "stats-event-parcel-aidl", + srcs: ["android/util/StatsEventParcel.aidl"], + backend: { + java: { + sdk_version: "28", + }, + cpp: { + enabled: false, + } + } +} diff --git a/core/java/android/os/IPullAtomCallback.aidl b/apex/statsd/aidl/android/os/IPullAtomCallback.aidl index 88d3c3e46ff5..88d3c3e46ff5 100644 --- a/core/java/android/os/IPullAtomCallback.aidl +++ b/apex/statsd/aidl/android/os/IPullAtomCallback.aidl diff --git a/core/java/android/os/IPullAtomResultReceiver.aidl b/apex/statsd/aidl/android/os/IPullAtomResultReceiver.aidl index bfb35ff0c9d1..00d026e25df3 100644 --- a/core/java/android/os/IPullAtomResultReceiver.aidl +++ b/apex/statsd/aidl/android/os/IPullAtomResultReceiver.aidl @@ -16,7 +16,7 @@ package android.os; -import android.util.StatsEvent; +import android.util.StatsEventParcel; /** * Binder interface to pull atoms for the stats service. @@ -27,6 +27,6 @@ interface IPullAtomResultReceiver { /** * Indicate that a pull request for an atom is complete. */ - oneway void pullFinished(int atomTag, boolean success, in StatsEvent[] output); + oneway void pullFinished(int atomTag, boolean success, in StatsEventParcel[] output); } diff --git a/core/java/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl index 22a25374e064..22a25374e064 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl diff --git a/core/java/android/os/IStatsManager.aidl b/apex/statsd/aidl/android/os/IStatsManager.aidl index 5ebb9f2e4e90..cc62f07a3750 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/apex/statsd/aidl/android/os/IStatsManager.aidl @@ -240,7 +240,7 @@ interface IStatsManager { * Logs an event for watchdog rollbacks. */ oneway void sendWatchdogRollbackOccurredAtom(in int rollbackType, in String packageName, - in long packageVersionCode); + in long packageVersionCode, in int rollbackReason, in String failingPackageName); /** * Returns the most recently registered experiment IDs. diff --git a/core/java/android/os/IStatsPullerCallback.aidl b/apex/statsd/aidl/android/os/IStatsPullerCallback.aidl index c3e1e55dde06..c3e1e55dde06 100644 --- a/core/java/android/os/IStatsPullerCallback.aidl +++ b/apex/statsd/aidl/android/os/IStatsPullerCallback.aidl diff --git a/apex/statsd/aidl/android/util/StatsEventParcel.aidl b/apex/statsd/aidl/android/util/StatsEventParcel.aidl new file mode 100644 index 000000000000..add8bfb47b1a --- /dev/null +++ b/apex/statsd/aidl/android/util/StatsEventParcel.aidl @@ -0,0 +1,8 @@ +package android.util; + +/** + * @hide + */ +parcelable StatsEventParcel { + byte[] buffer; +} diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index c9139b15f223..6fb3bc47859d 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -41,6 +41,7 @@ import android.app.AppOpsManager.HistoricalOps; import android.app.AppOpsManager.HistoricalOpsRequest; import android.app.AppOpsManager.HistoricalPackageOps; import android.app.AppOpsManager.HistoricalUidOps; +import android.app.INotificationManager; import android.app.ProcessMemoryState; import android.app.StatsManager; import android.bluetooth.BluetoothActivityEnergyInfo; @@ -139,6 +140,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.am.MemoryStatUtil.MemoryStat; +import com.android.server.notification.NotificationManagerService; import com.android.server.role.RoleManagerInternal; import com.android.server.stats.IonMemoryUtil.IonAllocations; import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot; @@ -1750,14 +1752,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (statsFiles.size() != 1) { return; } - InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream( - statsFiles.get(0)); - int[] len = new int[1]; - byte[] stats = readFully(stream, len); - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, - wallClockNanos); - e.writeStorage(Arrays.copyOf(stats, len[0])); - pulledData.add(e); + unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles); new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark).delete(); new File( @@ -1773,6 +1768,52 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + private INotificationManager mNotificationManager = + INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + + private void pullNotificationStats(int reportId, int tagId, long elapsedNanos, + long wallClockNanos, + List<StatsLogEventWrapper> pulledData) { + final long callingToken = Binder.clearCallingIdentity(); + try { + // determine last pull tine. Copy file trick from pullProcessStats? + long lastNotificationStatsNs = wallClockNanos - + TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS); + + List<ParcelFileDescriptor> statsFiles = new ArrayList<>(); + long notificationStatsNs = mNotificationManager.pullStats( + lastNotificationStatsNs, reportId, true, statsFiles); + if (statsFiles.size() != 1) { + return; + } + unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles); + } catch (IOException e) { + Log.e(TAG, "Getting notistats failed: ", e); + + } catch (RemoteException e) { + Log.e(TAG, "Getting notistats failed: ", e); + } catch (SecurityException e) { + Log.e(TAG, "Getting notistats failed: ", e); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + + } + + static void unpackStreamedData(int tagId, long elapsedNanos, long wallClockNanos, + List<StatsLogEventWrapper> pulledData, List<ParcelFileDescriptor> statsFiles) + throws IOException { + InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream( + statsFiles.get(0)); + int[] len = new int[1]; + byte[] stats = readFully(stream, len); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, + wallClockNanos); + e.writeStorage(Arrays.copyOf(stats, len[0])); + pulledData.add(e); + } + static byte[] readFully(InputStream stream, int[] outLen) throws IOException { int pos = 0; final int initialAvail = stream.available(); @@ -2621,6 +2662,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullAppOps(elapsedNanos, wallClockNanos, ret); break; } + case StatsLog.NOTIFICATION_REMOTE_VIEWS: { + pullNotificationStats(NotificationManagerService.REPORT_REMOTE_VIEWS, + tagId, elapsedNanos, wallClockNanos, ret); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; diff --git a/api/current.txt b/api/current.txt index 3cf158dbf142..542267c416f6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6834,7 +6834,7 @@ package android.app.admin { method public boolean removeOverrideApn(@NonNull android.content.ComponentName, int); method public boolean removeUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); method public boolean requestBugreport(@NonNull android.content.ComponentName); - method public boolean resetPassword(String, int); + method @Deprecated public boolean resetPassword(String, int); method public boolean resetPasswordWithToken(@NonNull android.content.ComponentName, String, byte[], int); method @Nullable public java.util.List<android.app.admin.NetworkEvent> retrieveNetworkLogs(@Nullable android.content.ComponentName, long); method @Nullable public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(@NonNull android.content.ComponentName); @@ -13453,6 +13453,7 @@ package android.drm { method public android.drm.DrmConvertedStatus closeConvertSession(int); method public android.drm.DrmConvertedStatus convertData(int, byte[]); method public String[] getAvailableDrmEngines(); + method @NonNull public java.util.Collection<android.drm.DrmSupportInfo> getAvailableDrmSupportInfo(); method public android.content.ContentValues getConstraints(String, int); method public android.content.ContentValues getConstraints(android.net.Uri, int); method public int getDrmObjectType(String, String); @@ -23277,6 +23278,8 @@ package android.location { method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback); method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback); method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback); + field public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED"; + field public static final String EXTRA_PROVIDER_ENABLED = "android.location.extra.PROVIDER_ENABLED"; field public static final String EXTRA_PROVIDER_NAME = "android.location.extra.PROVIDER_NAME"; field public static final String GPS_PROVIDER = "gps"; field public static final String KEY_LOCATION_CHANGED = "location"; @@ -38905,6 +38908,7 @@ package android.provider { field public static final int MEDIA_TYPE_IMAGE = 1; // 0x1 field public static final int MEDIA_TYPE_NONE = 0; // 0x0 field public static final int MEDIA_TYPE_PLAYLIST = 4; // 0x4 + field public static final int MEDIA_TYPE_SUBTITLE = 5; // 0x5 field public static final int MEDIA_TYPE_VIDEO = 3; // 0x3 field public static final String MIME_TYPE = "mime_type"; field public static final String PARENT = "parent"; @@ -42824,6 +42828,7 @@ package android.system { method public static String[] listxattr(String) throws android.system.ErrnoException; method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException; method public static android.system.StructStat lstat(String) throws android.system.ErrnoException; + method @NonNull public static java.io.FileDescriptor memfd_create(@NonNull String, int) throws android.system.ErrnoException; method public static void mincore(long, long, byte[]) throws android.system.ErrnoException; method public static void mkdir(String, int) throws android.system.ErrnoException; method public static void mkfifo(String, int) throws android.system.ErrnoException; @@ -43132,6 +43137,7 @@ package android.system { field public static final int MCAST_UNBLOCK_SOURCE; field public static final int MCL_CURRENT; field public static final int MCL_FUTURE; + field public static final int MFD_CLOEXEC; field public static final int MSG_CTRUNC; field public static final int MSG_DONTROUTE; field public static final int MSG_EOR; @@ -44703,7 +44709,6 @@ package android.telephony { field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int"; - field public static final String KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT = "parameters_use_for_5g_nr_signal_bar_int"; field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool"; field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool"; field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array"; diff --git a/api/system-current.txt b/api/system-current.txt index c7bde93c61fd..217943c519af 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1331,8 +1331,22 @@ package android.app.usage { package android.bluetooth { public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile { + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void disableOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void enableOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice); + method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothDevice getActiveDevice(); + method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@Nullable android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int getOptionalCodecsEnabled(@Nullable android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setCodecConfigPreference(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothCodecConfig); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setOptionalCodecsEnabled(@Nullable android.bluetooth.BluetoothDevice, int); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int supportsOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice); + field public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; // 0x0 + field public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; // 0x0 + field public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; // 0x1 + field public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; // 0xffffffff + field public static final int OPTIONAL_CODECS_SUPPORTED = 1; // 0x1 + field public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; // 0xffffffff } public final class BluetoothAdapter { @@ -1361,6 +1375,52 @@ package android.bluetooth { method public void onMetadataChanged(@NonNull android.bluetooth.BluetoothDevice, int, @Nullable byte[]); } + public final class BluetoothCodecConfig implements android.os.Parcelable { + ctor public BluetoothCodecConfig(int, int, int, int, int, long, long, long, long); + ctor public BluetoothCodecConfig(int); + method public int getBitsPerSample(); + method @NonNull public String getCodecName(); + method public int getCodecPriority(); + method public long getCodecSpecific1(); + method public int getCodecType(); + method public int getSampleRate(); + method public boolean isMandatoryCodec(); + field public static final int BITS_PER_SAMPLE_16 = 1; // 0x1 + field public static final int BITS_PER_SAMPLE_24 = 2; // 0x2 + field public static final int BITS_PER_SAMPLE_32 = 4; // 0x4 + field public static final int BITS_PER_SAMPLE_NONE = 0; // 0x0 + field public static final int CHANNEL_MODE_MONO = 1; // 0x1 + field public static final int CHANNEL_MODE_NONE = 0; // 0x0 + field public static final int CHANNEL_MODE_STEREO = 2; // 0x2 + field public static final int CODEC_PRIORITY_DEFAULT = 0; // 0x0 + field public static final int CODEC_PRIORITY_DISABLED = -1; // 0xffffffff + field public static final int CODEC_PRIORITY_HIGHEST = 1000000; // 0xf4240 + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothCodecConfig> CREATOR; + field public static final int SAMPLE_RATE_176400 = 16; // 0x10 + field public static final int SAMPLE_RATE_192000 = 32; // 0x20 + field public static final int SAMPLE_RATE_44100 = 1; // 0x1 + field public static final int SAMPLE_RATE_48000 = 2; // 0x2 + field public static final int SAMPLE_RATE_88200 = 4; // 0x4 + field public static final int SAMPLE_RATE_96000 = 8; // 0x8 + field public static final int SAMPLE_RATE_NONE = 0; // 0x0 + field public static final int SOURCE_CODEC_TYPE_AAC = 1; // 0x1 + field public static final int SOURCE_CODEC_TYPE_APTX = 2; // 0x2 + field public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; // 0x3 + field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240 + field public static final int SOURCE_CODEC_TYPE_LDAC = 4; // 0x4 + field public static final int SOURCE_CODEC_TYPE_MAX = 5; // 0x5 + field public static final int SOURCE_CODEC_TYPE_SBC = 0; // 0x0 + } + + public final class BluetoothCodecStatus implements android.os.Parcelable { + ctor public BluetoothCodecStatus(@Nullable android.bluetooth.BluetoothCodecConfig, @Nullable android.bluetooth.BluetoothCodecConfig[], @Nullable android.bluetooth.BluetoothCodecConfig[]); + method @Nullable public android.bluetooth.BluetoothCodecConfig getCodecConfig(); + method @Nullable public android.bluetooth.BluetoothCodecConfig[] getCodecsLocalCapabilities(); + method @Nullable public android.bluetooth.BluetoothCodecConfig[] getCodecsSelectableCapabilities(); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothCodecStatus> CREATOR; + field public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; + } + public final class BluetoothDevice implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess(); method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int); @@ -6075,6 +6135,7 @@ package android.os { } public class Binder implements android.os.IBinder { + method public int handleShellCommand(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.ParcelFileDescriptor, @NonNull android.os.ParcelFileDescriptor, @NonNull String[]); method public static void setProxyTransactListener(@Nullable android.os.Binder.ProxyTransactListener); } @@ -6623,7 +6684,7 @@ package android.os.connectivity { } public final class WifiActivityEnergyInfo implements android.os.Parcelable { - ctor public WifiActivityEnergyInfo(long, int, long, long, long, long, long); + ctor public WifiActivityEnergyInfo(long, int, long, long, long, long); method public int describeContents(); method public long getControllerEnergyUsedMicroJoules(); method public long getControllerIdleDurationMillis(); @@ -6980,6 +7041,11 @@ package android.provider { field public static final int FLAG_REMOVABLE_USB = 524288; // 0x80000 } + public final class MediaStore { + method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public static java.util.Set<java.lang.String> getRecentExternalVolumeNames(@NonNull android.content.Context); + method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException; + } + public abstract class SearchIndexableData { ctor public SearchIndexableData(); ctor public SearchIndexableData(android.content.Context); @@ -7237,6 +7303,10 @@ package android.provider { field public static final String SUB_ID = "sub_id"; } + public static final class Telephony.SimInfo { + field @NonNull public static final android.net.Uri CONTENT_URI; + } + public static final class Telephony.Sms.Intents { field public static final String ACTION_SMS_EMERGENCY_CB_RECEIVED = "android.provider.action.SMS_EMERGENCY_CB_RECEIVED"; } diff --git a/api/test-current.txt b/api/test-current.txt index 3ce8c5ead4cb..3e144697ee28 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2455,7 +2455,7 @@ package android.provider { method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static void deleteContributedMedia(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; method @NonNull public static java.io.File getVolumePath(@NonNull String) throws java.io.FileNotFoundException; - method @NonNull public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException; + method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException; method public static android.net.Uri scanFile(android.content.Context, java.io.File); method public static android.net.Uri scanFileFromShell(android.content.Context, java.io.File); method public static void scanVolume(android.content.Context, java.io.File); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index d10a661c6d83..3c5ad4231133 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1417,7 +1417,10 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn, const android::String16& packageNameIn, - const int64_t packageVersionCodeIn) { + const int64_t packageVersionCodeIn, + const int32_t rollbackReasonIn, + const android::String16& + failingPackageNameIn) { // Note: We skip the usage stats op check here since we do not have a package name. // This is ok since we are overloading the usage_stats permission. // This method only sends data, it does not receive it. @@ -1439,7 +1442,8 @@ Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackType } android::util::stats_write(android::util::WATCHDOG_ROLLBACK_OCCURRED, - rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn); + rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn, + rollbackReasonIn, String8(failingPackageNameIn).string()); // Fast return to save disk read. if (rollbackTypeIn != android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 8c98e7b96936..50b1014f4e8a 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -214,7 +214,9 @@ public: virtual Status sendWatchdogRollbackOccurredAtom( const int32_t rollbackTypeIn, const android::String16& packageNameIn, - const int64_t packageVersionCodeIn) override; + const int64_t packageVersionCodeIn, + const int32_t rollbackReasonIn, + const android::String16& failingPackageNameIn) override; /** * Binder call to get registered experiment IDs. diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 2efb78943812..f1ea4c9e0c68 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -112,7 +112,7 @@ message Atom { TouchEventReported touch_event_reported = 34; WakeupAlarmOccurred wakeup_alarm_occurred = 35; KernelWakeupReported kernel_wakeup_reported = 36; - WifiLockStateChanged wifi_lock_state_changed = 37; + WifiLockStateChanged wifi_lock_state_changed = 37 [(log_from_module) = "wifi"]; WifiSignalStrengthChanged wifi_signal_strength_changed = 38; WifiScanStateChanged wifi_scan_state_changed = 39; PhoneSignalStrengthChanged phone_signal_strength_changed = 40; @@ -356,7 +356,7 @@ message Atom { } // Pulled events will start at field 10000. - // Next: 10065 + // Next: 10067 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -423,6 +423,7 @@ message Atom { SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063; ProcessMemorySnapshot process_memory_snapshot = 10064; VmsClientStats vms_client_stats = 10065; + NotificationRemoteViews notification_remote_views = 10066; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -1742,6 +1743,19 @@ message WatchdogRollbackOccurred { optional string package_name = 2; optional int32 package_version_code = 3; + + enum RollbackReasonType { + REASON_UNKNOWN = 0; + REASON_NATIVE_CRASH = 1; + REASON_EXPLICIT_HEALTH_CHECK = 2; + REASON_APP_CRASH = 3; + REASON_APP_NOT_RESPONDING = 4; + } + optional RollbackReasonType rollback_reason = 4; + + // Set by RollbackPackageHealthObserver to be the package that is failing when a rollback + // is initiated. Empty if the package is unknown. + optional string failing_package_name = 5; } /** @@ -4947,6 +4961,24 @@ message ProcStatsPkgProc { optional ProcessStatsSectionProto proc_stats_section = 1; } +// Next Tag: 2 +message PackageRemoteViewInfoProto { + optional string package_name = 1; + // add per-package additional info here (like channels) +} + +// Next Tag: 2 +message NotificationRemoteViewsProto { + repeated PackageRemoteViewInfoProto package_remote_view_info = 1; +} + +/** + * Pulled from NotificationManagerService.java + */ +message NotificationRemoteViews { + optional NotificationRemoteViewsProto notification_remote_views = 1; +} + message PowerProfileProto { optional double cpu_suspend = 1; @@ -5960,6 +5992,8 @@ message PermissionGrantRequestResultReported { IGNORED_RESTRICTED_PERMISSION = 9; // one time permission was granted by user action USER_GRANTED_ONE_TIME = 10; + // user ignored request by leaving the request screen without choosing any option + USER_IGNORED = 11; } // The result of the permission grant optional Result result = 6; diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp index 6bd05452f3b0..6b6fe7d9617f 100644 --- a/cmds/statsd/src/external/PullResultReceiver.cpp +++ b/cmds/statsd/src/external/PullResultReceiver.cpp @@ -25,12 +25,13 @@ namespace os { namespace statsd { PullResultReceiver::PullResultReceiver( - std::function<void(int32_t, bool, const vector<android::util::StatsEvent>&)> pullFinishCb) + std::function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)> + pullFinishCb) : pullFinishCallback(std::move(pullFinishCb)) { } Status PullResultReceiver::pullFinished(int32_t atomTag, bool success, - const vector<StatsEvent>& output) { + const vector<StatsEventParcel>& output) { pullFinishCallback(atomTag, success, output); return Status::ok(); } @@ -40,4 +41,4 @@ PullResultReceiver::~PullResultReceiver() { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h index f731f778a337..17d06e4ff4db 100644 --- a/cmds/statsd/src/external/PullResultReceiver.h +++ b/cmds/statsd/src/external/PullResultReceiver.h @@ -24,7 +24,7 @@ namespace statsd { class PullResultReceiver : public BnPullAtomResultReceiver { public: - PullResultReceiver(function<void(int32_t, bool, const vector<android::util::StatsEvent>&)> + PullResultReceiver(function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)> pullFinishCallback); ~PullResultReceiver(); @@ -32,10 +32,11 @@ public: * Binder call for finishing a pull. */ binder::Status pullFinished(int32_t atomTag, bool success, - const vector<android::util::StatsEvent>& output) override; + const vector<android::util::StatsEventParcel>& output) override; private: - function<void(int32_t, bool, const vector<android::util::StatsEvent>&)> pullFinishCallback; + function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)> + pullFinishCallback; }; } // namespace statsd diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp index 92db68477dbd..f5b1e7f78736 100644 --- a/cmds/statsd/src/external/StatsCallbackPuller.cpp +++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp @@ -20,7 +20,7 @@ #include "StatsCallbackPuller.h" #include <android/os/IPullAtomCallback.h> -#include <android/util/StatsEvent.h> +#include <android/util/StatsEventParcel.h> #include "PullResultReceiver.h" #include "StatsPullerManager.h" @@ -57,13 +57,19 @@ bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { sp<PullResultReceiver> resultReceiver = new PullResultReceiver( [cv_mutex, cv, pullFinish, pullSuccess, sharedData]( - int32_t atomTag, bool success, const vector<StatsEvent>& output) { + int32_t atomTag, bool success, const vector<StatsEventParcel>& output) { // This is the result of the pull, executing in a statsd binder thread. // The pull could have taken a long time, and we should only modify // data (the output param) if the pointer is in scope and the pull did not time out. { lock_guard<mutex> lk(*cv_mutex); - // TODO: fill the shared vector of LogEvents once StatsEvent is complete. + for (const StatsEventParcel& parcel: output) { + shared_ptr<LogEvent> event = + make_shared<LogEvent>(const_cast<uint8_t*>(parcel.buffer.data()), + parcel.buffer.size(), + /*uid=*/ -1); + sharedData->push_back(event); + } *pullSuccess = success; *pullFinish = true; } diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index b5bad0530503..615af89e3186 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -281,6 +281,9 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{.atomTag = android::util::VMS_CLIENT_STATS}, {.additiveFields = {5, 6, 7, 8, 9, 10}, .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}}, + // NotiifcationRemoteViews. + {{.atomTag = android::util::NOTIFICATION_REMOTE_VIEWS}, + {.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index eb78ebc521e1..b7d169de89c8 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -159,7 +159,7 @@ ValueMetricProducer::ValueMetricProducer( // Kicks off the puller immediately if condition is true and diff based. if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { - pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition); + pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); } // Now that activations are processed, start the condition timer if needed. mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue, @@ -216,7 +216,7 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, invalidateCurrentBucket(); break; case NO_TIME_CONSTRAINTS: - pullAndMatchEventsLocked(dumpTimeNs, mCondition); + pullAndMatchEventsLocked(dumpTimeNs); break; } } @@ -366,7 +366,7 @@ void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) // Pull on active state changes. if (!isEventTooLate) { if (mIsPulled) { - pullAndMatchEventsLocked(eventTimeNs, mCondition); + pullAndMatchEventsLocked(eventTimeNs); } // When active state changes from true to false, clear diff base but don't // reset other counters as we may accumulate more value in the bucket. @@ -425,7 +425,7 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, // called before #onDataPulled. if (mIsPulled && (newCondition == ConditionState::kTrue || mCondition == ConditionState::kTrue)) { - pullAndMatchEventsLocked(eventTimeNs, newCondition); + pullAndMatchEventsLocked(eventTimeNs); } // For metrics that use diff, when condition changes from true to false, @@ -443,8 +443,7 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, mConditionTimer.onConditionChanged(mCondition, eventTimeNs); } -void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, - ConditionState condition) { +void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { vector<std::shared_ptr<LogEvent>> allData; if (!mPullerManager->Pull(mPullTagId, &allData)) { ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs); @@ -452,7 +451,7 @@ void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, return; } - accumulateEvents(allData, timestampNs, timestampNs, condition); + accumulateEvents(allData, timestampNs, timestampNs); } int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) { @@ -474,7 +473,7 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven if (isEventLate) { // If the event is late, we are in the middle of a bucket. Just // process the data without trying to snap the data to the nearest bucket. - accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs, mCondition); + accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs); } else { // For scheduled pulled data, the effective event time is snap to the nearest // bucket end. In the case of waking up from a deep sleep state, we will @@ -488,7 +487,7 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1; StatsdStats::getInstance().noteBucketBoundaryDelayNs( mMetricId, originalPullTimeNs - bucketEndTime); - accumulateEvents(allData, originalPullTimeNs, bucketEndTime, mCondition); + accumulateEvents(allData, originalPullTimeNs, bucketEndTime); } } } @@ -499,8 +498,7 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven } void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, - int64_t originalPullTimeNs, int64_t eventElapsedTimeNs, - ConditionState condition) { + int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) { bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs; if (isEventLate) { VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 206e602dd1c7..2033a2a0cd02 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -78,7 +78,7 @@ public: return; } if (mIsPulled && mCondition) { - pullAndMatchEventsLocked(eventTimeNs, mCondition); + pullAndMatchEventsLocked(eventTimeNs); } flushCurrentBucketLocked(eventTimeNs, eventTimeNs); }; @@ -188,11 +188,10 @@ private: bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey); - void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition); + void pullAndMatchEventsLocked(const int64_t timestampNs); void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, - int64_t originalPullTimeNs, int64_t eventElapsedTimeNs, - ConditionState condition); + int64_t originalPullTimeNs, int64_t eventElapsedTimeNs); ValueBucket buildPartialBucket(int64_t bucketEndTime, const std::vector<Interval>& intervals); diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp index 90ce1e90142e..ef59c9242cb2 100644 --- a/cmds/statsd/src/state/StateTracker.cpp +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -139,6 +139,13 @@ void StateTracker::handlePartialReset(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey) { VLOG("StateTracker handle partial reset"); if (mStateMap.find(primaryKey) != mStateMap.end()) { + for (auto l : mListeners) { + auto sl = l.promote(); + if (sl != nullptr) { + sl->onStateChanged(eventTimeNs, mAtomId, primaryKey, + mStateMap.find(primaryKey)->second.state, mDefaultState); + } + } mStateMap.erase(primaryKey); } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 35a48a82cd5e..49a8e2f3f816 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -6254,6 +6254,7 @@ public final class ActivityThread extends ClientTransactionHandler { // send up app name; do this *before* waiting for debugger Process.setArgV0(data.processName); android.ddm.DdmHandleAppName.setAppName(data.processName, + data.appInfo.packageName, UserHandle.myUserId()); VMRuntime.setProcessPackageName(data.appInfo.packageName); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 0957dba4eac1..86f52af1a13b 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -207,4 +207,6 @@ interface INotificationManager void setPrivateNotificationsAllowed(boolean allow); boolean getPrivateNotificationsAllowed(); + + long pullStats(long startNs, int report, boolean doAgg, out List<ParcelFileDescriptor> stats); } diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 2ef05105825a..90cd51f8649f 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -34,6 +34,7 @@ import android.os.ServiceManager; import android.util.AndroidException; import android.util.Slog; import android.util.StatsEvent; +import android.util.StatsEventParcel; import com.android.internal.annotations.GuardedBy; @@ -540,10 +541,12 @@ public final class StatsManager { mExecutor.execute(() -> { List<StatsEvent> data = new ArrayList<>(); boolean success = mCallback.onPullAtom(atomTag, data); - StatsEvent[] arr = new StatsEvent[data.size()]; - arr = data.toArray(arr); + StatsEventParcel[] parcels = new StatsEventParcel[data.size()]; + for (int i = 0; i < data.size(); i++) { + parcels[i].buffer = data.get(i).getBytes(); + } try { - resultReceiver.pullFinished(atomTag, success, arr); + resultReceiver.pullFinished(atomTag, success, parcels); } catch (RemoteException e) { Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 34ceb08f39bf..915e4572c035 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3742,17 +3742,35 @@ public class DevicePolicyManager { /** * Force a new password for device unlock (the password needed to access the entire device) or * the work profile challenge on the current user. This takes effect immediately. - * <p> - * <em>For device owner and profile owners targeting SDK level - * {@link android.os.Build.VERSION_CODES#O} or above, this API is no longer available and will - * throw {@link SecurityException}. Please use the new API {@link #resetPasswordWithToken} - * instead. </em> - * <p> - * <em>Note: This API has been limited as of {@link android.os.Build.VERSION_CODES#N} for - * device admins that are not device owner and not profile owner. - * The password can now only be changed if there is currently no password set. Device owner - * and profile owner can still do this when user is unlocked and does not have a managed - * profile.</em> + * + * <p> Before {@link android.os.Build.VERSION_CODES#N}, this API is available to device admin, + * profile owner and device owner. Starting from {@link android.os.Build.VERSION_CODES#N}, + * legacy device admin (who is not also profile owner or device owner) can only call this + * API to set a new password if there is currently no password set. Profile owner and device + * owner can continue to force change an existing password as long as the target user is + * unlocked, although device owner will not be able to call this API at all if there is also a + * managed profile on the device. + * + * <p> Between {@link android.os.Build.VERSION_CODES#O}, + * {@link android.os.Build.VERSION_CODES#P} and {@link android.os.Build.VERSION_CODES#Q}, + * profile owner and devices owner targeting SDK level {@link android.os.Build.VERSION_CODES#O} + * or above who attempt to call this API will receive {@link SecurityException}; they are + * encouraged to migrate to the new {@link #resetPasswordWithToken} API instead. + * Profile owner and device owner targeting older SDK levels are not affected: they continue + * to experience the existing behaviour described in the previous paragraph. + * + * <p><em>Starting from {@link android.os.Build.VERSION_CODES#R}, this API is no longer + * supported in most cases.</em> Device owner and profile owner calling + * this API will receive {@link SecurityException} if they target SDK level + * {@link android.os.Build.VERSION_CODES#O} or above, or they will receive a silent failure + * (API returning {@code false}) if they target lower SDK level. + * For legacy device admins, this API throws {@link SecurityException} if they target SDK level + * {@link android.os.Build.VERSION_CODES#N} or above, and returns {@code false} otherwise. Only + * privileged apps holding RESET_PASSWORD permission which are part of + * the system factory image can still call this API to set a new password if there is currently + * no password set. In this case, if the device already has a password, this API will throw + * {@link SecurityException}. + * * <p> * The given password must be sufficient for the current password quality and length constraints * as returned by {@link #getPasswordQuality(ComponentName)} and @@ -3760,12 +3778,7 @@ public class DevicePolicyManager { * it will be rejected and false returned. Note that the password may be a stronger quality * (containing alphanumeric characters when the requested quality is only numeric), in which * case the currently active quality will be increased to match. - * <p> - * Calling with a null or empty password will clear any existing PIN, pattern or password if the - * current password constraints allow it. <em>Note: This will not work in - * {@link android.os.Build.VERSION_CODES#N} and later for managed profiles, or for device admins - * that are not device owner or profile owner. Once set, the password cannot be changed to null - * or empty except by these admins.</em> + * * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, this * methods does nothing. * <p> @@ -3777,11 +3790,13 @@ public class DevicePolicyManager { * @param flags May be 0 or combination of {@link #RESET_PASSWORD_REQUIRE_ENTRY} and * {@link #RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT}. * @return Returns true if the password was applied, or false if it is not acceptable for the - * current constraints or if the user has not been decrypted yet. + * current constraints. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} * @throws IllegalStateException if the calling user is locked or has a managed profile. + * @deprecated Please use {@link #resetPasswordWithToken} instead. */ + @Deprecated @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN) public boolean resetPassword(String password, int flags) { throwIfParentInstance("resetPassword"); @@ -5555,7 +5570,12 @@ public class DevicePolicyManager { * device, for this user. After setting this, no applications running as this user will be able * to access any cameras on the device. * <p> - * If the caller is device owner, then the restriction will be applied to all users. + * This method can be called on the {@link DevicePolicyManager} instance, + * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be + * the profile owner of an organization-owned managed profile. + * <p> + * If the caller is device owner or called on the parent instance, then the + * restriction will be applied to all users. * <p> * The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call this method; if it has @@ -5567,10 +5587,9 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}. */ public void setCameraDisabled(@NonNull ComponentName admin, boolean disabled) { - throwIfParentInstance("setCameraDisabled"); if (mService != null) { try { - mService.setCameraDisabled(admin, disabled); + mService.setCameraDisabled(admin, disabled, mParentInstance); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -5580,11 +5599,15 @@ public class DevicePolicyManager { /** * Determine whether or not the device's cameras have been disabled for this user, * either by the calling admin, if specified, or all admins. + * <p> + * This method can be called on the {@link DevicePolicyManager} instance, + * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be + * the profile owner of an organization-owned managed profile. + * * @param admin The name of the admin component to check, or {@code null} to check whether any admins * have disabled the camera */ public boolean getCameraDisabled(@Nullable ComponentName admin) { - throwIfParentInstance("getCameraDisabled"); return getCameraDisabled(admin, myUserId()); } @@ -5593,7 +5616,7 @@ public class DevicePolicyManager { public boolean getCameraDisabled(@Nullable ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getCameraDisabled(admin, userHandle); + return mService.getCameraDisabled(admin, userHandle, mParentInstance); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -7893,7 +7916,8 @@ public class DevicePolicyManager { * for the list of keys. * @throws SecurityException if {@code admin} is not a device or profile owner. */ - public void addUserRestriction(@NonNull ComponentName admin, String key) { + public void addUserRestriction(@NonNull ComponentName admin, + @UserManager.UserRestrictionKey String key) { throwIfParentInstance("addUserRestriction"); if (mService != null) { try { @@ -7915,7 +7939,8 @@ public class DevicePolicyManager { * for the list of keys. * @throws SecurityException if {@code admin} is not a device or profile owner. */ - public void clearUserRestriction(@NonNull ComponentName admin, String key) { + public void clearUserRestriction(@NonNull ComponentName admin, + @UserManager.UserRestrictionKey String key) { throwIfParentInstance("clearUserRestriction"); if (mService != null) { try { @@ -9344,7 +9369,6 @@ public class DevicePolicyManager { * <li>{@link #setPasswordExpirationTimeout}</li> * <li>{@link #getPasswordExpiration}</li> * <li>{@link #getPasswordMaximumLength}</li> - * <li>{@link #getPasswordComplexity}</li> * <li>{@link #isActivePasswordSufficient}</li> * <li>{@link #getCurrentFailedPasswordAttempts}</li> * <li>{@link #getMaximumFailedPasswordsForWipe}</li> @@ -9359,6 +9383,14 @@ public class DevicePolicyManager { * <li>{@link #getRequiredStrongAuthTimeout}</li> * <li>{@link #setRequiredStrongAuthTimeout}</li> * </ul> + * <p> + * The following methods are supported for the parent instance but can only be called by the + * profile owner of a managed profile that was created during the device provisioning flow: + * <ul> + * <li>{@link #getPasswordComplexity}</li> + * <li>{@link #setCameraDisabled}</li> + * <li>{@link #getCameraDisabled}</li> + * </ul> * * <p>The following methods can be called by the profile owner of a managed profile * on an organization-owned device: diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index 713126ee9341..f299d456a18f 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -145,15 +145,6 @@ public abstract class DevicePolicyManagerInternal { public abstract void reportSeparateProfileChallengeChanged(@UserIdInt int userId); /** - * Check whether the user could have their password reset in an untrusted manor due to there - * being an admin which can call {@link #resetPassword} to reset the password without knowledge - * of the previous password. - * - * @param userId The user in question - */ - public abstract boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId); - - /** * Return text of error message if printing is disabled. * Called by Print Service when printing is disabled by PO or DO when printing is attempted. * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index f55026c76906..34246fa808bd 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -114,8 +114,8 @@ interface IDevicePolicyManager { boolean requestBugreport(in ComponentName who); - void setCameraDisabled(in ComponentName who, boolean disabled); - boolean getCameraDisabled(in ComponentName who, int userHandle); + void setCameraDisabled(in ComponentName who, boolean disabled, boolean parent); + boolean getCameraDisabled(in ComponentName who, int userHandle, boolean parent); void setScreenCaptureDisabled(in ComponentName who, boolean disabled); boolean getScreenCaptureDisabled(in ComponentName who, int userHandle); diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 8ed61b6c87e3..64df0e84f6dc 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -32,6 +33,8 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -154,13 +157,22 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public static final int STATE_NOT_PLAYING = 11; + /** @hide */ + @IntDef(prefix = "OPTIONAL_CODECS_", value = { + OPTIONAL_CODECS_SUPPORT_UNKNOWN, + OPTIONAL_CODECS_NOT_SUPPORTED, + OPTIONAL_CODECS_SUPPORTED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface OptionalCodecsSupportStatus {} + /** * We don't have a stored preference for whether or not the given A2DP sink device supports * optional codecs. * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; /** @@ -168,7 +180,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; /** @@ -176,16 +188,25 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_SUPPORTED = 1; + /** @hide */ + @IntDef(prefix = "OPTIONAL_CODECS_PREF_", value = { + OPTIONAL_CODECS_PREF_UNKNOWN, + OPTIONAL_CODECS_PREF_DISABLED, + OPTIONAL_CODECS_PREF_ENABLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface OptionalCodecsPreferenceStatus {} + /** - * We don't have a stored preference for whether optional codecs should be enabled or disabled - * for the given A2DP device. + * We don't have a stored preference for whether optional codecs should be enabled or + * disabled for the given A2DP device. * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; /** @@ -193,7 +214,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; /** @@ -201,7 +222,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; private BluetoothAdapter mAdapter; @@ -248,13 +269,12 @@ public final class BluetoothA2dp implements BluetoothProfile { * the state. Users can get the connection state of the profile * from this intent. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); @@ -289,13 +309,12 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -384,14 +403,12 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted * with the active device. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device the remote Bluetooth device. Could be null to clear * the active device and stop streaming audio to a Bluetooth device. * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); @@ -412,16 +429,13 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Get the connected device that is active. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} - * permission. - * * @return the connected device that is active or null if no device * is active * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @SystemApi @Nullable - @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { @@ -441,7 +455,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * Set priority of the profile * * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, + * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} * * @param device Paired bluetooth device * @param priority @@ -626,8 +640,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @UnsupportedAppUsage - public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { + @SystemApi + @Nullable + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { final IBluetoothA2dp service = getService(); @@ -652,9 +668,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @UnsupportedAppUsage - public void setCodecConfigPreference(BluetoothDevice device, - BluetoothCodecConfig codecConfig) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void setCodecConfigPreference(@Nullable BluetoothDevice device, + @Nullable BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); try { final IBluetoothA2dp service = getService(); @@ -676,8 +693,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage - public void enableOptionalCodecs(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void enableOptionalCodecs(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); enableDisableOptionalCodecs(device, true); } @@ -689,8 +707,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage - public void disableOptionalCodecs(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void disableOptionalCodecs(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); enableDisableOptionalCodecs(device, false); } @@ -728,8 +747,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @UnsupportedAppUsage - public int supportsOptionalCodecs(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @OptionalCodecsSupportStatus + public int supportsOptionalCodecs(@Nullable BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -738,7 +759,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); + Log.e(TAG, "Error talking to BT service in supportsOptionalCodecs()", e); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; } } @@ -751,8 +772,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage - public int getOptionalCodecsEnabled(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @OptionalCodecsPreferenceStatus + public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -761,7 +784,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_PREF_UNKNOWN; } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); + Log.e(TAG, "Error talking to BT service in getOptionalCodecsEnabled()", e); return OPTIONAL_CODECS_PREF_UNKNOWN; } } @@ -775,8 +798,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage - public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device, + @OptionalCodecsPreferenceStatus int value) { try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java index c17834aa8e52..cf3367602aa9 100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -320,7 +320,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * Set priority of the profile * * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, + * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} * * @param device Paired bluetooth device * @param priority diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 9b5280d48f45..3f8cb627e382 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -862,18 +862,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isEnabled() { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isEnabled(); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - return false; + return getState() == BluetoothAdapter.STATE_ON; } /** diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 36f3a1eeba79..08d0797997b5 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -16,10 +16,15 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -29,78 +34,131 @@ import java.util.Objects; * * {@hide} */ +@SystemApi public final class BluetoothCodecConfig implements Parcelable { // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: // hardware/libhardware/include/hardware/bt_av.h - @UnsupportedAppUsage + + /** @hide */ + @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = { + SOURCE_CODEC_TYPE_SBC, + SOURCE_CODEC_TYPE_AAC, + SOURCE_CODEC_TYPE_APTX, + SOURCE_CODEC_TYPE_APTX_HD, + SOURCE_CODEC_TYPE_LDAC, + SOURCE_CODEC_TYPE_MAX, + SOURCE_CODEC_TYPE_INVALID + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SourceCodecType {} + public static final int SOURCE_CODEC_TYPE_SBC = 0; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_AAC = 1; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_APTX = 2; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_LDAC = 4; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_MAX = 5; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; - @UnsupportedAppUsage + /** @hide */ + @IntDef(prefix = "CODEC_PRIORITY_", value = { + CODEC_PRIORITY_DISABLED, + CODEC_PRIORITY_DEFAULT, + CODEC_PRIORITY_HIGHEST + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CodecPriority {} + public static final int CODEC_PRIORITY_DISABLED = -1; - @UnsupportedAppUsage + public static final int CODEC_PRIORITY_DEFAULT = 0; - @UnsupportedAppUsage + public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; - @UnsupportedAppUsage + + /** @hide */ + @IntDef(prefix = "SAMPLE_RATE_", value = { + SAMPLE_RATE_NONE, + SAMPLE_RATE_44100, + SAMPLE_RATE_48000, + SAMPLE_RATE_88200, + SAMPLE_RATE_96000, + SAMPLE_RATE_176400, + SAMPLE_RATE_192000 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SampleRate {} + public static final int SAMPLE_RATE_NONE = 0; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_44100 = 0x1 << 0; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_48000 = 0x1 << 1; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_88200 = 0x1 << 2; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_96000 = 0x1 << 3; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_176400 = 0x1 << 4; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_192000 = 0x1 << 5; - @UnsupportedAppUsage + + /** @hide */ + @IntDef(prefix = "BITS_PER_SAMPLE_", value = { + BITS_PER_SAMPLE_NONE, + BITS_PER_SAMPLE_16, + BITS_PER_SAMPLE_24, + BITS_PER_SAMPLE_32 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BitsPerSample {} + public static final int BITS_PER_SAMPLE_NONE = 0; - @UnsupportedAppUsage + public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; - @UnsupportedAppUsage + public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; - @UnsupportedAppUsage + public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; - @UnsupportedAppUsage + + /** @hide */ + @IntDef(prefix = "CHANNEL_MODE_", value = { + CHANNEL_MODE_NONE, + CHANNEL_MODE_MONO, + CHANNEL_MODE_STEREO + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ChannelMode {} + public static final int CHANNEL_MODE_NONE = 0; - @UnsupportedAppUsage + public static final int CHANNEL_MODE_MONO = 0x1 << 0; - @UnsupportedAppUsage + public static final int CHANNEL_MODE_STEREO = 0x1 << 1; - private final int mCodecType; - private int mCodecPriority; - private final int mSampleRate; - private final int mBitsPerSample; - private final int mChannelMode; + private final @SourceCodecType int mCodecType; + private @CodecPriority int mCodecPriority; + private final @SampleRate int mSampleRate; + private final @BitsPerSample int mBitsPerSample; + private final @ChannelMode int mChannelMode; private final long mCodecSpecific1; private final long mCodecSpecific2; private final long mCodecSpecific3; private final long mCodecSpecific4; - @UnsupportedAppUsage - public BluetoothCodecConfig(int codecType, int codecPriority, - int sampleRate, int bitsPerSample, - int channelMode, long codecSpecific1, + public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, + @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, + @ChannelMode int channelMode, long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { mCodecType = codecType; @@ -114,8 +172,7 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } - @UnsupportedAppUsage - public BluetoothCodecConfig(int codecType) { + public BluetoothCodecConfig(@SourceCodecType int codecType) { mCodecType = codecType; mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE; @@ -144,6 +201,12 @@ public final class BluetoothCodecConfig implements Parcelable { return false; } + /** + * Returns a hash based on the config values + * + * @return a hash based on the config values + * @hide + */ @Override public int hashCode() { return Objects.hash(mCodecType, mCodecPriority, mSampleRate, @@ -155,6 +218,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether the object contains valid codec configuration. * * @return true if the object contains valid codec configuration, otherwise false. + * @hide */ public boolean isValid() { return (mSampleRate != SAMPLE_RATE_NONE) @@ -242,6 +306,12 @@ public final class BluetoothCodecConfig implements Parcelable { + ",mCodecSpecific4:" + mCodecSpecific4 + "}"; } + /** + * Always returns 0 + * + * @return 0 + * @hide + */ @Override public int describeContents() { return 0; @@ -271,6 +341,14 @@ public final class BluetoothCodecConfig implements Parcelable { } }; + /** + * Flattens the object to a parcel + * + * @param out The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * + * @hide + */ @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mCodecType); @@ -289,7 +367,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec name */ - public String getCodecName() { + public @NonNull String getCodecName() { switch (mCodecType) { case SOURCE_CODEC_TYPE_SBC: return "SBC"; @@ -315,8 +393,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ - @UnsupportedAppUsage - public int getCodecType() { + public @SourceCodecType int getCodecType() { return mCodecType; } @@ -336,8 +413,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ - @UnsupportedAppUsage - public int getCodecPriority() { + public @CodecPriority int getCodecPriority() { return mCodecPriority; } @@ -347,9 +423,10 @@ public final class BluetoothCodecConfig implements Parcelable { * means higher priority. If 0, reset to default. * * @param codecPriority the codec priority + * @hide */ @UnsupportedAppUsage - public void setCodecPriority(int codecPriority) { + public void setCodecPriority(@CodecPriority int codecPriority) { mCodecPriority = codecPriority; } @@ -366,8 +443,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ - @UnsupportedAppUsage - public int getSampleRate() { + public @SampleRate int getSampleRate() { return mSampleRate; } @@ -381,8 +457,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ - @UnsupportedAppUsage - public int getBitsPerSample() { + public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } @@ -394,9 +469,10 @@ public final class BluetoothCodecConfig implements Parcelable { * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO} * * @return the codec channel mode + * @hide */ @UnsupportedAppUsage - public int getChannelMode() { + public @ChannelMode int getChannelMode() { return mChannelMode; } @@ -405,7 +481,6 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ - @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } @@ -414,6 +489,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Gets a codec specific value2. * * @return a codec specific value2 + * @hide */ @UnsupportedAppUsage public long getCodecSpecific2() { @@ -424,6 +500,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Gets a codec specific value3. * * @return a codec specific value3 + * @hide */ @UnsupportedAppUsage public long getCodecSpecific3() { @@ -434,6 +511,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Gets a codec specific value4. * * @return a codec specific value4 + * @hide */ @UnsupportedAppUsage public long getCodecSpecific4() { @@ -445,6 +523,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param valueSet the value set presented by a bitmask * @return true if the valueSet contains zero or single bit, otherwise false. + * @hide */ private static boolean hasSingleBit(int valueSet) { return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0); @@ -454,6 +533,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether the object contains none or single sample rate. * * @return true if the object contains none or single sample rate, otherwise false. + * @hide */ public boolean hasSingleSampleRate() { return hasSingleBit(mSampleRate); @@ -463,6 +543,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether the object contains none or single bits per sample. * * @return true if the object contains none or single bits per sample, otherwise false. + * @hide */ public boolean hasSingleBitsPerSample() { return hasSingleBit(mBitsPerSample); @@ -472,6 +553,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether the object contains none or single channel mode. * * @return true if the object contains none or single channel mode, otherwise false. + * @hide */ public boolean hasSingleChannelMode() { return hasSingleBit(mChannelMode); @@ -482,6 +564,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param other the codec config to compare against * @return true if the audio feeding parameters are same, otherwise false + * @hide */ public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) { return (other != null && other.mSampleRate == mSampleRate @@ -495,6 +578,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param other the codec config to compare against * @return true if the audio feeding parameters are similar, otherwise false. + * @hide */ public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { if (other == null || mCodecType != other.mCodecType) { @@ -526,6 +610,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param other the codec config to compare against * @return true if the codec specific parameters are the same, otherwise false. + * @hide */ public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { if (other == null && mCodecType != other.mCodecType) { diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 58a764a85bed..b6e77391da5d 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,7 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,6 +32,7 @@ import java.util.Objects; * * {@hide} */ +@SystemApi public final class BluetoothCodecStatus implements Parcelable { /** * Extra for the codec configuration intents of the individual profiles. @@ -39,17 +40,16 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = - "android.bluetooth.codec.extra.CODEC_STATUS"; + "android.bluetooth.extra.CODEC_STATUS"; private final @Nullable BluetoothCodecConfig mCodecConfig; private final BluetoothCodecConfig[] mCodecsLocalCapabilities; private final BluetoothCodecConfig[] mCodecsSelectableCapabilities; - public BluetoothCodecStatus(BluetoothCodecConfig codecConfig, - BluetoothCodecConfig[] codecsLocalCapabilities, - BluetoothCodecConfig[] codecsSelectableCapabilities) { + public BluetoothCodecStatus(@Nullable BluetoothCodecConfig codecConfig, + @Nullable BluetoothCodecConfig[] codecsLocalCapabilities, + @Nullable BluetoothCodecConfig[] codecsSelectableCapabilities) { mCodecConfig = codecConfig; mCodecsLocalCapabilities = codecsLocalCapabilities; mCodecsSelectableCapabilities = codecsSelectableCapabilities; @@ -74,6 +74,7 @@ public final class BluetoothCodecStatus implements Parcelable { * @param c1 the first array of capabilities to compare * @param c2 the second array of capabilities to compare * @return true if both arrays contain same capabilities + * @hide */ public static boolean sameCapabilities(BluetoothCodecConfig[] c1, BluetoothCodecConfig[] c2) { @@ -95,6 +96,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @param codecConfig the codec config to compare against * @return true if the codec config matches, otherwise false + * @hide */ public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) { if (codecConfig == null || !codecConfig.hasSingleSampleRate() @@ -125,7 +127,12 @@ public final class BluetoothCodecStatus implements Parcelable { return false; } - + /** + * Returns a hash based on the codec config and local capabilities + * + * @return a hash based on the config values + * @hide + */ @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, @@ -140,6 +147,12 @@ public final class BluetoothCodecStatus implements Parcelable { + "}"; } + /** + * Always returns 0 + * + * @return 0 + * @hide + */ @Override public int describeContents() { return 0; @@ -165,6 +178,14 @@ public final class BluetoothCodecStatus implements Parcelable { } }; + /** + * Flattens the object to a parcel + * + * @param out The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * + * @hide + */ @Override public void writeToParcel(Parcel out, int flags) { out.writeTypedObject(mCodecConfig, 0); @@ -177,7 +198,6 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -187,8 +207,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage - public BluetoothCodecConfig[] getCodecsLocalCapabilities() { + public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -197,8 +216,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage - public BluetoothCodecConfig[] getCodecsSelectableCapabilities() { + public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 0be3eca8239e..49187dcde342 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1131,20 +1131,7 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); - return false; - } - try { - Log.i(TAG, "createBond() for device " + getAddress() - + " called by pid: " + Process.myPid() - + " tid: " + Process.myTid()); - return service.createBond(this, TRANSPORT_AUTO); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; + return createBond(TRANSPORT_AUTO); } /** @@ -1165,23 +1152,7 @@ public final class BluetoothDevice implements Parcelable { */ @UnsupportedAppUsage public boolean createBond(int transport) { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); - return false; - } - if (TRANSPORT_AUTO > transport || transport > TRANSPORT_LE) { - throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport"); - } - try { - Log.i(TAG, "createBond() for device " + getAddress() - + " called by pid: " + Process.myPid() - + " tid: " + Process.myTid()); - return service.createBond(this, transport); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; + return createBondOutOfBand(transport, null); } /** @@ -1209,7 +1180,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.createBondOutOfBand(this, transport, oobData); + return service.createBond(this, transport, oobData); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index e9fc8f6acfc6..f017aad25c0c 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -185,7 +185,8 @@ public class PackageInstaller { * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED}, * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT}, * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, - * {@link #STATUS_FAILURE_STORAGE}. + * {@link #STATUS_FAILURE_STORAGE}, {@link #STATUS_FAILURE_NAME_NOT_FOUND}, + * {@link #STATUS_FAILURE_ILLEGAL_STATE} or {@link #STATUS_FAILURE_SECURITY}. * <p> * More information about a status may be available through additional * extras; see the individual status documentation for details. @@ -1130,9 +1131,14 @@ public class PackageInstaller { * * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES * permission. - * @param statusReceiver Called when the state of the session changes. Intents sent to this - * receiver contain {@link #EXTRA_STATUS}. Refer to the individual - * transfer status codes on how to handle them. + * @param statusReceiver Called when the state of the session changes. Intents sent to + * this receiver contain {@link #EXTRA_STATUS}. Possible statuses: + * {@link #STATUS_FAILURE_NAME_NOT_FOUND}, + * {@link #STATUS_FAILURE_ILLEGAL_STATE}, + * {@link #STATUS_FAILURE_SECURITY}, + * {@link #STATUS_FAILURE}. + * Refer to the individual transfer status codes on how to handle + * them. * * @throws PackageManager.NameNotFoundException if the new owner could not be found. * @throws SecurityException if called after the session has been committed or abandoned. diff --git a/core/java/android/ddm/DdmHandleAppName.java b/core/java/android/ddm/DdmHandleAppName.java index 956078772ca8..de7acbaf6e55 100644 --- a/core/java/android/ddm/DdmHandleAppName.java +++ b/core/java/android/ddm/DdmHandleAppName.java @@ -17,10 +17,12 @@ package android.ddm; import android.annotation.UnsupportedAppUsage; +import android.util.Log; + import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; -import android.util.Log; + import java.nio.ByteBuffer; @@ -31,7 +33,7 @@ public class DdmHandleAppName extends ChunkHandler { public static final int CHUNK_APNM = type("APNM"); - private volatile static String mAppName = ""; + private static volatile Names sNames = new Names("", ""); private static DdmHandleAppName mInstance = new DdmHandleAppName(); @@ -66,45 +68,81 @@ public class DdmHandleAppName extends ChunkHandler { /** + * Sets all names to the same name. + */ + @UnsupportedAppUsage + public static void setAppName(String name, int userId) { + setAppName(name, name, userId); + } + + /** * Set the application name. Called when we get named, which may be * before or after DDMS connects. For the latter we need to send up * an APNM message. */ @UnsupportedAppUsage - public static void setAppName(String name, int userId) { - if (name == null || name.length() == 0) - return; + public static void setAppName(String appName, String pkgName, int userId) { + if (appName == null || appName.isEmpty() || pkgName == null || pkgName.isEmpty()) return; - mAppName = name; + sNames = new Names(appName, pkgName); // if DDMS is already connected, send the app name up - sendAPNM(name, userId); + sendAPNM(appName, pkgName, userId); } @UnsupportedAppUsage - public static String getAppName() { - return mAppName; + public static Names getNames() { + return sNames; } - /* + /** * Send an APNM (APplication NaMe) chunk. */ - private static void sendAPNM(String appName, int userId) { + private static void sendAPNM(String appName, String pkgName, int userId) { if (false) Log.v("ddm", "Sending app name"); ByteBuffer out = ByteBuffer.allocate( 4 /* appName's length */ - + appName.length()*2 /* appName */ - + 4 /* userId */); + + appName.length() * 2 /* appName */ + + 4 /* userId */ + + 4 /* pkgName's length */ + + pkgName.length() * 2 /* pkgName */); out.order(ChunkHandler.CHUNK_ORDER); out.putInt(appName.length()); putString(out, appName); out.putInt(userId); + out.putInt(pkgName.length()); + putString(out, pkgName); Chunk chunk = new Chunk(CHUNK_APNM, out); DdmServer.sendChunk(chunk); } + /** + * A class that encapsulates the app and package names into a single + * instance, effectively synchronizing the two names. + */ + static final class Names { + + private final String mAppName; + + private final String mPkgName; + + private Names(String appName, String pkgName) { + mAppName = appName; + mPkgName = pkgName; + } + + public String getAppName() { + return mAppName; + } + + public String getPkgName() { + return mPkgName; + } + + } + } diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java index 87568e857d6d..60dfc8d7ee7b 100644 --- a/core/java/android/ddm/DdmHandleHello.java +++ b/core/java/android/ddm/DdmHandleHello.java @@ -126,10 +126,9 @@ public class DdmHandleHello extends ChunkHandler { String vmVersion = System.getProperty("java.vm.version", "?"); String vmIdent = vmName + " v" + vmVersion; - //String appName = android.app.ActivityThread.currentPackageName(); - //if (appName == null) - // appName = "unknown"; - String appName = DdmHandleAppName.getAppName(); + DdmHandleAppName.Names names = DdmHandleAppName.getNames(); + String appName = names.getAppName(); + String pkgName = names.getPkgName(); VMRuntime vmRuntime = VMRuntime.getRuntime(); String instructionSetDescription = @@ -142,12 +141,13 @@ public class DdmHandleHello extends ChunkHandler { + (vmRuntime.isCheckJniEnabled() ? "true" : "false"); boolean isNativeDebuggable = vmRuntime.isNativeDebuggable(); - ByteBuffer out = ByteBuffer.allocate(28 + ByteBuffer out = ByteBuffer.allocate(32 + vmIdent.length() * 2 + appName.length() * 2 + instructionSetDescription.length() * 2 + vmFlags.length() * 2 - + 1); + + 1 + + pkgName.length() * 2); out.order(ChunkHandler.CHUNK_ORDER); out.putInt(CLIENT_PROTOCOL_VERSION); out.putInt(android.os.Process.myPid()); @@ -161,6 +161,8 @@ public class DdmHandleHello extends ChunkHandler { out.putInt(vmFlags.length()); putString(out, vmFlags); out.put((byte)(isNativeDebuggable ? 1 : 0)); + out.putInt(pkgName.length()); + putString(out, pkgName); Chunk reply = new Chunk(CHUNK_HELO, out); diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index c8bf570e1bc8..191516b5b992 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -86,12 +86,10 @@ public interface BiometricConstants { int BIOMETRIC_ERROR_LOCKOUT = 7; /** - * Hardware vendors may extend this list if there are conditions that do not fall under one of - * the above categories. Vendors are responsible for providing error strings for these errors. - * These messages are typically reserved for internal operations such as enrollment, but may be - * used to express vendor errors not otherwise covered. Applications are expected to show the - * error message string if they happen, but are advised not to rely on the message id since they - * will be device and vendor-specific + * OEMs should use this constant if there are conditions that do not fit under any of the other + * publicly defined constants, and must provide appropriate strings for these + * errors to the {@link BiometricPrompt.AuthenticationCallback#onAuthenticationError(int, + * CharSequence)} callback. OEMs should expect that the error message will be shown to users. */ int BIOMETRIC_ERROR_VENDOR = 8; diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java index f2c50b5cc464..5adf948de348 100644 --- a/core/java/android/hardware/display/DisplayViewport.java +++ b/core/java/android/hardware/display/DisplayViewport.java @@ -134,7 +134,9 @@ public final class DisplayViewport { result += prime * result + deviceWidth; result += prime * result + deviceHeight; result += prime * result + uniqueId.hashCode(); - result += prime * result + physicalPort; + if (physicalPort != null) { + result += prime * result + physicalPort.hashCode(); + } result += prime * result + type; return result; } @@ -142,11 +144,12 @@ public final class DisplayViewport { // For debugging purposes. @Override public String toString() { + final Integer port = physicalPort == null ? null : Byte.toUnsignedInt(physicalPort); return "DisplayViewport{type=" + typeToString(type) + ", valid=" + valid + ", displayId=" + displayId + ", uniqueId='" + uniqueId + "'" - + ", physicalPort=" + physicalPort + + ", physicalPort=" + port + ", orientation=" + orientation + ", logicalFrame=" + logicalFrame + ", physicalFrame=" + physicalFrame diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 15ff69e7fd2a..a9c5a9118a31 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -909,8 +909,11 @@ public class Binder implements IBinder { } /** - * Handle a call to {@link #shellCommand}. The default implementation simply prints - * an error message. Override and replace with your own. + * Handle a call to {@link #shellCommand}. + * + * <p>The default implementation performs a caller check to make sure the caller UID is of + * SHELL or ROOT, and then call {@link #handleShellCommand}. + * * <p class="caution">Note: no permission checking is done before calling this method; you must * apply any security checks as appropriate for the command being executed. * Consider using {@link ShellCommand} to help in the implementation.</p> @@ -921,6 +924,12 @@ public class Binder implements IBinder { @NonNull String[] args, @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) { + resultReceiver.send(-1, null); + throw new SecurityException("Shell commands are only callable by ADB"); + } + // First, convert in, out and err to @NonNull, by redirecting any that's null to /dev/null. try { if (in == null) { @@ -961,19 +970,23 @@ public class Binder implements IBinder { /** * System services can implement this method to implement ADB shell commands. * - * TODO More Javadoc. - * TODO Add a generic way to define subcommands and their permissions. + * <p>A system binder service can implement it to handle shell commands on ADB. For example, + * the Job Scheduler service implements it to handle <code>adb shell cmd jobscheduler</code>. * - * @param in standard input. - * @param out standard output. - * @param err standard error. + * <p>Commands are only executable by ADB shell; i.e. only {@link Process#SHELL_UID} and + * {@link Process#ROOT_UID} can call them. + * + * @param in standard input + * @param out standard output + * @param err standard error * @param args arguments passed to the command. Can be empty. The first argument is typically * a subcommand, such as {@code run} for {@code adb shell cmd jobscheduler run}. + * @return the status code returned from the <code>cmd</code> command. * * @hide */ - // @SystemApi TODO Make it a system API. - protected int handleShellCommand(@NonNull ParcelFileDescriptor in, + @SystemApi + public int handleShellCommand(@NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args) { FileOutputStream ferr = new FileOutputStream(err.getFileDescriptor()); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 9e9cd9218a0f..137f53782124 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -24,6 +24,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.StringDef; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -1112,6 +1113,82 @@ public class UserManager { */ public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending"; + /** + * List of key values that can be passed into the various user restriction related methods + * in {@link UserManager} & {@link DevicePolicyManager}. + * Note: This is slightly different from the real set of user restrictions listed in {@link + * com.android.server.pm.UserRestrictionsUtils#USER_RESTRICTIONS}. For example + * {@link #KEY_RESTRICTIONS_PENDING} is not a real user restriction, but is a a legitimate + * value that can be passed into {@link #hasUserRestriction(String)}. + * @hide + */ + @StringDef(value = { + DISALLOW_MODIFY_ACCOUNTS, + DISALLOW_CONFIG_WIFI, + DISALLOW_CONFIG_LOCALE, + DISALLOW_INSTALL_APPS, + DISALLOW_UNINSTALL_APPS, + DISALLOW_SHARE_LOCATION, + DISALLOW_AIRPLANE_MODE, + DISALLOW_CONFIG_BRIGHTNESS, + DISALLOW_AMBIENT_DISPLAY, + DISALLOW_CONFIG_SCREEN_TIMEOUT, + DISALLOW_INSTALL_UNKNOWN_SOURCES, + DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, + DISALLOW_CONFIG_BLUETOOTH, + DISALLOW_BLUETOOTH, + DISALLOW_BLUETOOTH_SHARING, + DISALLOW_USB_FILE_TRANSFER, + DISALLOW_CONFIG_CREDENTIALS, + DISALLOW_REMOVE_USER, + DISALLOW_REMOVE_MANAGED_PROFILE, + DISALLOW_DEBUGGING_FEATURES, + DISALLOW_CONFIG_VPN, + DISALLOW_CONFIG_LOCATION, + DISALLOW_CONFIG_DATE_TIME, + DISALLOW_CONFIG_TETHERING, + DISALLOW_NETWORK_RESET, + DISALLOW_FACTORY_RESET, + DISALLOW_ADD_USER, + DISALLOW_ADD_MANAGED_PROFILE, + ENSURE_VERIFY_APPS, + DISALLOW_CONFIG_CELL_BROADCASTS, + DISALLOW_CONFIG_MOBILE_NETWORKS, + DISALLOW_APPS_CONTROL, + DISALLOW_MOUNT_PHYSICAL_MEDIA, + DISALLOW_UNMUTE_MICROPHONE, + DISALLOW_ADJUST_VOLUME, + DISALLOW_OUTGOING_CALLS, + DISALLOW_SMS, + DISALLOW_FUN, + DISALLOW_CREATE_WINDOWS, + DISALLOW_SYSTEM_ERROR_DIALOGS, + DISALLOW_CROSS_PROFILE_COPY_PASTE, + DISALLOW_OUTGOING_BEAM, + DISALLOW_WALLPAPER, + DISALLOW_SET_WALLPAPER, + DISALLOW_SAFE_BOOT, + DISALLOW_RECORD_AUDIO, + DISALLOW_RUN_IN_BACKGROUND, + DISALLOW_CAMERA, + DISALLOW_UNMUTE_DEVICE, + DISALLOW_DATA_ROAMING, + DISALLOW_SET_USER_ICON, + DISALLOW_OEM_UNLOCK, + DISALLOW_UNIFIED_PASSWORD, + ALLOW_PARENT_PROFILE_APP_LINKING, + DISALLOW_AUTOFILL, + DISALLOW_CONTENT_CAPTURE, + DISALLOW_CONTENT_SUGGESTIONS, + DISALLOW_USER_SWITCH, + DISALLOW_SHARE_INTO_MANAGED_PROFILE, + DISALLOW_PRINTING, + DISALLOW_CONFIG_PRIVATE_DNS, + KEY_RESTRICTIONS_PENDING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UserRestrictionKey {} + private static final String ACTION_CREATE_USER = "android.os.action.CREATE_USER"; /** @@ -2026,7 +2103,8 @@ public class UserManager { @SystemApi @UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) - public int getUserRestrictionSource(String restrictionKey, UserHandle userHandle) { + public int getUserRestrictionSource(@UserRestrictionKey String restrictionKey, + UserHandle userHandle) { try { return mService.getUserRestrictionSource(restrictionKey, userHandle.getIdentifier()); } catch (RemoteException re) { @@ -2045,7 +2123,7 @@ public class UserManager { @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public List<EnforcingUser> getUserRestrictionSources( - String restrictionKey, UserHandle userHandle) { + @UserRestrictionKey String restrictionKey, UserHandle userHandle) { try { return mService.getUserRestrictionSources(restrictionKey, userHandle.getIdentifier()); } catch (RemoteException re) { @@ -2091,7 +2169,8 @@ public class UserManager { * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. */ @UnsupportedAppUsage - public boolean hasBaseUserRestriction(String restrictionKey, UserHandle userHandle) { + public boolean hasBaseUserRestriction(@UserRestrictionKey String restrictionKey, + UserHandle userHandle) { try { return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier()); } catch (RemoteException re) { @@ -2162,7 +2241,7 @@ public class UserManager { * @param restrictionKey The string key representing the restriction. * @return {@code true} if the current user has the given restriction, {@code false} otherwise. */ - public boolean hasUserRestriction(String restrictionKey) { + public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey) { return hasUserRestrictionForUser(restrictionKey, Process.myUserHandle()); } @@ -2174,7 +2253,8 @@ public class UserManager { * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. */ @UnsupportedAppUsage - public boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) { + public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey, + UserHandle userHandle) { return hasUserRestrictionForUser(restrictionKey, userHandle); } @@ -2194,7 +2274,7 @@ public class UserManager { @RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) - public boolean hasUserRestrictionForUser(@NonNull String restrictionKey, + public boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey, @NonNull UserHandle userHandle) { try { return mService.hasUserRestriction(restrictionKey, userHandle.getIdentifier()); @@ -2207,7 +2287,7 @@ public class UserManager { * @hide * Returns whether any user on the device has the given user restriction set. */ - public boolean hasUserRestrictionOnAnyUser(String restrictionKey) { + public boolean hasUserRestrictionOnAnyUser(@UserRestrictionKey String restrictionKey) { try { return mService.hasUserRestrictionOnAnyUser(restrictionKey); } catch (RemoteException re) { diff --git a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java index 7db003d9853c..664b6c87d339 100644 --- a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java +++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java @@ -19,9 +19,13 @@ package android.os.connectivity; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.app.ActivityThread; +import android.content.Context; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.os.PowerProfile; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -72,7 +76,6 @@ public final class WifiActivityEnergyInfo implements Parcelable { * @param scanDurationMillis Cumulative milliseconds when radio is awake due to scan. * @param idleDurationMillis Cumulative milliseconds when radio is awake but not transmitting or * receiving. - * @param energyUsedMicroJoules Cumulative energy consumed by Wifi, in microjoules. */ public WifiActivityEnergyInfo( long timeSinceBootMillis, @@ -80,14 +83,33 @@ public final class WifiActivityEnergyInfo implements Parcelable { long txDurationMillis, long rxDurationMillis, long scanDurationMillis, - long idleDurationMillis, - long energyUsedMicroJoules) { + long idleDurationMillis) { mTimeSinceBootMillis = timeSinceBootMillis; mStackState = stackState; mControllerTxDurationMillis = txDurationMillis; mControllerRxDurationMillis = rxDurationMillis; mControllerScanDurationMillis = scanDurationMillis; mControllerIdleDurationMillis = idleDurationMillis; + + final Context context = ActivityThread.currentActivityThread().getSystemContext(); + if (context == null) { + mControllerEnergyUsedMicroJoules = 0L; + return; + } + // Calculate energy used using PowerProfile. + PowerProfile powerProfile = new PowerProfile(context); + final double rxIdleCurrent = powerProfile.getAveragePower( + PowerProfile.POWER_WIFI_CONTROLLER_IDLE); + final double rxCurrent = powerProfile.getAveragePower( + PowerProfile.POWER_WIFI_CONTROLLER_RX); + final double txCurrent = powerProfile.getAveragePower( + PowerProfile.POWER_WIFI_CONTROLLER_TX); + final double voltage = powerProfile.getAveragePower( + PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; + final long energyUsedMicroJoules = (long) ((mControllerTxDurationMillis * txCurrent + + mControllerRxDurationMillis * rxCurrent + + mControllerIdleDurationMillis * rxIdleCurrent) + * voltage); mControllerEnergyUsedMicroJoules = energyUsedMicroJoules; } @@ -113,9 +135,8 @@ public final class WifiActivityEnergyInfo implements Parcelable { long rxTime = in.readLong(); long scanTime = in.readLong(); long idleTime = in.readLong(); - long energyUsed = in.readLong(); return new WifiActivityEnergyInfo(timestamp, stackState, - txTime, rxTime, scanTime, idleTime, energyUsed); + txTime, rxTime, scanTime, idleTime); } public WifiActivityEnergyInfo[] newArray(int size) { return new WifiActivityEnergyInfo[size]; @@ -130,7 +151,6 @@ public final class WifiActivityEnergyInfo implements Parcelable { out.writeLong(mControllerRxDurationMillis); out.writeLong(mControllerScanDurationMillis); out.writeLong(mControllerIdleDurationMillis); - out.writeLong(mControllerEnergyUsedMicroJoules); } @Override diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 2c53025da350..2fa3386bccb8 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -27,6 +27,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.Activity; @@ -62,6 +63,7 @@ import android.os.UserManager; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; +import android.os.storage.VolumeRecord; import android.service.media.CameraPrewarmService; import android.text.TextUtils; import android.text.format.DateUtils; @@ -157,6 +159,8 @@ public final class MediaStore { public static final String SCAN_FILE_CALL = "scan_file"; /** {@hide} */ public static final String SCAN_VOLUME_CALL = "scan_volume"; + /** {@hide} */ + public static final String SUICIDE_CALL = "suicide"; /** * Extra used with {@link #SCAN_FILE_CALL} or {@link #SCAN_VOLUME_CALL} to indicate that @@ -1590,31 +1594,41 @@ public final class MediaStore { /** * Constant for the {@link #MEDIA_TYPE} column indicating that file - * is not an audio, image, video or playlist file. + * is not an audio, image, video, playlist, or subtitles file. */ public static final int MEDIA_TYPE_NONE = 0; /** - * Constant for the {@link #MEDIA_TYPE} column indicating that file is an image file. + * Constant for the {@link #MEDIA_TYPE} column indicating that file + * is an image file. */ public static final int MEDIA_TYPE_IMAGE = 1; /** - * Constant for the {@link #MEDIA_TYPE} column indicating that file is an audio file. + * Constant for the {@link #MEDIA_TYPE} column indicating that file + * is an audio file. */ public static final int MEDIA_TYPE_AUDIO = 2; /** - * Constant for the {@link #MEDIA_TYPE} column indicating that file is a video file. + * Constant for the {@link #MEDIA_TYPE} column indicating that file + * is a video file. */ public static final int MEDIA_TYPE_VIDEO = 3; /** - * Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file. + * Constant for the {@link #MEDIA_TYPE} column indicating that file + * is a playlist file. */ public static final int MEDIA_TYPE_PLAYLIST = 4; /** + * Constant for the {@link #MEDIA_TYPE} column indicating that file + * is a subtitles or lyrics file. + */ + public static final int MEDIA_TYPE_SUBTITLE = 5; + + /** * Column indicating if the file is part of Downloads collection. * @hide */ @@ -3603,6 +3617,43 @@ public final class MediaStore { } /** + * Return list of all specific volume names that have recently been part of + * {@link #VOLUME_EXTERNAL}. + * <p> + * This includes both currently mounted volumes <em>and</em> recently + * mounted (but currently unmounted) volumes. Any indexed metadata for these + * volumes is preserved to optimize the speed of remounting at a later time. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) + public static @NonNull Set<String> getRecentExternalVolumeNames(@NonNull Context context) { + final StorageManager sm = context.getSystemService(StorageManager.class); + + // We always have primary storage + final Set<String> volumeNames = new ArraySet<>(); + volumeNames.add(VOLUME_EXTERNAL_PRIMARY); + + final long lastWeek = System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS; + for (VolumeRecord rec : sm.getVolumeRecords()) { + // Skip volumes without valid UUIDs + if (TextUtils.isEmpty(rec.fsUuid)) continue; + + final VolumeInfo vi = sm.findVolumeByUuid(rec.fsUuid); + if (vi != null && vi.isVisibleForUser(UserHandle.myUserId()) + && vi.isMountedReadable()) { + // We're mounted right now + volumeNames.add(rec.getNormalizedFsUuid()); + } else if (rec.lastSeenMillis > 0 && rec.lastSeenMillis < lastWeek) { + // We're not mounted right now, but we've been seen recently + volumeNames.add(rec.getNormalizedFsUuid()); + } + } + return volumeNames; + } + + /** * Return the volume name that the given {@link Uri} references. */ public static @NonNull String getVolumeName(@NonNull Uri uri) { @@ -3701,6 +3752,8 @@ public final class MediaStore { * @hide */ @TestApi + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public static @NonNull Collection<File> getVolumeScanPaths(@NonNull String volumeName) throws FileNotFoundException { if (TextUtils.isEmpty(volumeName)) { @@ -3929,6 +3982,16 @@ public final class MediaStore { } /** @hide */ + public static void suicide(Context context) { + final ContentResolver resolver = context.getContentResolver(); + try (ContentProviderClient client = resolver + .acquireUnstableContentProviderClient(AUTHORITY)) { + client.call(SUICIDE_CALL, null, null); + } catch (Exception ignored) { + } + } + + /** @hide */ @TestApi public static Uri scanFile(Context context, File file) { return scan(context, SCAN_FILE_CALL, file, false); diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 22f90f62b114..70c8e5d311bd 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4837,4 +4837,23 @@ public final class Telephony { public static final Uri CONTENT_URI = Uri.parse("content://carrier_id/all"); } } + + /** + * Contains SIM Information + * @hide + */ + @SystemApi + public static final class SimInfo { + /** + * Not instantiable. + * @hide + */ + private SimInfo() {} + + /** + * The {@code content://} style URI for this provider. + */ + @NonNull + public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo"); + } } diff --git a/telephony/java/android/telephony/SubscriptionPlan.aidl b/core/java/android/telephony/SubscriptionPlan.aidl index 655df3a71b3d..655df3a71b3d 100755 --- a/telephony/java/android/telephony/SubscriptionPlan.aidl +++ b/core/java/android/telephony/SubscriptionPlan.aidl diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java index 28a5c2086ede..28a5c2086ede 100644 --- a/telephony/java/android/telephony/SubscriptionPlan.java +++ b/core/java/android/telephony/SubscriptionPlan.java diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java index 0a4069d15706..7e7164042781 100644 --- a/core/java/android/util/StatsEvent.java +++ b/core/java/android/util/StatsEvent.java @@ -20,8 +20,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; import android.os.SystemClock; import com.android.internal.annotations.GuardedBy; @@ -44,7 +42,7 @@ import com.android.internal.annotations.VisibleForTesting; * </pre> * @hide **/ -public final class StatsEvent implements Parcelable { +public final class StatsEvent { // Type Ids. /** * @hide @@ -265,39 +263,6 @@ public final class StatsEvent implements Parcelable { } /** - * Boilerplate for Parcel. - */ - public static final @NonNull Parcelable.Creator<StatsEvent> CREATOR = - new Parcelable.Creator<StatsEvent>() { - public StatsEvent createFromParcel(Parcel in) { - // Purposefully leaving this method not implemented. - throw new RuntimeException("Not implemented"); - } - - public StatsEvent[] newArray(int size) { - // Purposefully leaving this method not implemented. - throw new RuntimeException("Not implemented"); - } - }; - - /** - * Boilerplate for Parcel. - */ - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mAtomId); - out.writeInt(getNumBytes()); - out.writeByteArray(getBytes()); - } - - /** - * Boilerplate for Parcel. - */ - public int describeContents() { - return 0; - } - - - /** * Builder for constructing a StatsEvent object. * * <p>This class defines and encapsulates the socket encoding for the buffer. diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java index 64e15cfb7948..8cb5b05df685 100644 --- a/core/java/android/util/StatsLog.java +++ b/core/java/android/util/StatsLog.java @@ -179,6 +179,8 @@ public final class StatsLog extends StatsLogInternal { * @param rollbackType state of the rollback. * @param packageName package name being rolled back. * @param packageVersionCode version of the package being rolled back. + * @param rollbackReason reason the package is being rolled back. + * @param failingPackageName the package name causing the failure. * * @return True if the log request was sent to statsd. * @@ -186,7 +188,7 @@ public final class StatsLog extends StatsLogInternal { */ @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) public static boolean logWatchdogRollbackOccurred(int rollbackType, String packageName, - long packageVersionCode) { + long packageVersionCode, int rollbackReason, String failingPackageName) { synchronized (sLogLock) { try { IStatsManager service = getIStatsManagerLocked(); @@ -198,7 +200,7 @@ public final class StatsLog extends StatsLogInternal { } service.sendWatchdogRollbackOccurredAtom(rollbackType, packageName, - packageVersionCode); + packageVersionCode, rollbackReason, failingPackageName); return true; } catch (RemoteException e) { sService = null; diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java index c8b7e25e59fb..e0d9a4dd1df0 100644 --- a/core/java/android/view/DisplayAddress.java +++ b/core/java/android/view/DisplayAddress.java @@ -41,6 +41,18 @@ public abstract class DisplayAddress implements Parcelable { } /** + * Creates an address for a physical display given its port and model. + * + * @param port A port in the range [0, 255] interpreted as signed. + * @param model A positive integer, or {@code null} if the model cannot be identified. + * @return The {@link Physical} address. + */ + @NonNull + public static Physical fromPortAndModel(byte port, Long model) { + return new Physical(port, model); + } + + /** * Creates an address for a network display given its MAC address. * * @param macAddress A MAC address in colon notation. @@ -64,12 +76,23 @@ public abstract class DisplayAddress implements Parcelable { public static final class Physical extends DisplayAddress { private static final long UNKNOWN_MODEL = 0; private static final int MODEL_SHIFT = 8; - private static final int PORT_MASK = 0xFF; private final long mPhysicalDisplayId; /** + * Stable display ID combining port and model. + * + * @return An ID in the range [0, 2^64) interpreted as signed. + * @see SurfaceControl#getPhysicalDisplayIds + */ + public long getPhysicalDisplayId() { + return mPhysicalDisplayId; + } + + /** * Physical port to which the display is connected. + * + * @return A port in the range [0, 255] interpreted as signed. */ public byte getPort() { return (byte) mPhysicalDisplayId; @@ -78,7 +101,7 @@ public abstract class DisplayAddress implements Parcelable { /** * Model identifier unique across manufacturers. * - * @return The model ID, or {@code null} if the model cannot be identified. + * @return A positive integer, or {@code null} if the model cannot be identified. */ @Nullable public Long getModel() { @@ -95,7 +118,7 @@ public abstract class DisplayAddress implements Parcelable { @Override public String toString() { final StringBuilder builder = new StringBuilder("{") - .append("port=").append(getPort() & PORT_MASK); + .append("port=").append(Byte.toUnsignedInt(getPort())); final Long model = getModel(); if (model != null) { @@ -119,6 +142,11 @@ public abstract class DisplayAddress implements Parcelable { mPhysicalDisplayId = physicalDisplayId; } + private Physical(byte port, Long model) { + mPhysicalDisplayId = Byte.toUnsignedLong(port) + | (model == null ? UNKNOWN_MODEL : (model << MODEL_SHIFT)); + } + public static final @NonNull Parcelable.Creator<Physical> CREATOR = new Parcelable.Creator<Physical>() { @Override diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index afa661e26d4c..3171306fc568 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1646,7 +1646,7 @@ public final class ViewRootImpl implements ViewParent, mBlastSurfaceControl, width, height); } - mBlastBufferQueue.update(mSurfaceControl, width, height); + mBlastBufferQueue.update(mBlastSurfaceControl, width, height); mTransaction.show(mBlastSurfaceControl) .reparent(mBlastSurfaceControl, mSurfaceControl) diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index c571737cec8f..7cec440dd80b 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -3473,18 +3473,10 @@ public class RemoteViews implements Parcelable, Filter { return applyAsync(context, parent, executor, listener, null); } - private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) { - CancellationSignal cancelSignal = new CancellationSignal(); - cancelSignal.setOnCancelListener(task); - - task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor); - return cancelSignal; - } - /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener, OnClickHandler handler) { - return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor); + return getAsyncApplyTask(context, parent, listener, handler).startTaskOnExecutor(executor); } private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, @@ -3495,6 +3487,7 @@ public class RemoteViews implements Parcelable, Filter { private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree> implements CancellationSignal.OnCancelListener { + final CancellationSignal mCancelSignal = new CancellationSignal(); final RemoteViews mRV; final ViewGroup mParent; final Context mContext; @@ -3545,6 +3538,7 @@ public class RemoteViews implements Parcelable, Filter { @Override protected void onPostExecute(ViewTree viewTree) { + mCancelSignal.setOnCancelListener(null); if (mError == null) { if (mListener != null) { mListener.onViewInflated(viewTree.mRoot); @@ -3581,6 +3575,13 @@ public class RemoteViews implements Parcelable, Filter { @Override public void onCancel() { cancel(true); + mCancelSignal.setOnCancelListener(null); + } + + private CancellationSignal startTaskOnExecutor(Executor executor) { + mCancelSignal.setOnCancelListener(this); + executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor); + return mCancelSignal; } } @@ -3646,8 +3647,8 @@ public class RemoteViews implements Parcelable, Filter { } } - return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(), - context, listener, handler, v), executor); + return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(), + context, listener, handler, v).startTaskOnExecutor(executor); } private void performApply(View v, ViewGroup parent, OnClickHandler handler) { diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index fd3cd42b07a1..a21187165c65 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -30,8 +30,10 @@ import android.os.SystemProperties; import android.os.Trace; import android.util.Log; import android.util.Slog; + import com.android.internal.logging.AndroidConfig; import com.android.server.NetworkManagementSocketTagger; + import dalvik.system.RuntimeHooks; import dalvik.system.VMRuntime; @@ -374,9 +376,6 @@ public class RuntimeInit { // leftover running threads to crash before the process actually exits. nativeSetExitWithoutCleanup(true); - // We want to be fairly aggressive about heap utilization, to avoid - // holding on to a lot of memory that isn't needed. - VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); final Arguments args = new Arguments(argv); diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 897b982406dc..13cc98be5d5a 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -43,7 +43,7 @@ interface ILockSettings { long getLong(in String key, in long defaultValue, in int userId); @UnsupportedAppUsage String getString(in String key, in String defaultValue, in int userId); - boolean setLockCredential(in LockscreenCredential credential, in LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange); + boolean setLockCredential(in LockscreenCredential credential, in LockscreenCredential savedCredential, int userId); void resetKeyStore(int userId); VerifyCredentialResponse checkCredential(in LockscreenCredential credential, int userId, in ICheckCredentialProgressCallback progressCallback); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index b534213ec859..cff39f120dd7 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -636,35 +636,16 @@ public class LockPatternUtils { * * @param newCredential The new credential to save * @param savedCredential The current credential - * @param userId the user whose lockscreen credential is to be changed - * - * @return whether this method saved the new password successfully or not. This flow will fail - * and return false if the given credential is wrong. - * @throws RuntimeException if password change encountered an unrecoverable error. - */ - public boolean setLockCredential(@NonNull LockscreenCredential newCredential, - @NonNull LockscreenCredential savedCredential, int userId) { - return setLockCredential(newCredential, savedCredential, userId, false); - } - - /** - * Save a new lockscreen credential. - * <p> This method will fail (returning {@code false}) if the previously saved pattern provided - * is incorrect and allowUntrustedChange is false, or if the lockscreen verification is still - * being throttled. - * @param newCredential The new credential to save - * @param savedCredential The current credential * @param userHandle the user whose lockscreen credential is to be changed - * @param allowUntrustedChange whether we want to allow saving a new pattern if the existing - * credentialt being provided is incorrect. * * @return whether this method saved the new password successfully or not. This flow will fail - * and return false if the given credential is wrong and allowUntrustedChange is false. + * and return false if the given credential is wrong. * @throws RuntimeException if password change encountered an unrecoverable error. + * @throws UnsupportedOperationException secure lockscreen is not supported on this device. + * @throws IllegalArgumentException if new credential is too short. */ public boolean setLockCredential(@NonNull LockscreenCredential newCredential, - @NonNull LockscreenCredential savedCredential, int userHandle, - boolean allowUntrustedChange) { + @NonNull LockscreenCredential savedCredential, int userHandle) { if (!hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); @@ -672,8 +653,7 @@ public class LockPatternUtils { newCredential.checkLength(); try { - if (!getLockSettings().setLockCredential( - newCredential, savedCredential, userHandle, allowUntrustedChange)) { + if (!getLockSettings().setLockCredential(newCredential, savedCredential, userHandle)) { return false; } } catch (RemoteException e) { diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index dc4f09a2af6f..13bfc1bf72b8 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -66,10 +66,15 @@ import org.xmlpull.v1.XmlSerializer; public class BootReceiver extends BroadcastReceiver { private static final String TAG = "BootReceiver"; + private static final String TAG_TRUNCATED = "[[TRUNCATED]]\n"; + // Maximum size of a logged event (files get truncated if they're longer). // Give userdebug builds a larger max to capture extra debug, esp. for last_kmsg. private static final int LOG_SIZE = SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536; + private static final int LASTK_LOG_SIZE = + SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536; + private static final int GMSCORE_LASTK_LOG_SIZE = 196608; private static final File TOMBSTONE_DIR = new File("/data/tombstones"); private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE"; @@ -224,12 +229,12 @@ public class BootReceiver extends BroadcastReceiver { if (db != null) db.addText("SYSTEM_BOOT", headers); // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile()) - addFileWithFootersToDropBox(db, timestamps, headers, lastKmsgFooter, - "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG"); - addFileWithFootersToDropBox(db, timestamps, headers, lastKmsgFooter, - "/sys/fs/pstore/console-ramoops", -LOG_SIZE, "SYSTEM_LAST_KMSG"); - addFileWithFootersToDropBox(db, timestamps, headers, lastKmsgFooter, - "/sys/fs/pstore/console-ramoops-0", -LOG_SIZE, "SYSTEM_LAST_KMSG"); + addLastkToDropBox(db, timestamps, headers, lastKmsgFooter, + "/proc/last_kmsg", -LASTK_LOG_SIZE, "SYSTEM_LAST_KMSG"); + addLastkToDropBox(db, timestamps, headers, lastKmsgFooter, + "/sys/fs/pstore/console-ramoops", -LASTK_LOG_SIZE, "SYSTEM_LAST_KMSG"); + addLastkToDropBox(db, timestamps, headers, lastKmsgFooter, + "/sys/fs/pstore/console-ramoops-0", -LASTK_LOG_SIZE, "SYSTEM_LAST_KMSG"); addFileToDropBox(db, timestamps, headers, "/cache/recovery/log", -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); addFileToDropBox(db, timestamps, headers, "/cache/recovery/last_kmsg", @@ -278,6 +283,23 @@ public class BootReceiver extends BroadcastReceiver { sTombstoneObserver.startWatching(); } + private static void addLastkToDropBox( + DropBoxManager db, HashMap<String, Long> timestamps, + String headers, String footers, String filename, int maxSize, + String tag) throws IOException { + int extraSize = headers.length() + TAG_TRUNCATED.length() + footers.length(); + // GMSCore will do 2nd truncation to be 192KiB + // LASTK_LOG_SIZE + extraSize must be less than GMSCORE_LASTK_LOG_SIZE + if (LASTK_LOG_SIZE + extraSize > GMSCORE_LASTK_LOG_SIZE) { + if (GMSCORE_LASTK_LOG_SIZE > extraSize) { + maxSize = -(GMSCORE_LASTK_LOG_SIZE - extraSize); + } else { + maxSize = 0; + } + } + addFileWithFootersToDropBox(db, timestamps, headers, footers, filename, maxSize, tag); + } + private static void addFileToDropBox( DropBoxManager db, HashMap<String, Long> timestamps, String headers, String filename, int maxSize, String tag) throws IOException { @@ -301,7 +323,7 @@ public class BootReceiver extends BroadcastReceiver { timestamps.put(filename, fileTime); - String fileContents = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); + String fileContents = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED); String text = headers + fileContents + footers; // Create an additional report for system server native crashes, with a special tag. if (tag.equals(TAG_TOMBSTONE) && fileContents.contains(">>> system_server <<<")) { @@ -345,7 +367,7 @@ public class BootReceiver extends BroadcastReceiver { timestamps.put(tag, fileTime); - String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); + String log = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED); StringBuilder sb = new StringBuilder(); for (String line : log.split("\n")) { if (line.contains("audit")) { @@ -370,7 +392,7 @@ public class BootReceiver extends BroadcastReceiver { long fileTime = file.lastModified(); if (fileTime <= 0) return; // File does not exist - String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); + String log = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED); Pattern pattern = Pattern.compile(FS_STAT_PATTERN); String lines[] = log.split("\n"); int lineNumber = 0; diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 378e125a3a3e..97451a2c4cfd 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -648,6 +648,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX]; std::string fingerprintBuf; char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX]; + char opaqueJniIds[sizeof("-Xopaque-jni-ids:") - 1 + PROPERTY_VALUE_MAX]; char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX]; // Read if we are using the profile configuration, do this at the start since the last ART args @@ -839,6 +840,14 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p "default"); } + // Only pass an explicit opaque-jni-ids to apps forked from zygote + if (zygote) { + parseRuntimeOption("dalvik.vm.opaque-jni-ids", + opaqueJniIds, + "-Xopaque-jni-ids:", + "swapable"); + } + parseRuntimeOption("dalvik.vm.lockprof.threshold", lockProfThresholdBuf, "-Xlockprofthreshold:"); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index f3a626e1e193..5d13cf82141b 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -32,6 +32,11 @@ using ::android::base::unique_fd; namespace android { +static struct overlayableinfo_offsets_t { + jclass classObject; + jmethodID constructor; +} gOverlayableInfoOffsets; + static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, jboolean force_shared_lib, jboolean overlay, jboolean for_loader) { ScopedUtfChars path(env, java_path); @@ -222,12 +227,9 @@ static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr return 0; } - jclass overlayable_class = env->FindClass("android/content/om/OverlayableInfo"); - jmethodID overlayable_constructor = env->GetMethodID(overlayable_class, "<init>", - "(Ljava/lang/String;Ljava/lang/String;I)V"); return env->NewObject( - overlayable_class, - overlayable_constructor, + gOverlayableInfoOffsets.classObject, + gOverlayableInfoOffsets.constructor, overlayable_name, actor_string ); @@ -267,6 +269,11 @@ static const JNINativeMethod gApkAssetsMethods[] = { }; int register_android_content_res_ApkAssets(JNIEnv* env) { + jclass overlayableInfoClass = FindClassOrDie(env, "android/content/om/OverlayableInfo"); + gOverlayableInfoOffsets.classObject = MakeGlobalRefOrDie(env, overlayableInfoClass); + gOverlayableInfoOffsets.constructor = GetMethodIDOrDie(env, gOverlayableInfoOffsets.classObject, + "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"); + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, arraysize(gApkAssetsMethods)); } diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index 1ec05fb5e9fc..ecb4193a2c6c 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -264,3 +264,14 @@ message ZenPolicyProto { optional Sender priority_calls = 16; optional Sender priority_messages = 17; } + +// Next Tag: 2 +message PackageRemoteViewInfoProto { + optional string package_name = 1; + // add per-package additional info here (like channels) +} + +// Next Tag: 2 +message NotificationRemoteViewsProto { + repeated PackageRemoteViewInfoProto package_remote_view_info = 1; +}
\ No newline at end of file diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 307d754a1ad8..bfbd959df1a8 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2851,9 +2851,6 @@ <!-- String array containing numbers that shouldn't be logged. Country-specific. --> <string-array name="unloggable_phone_numbers" /> - <!-- Flag specifying whether or not IMS will use the dynamic ImsResolver --> - <bool name="config_dynamic_bind_ims">false</bool> - <!-- Cellular data service package name to bind to by default. If none is specified in an overlay, an empty string is passed in --> <string name="config_wwan_data_service_package" translatable="false">com.android.phone</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 083f33cc8035..ee9287c7d64c 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -294,7 +294,6 @@ <java-symbol type="bool" name="config_hotswapCapable" /> <java-symbol type="bool" name="config_mms_content_disposition_support" /> <java-symbol type="string" name="config_ims_package" /> - <java-symbol type="bool" name="config_dynamic_bind_ims" /> <java-symbol type="string" name="config_wwan_network_service_package" /> <java-symbol type="string" name="config_wlan_network_service_package" /> <java-symbol type="string" name="config_wwan_network_service_class" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 0c52029b93f8..cef21db1e0f8 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1735,6 +1735,7 @@ easier. <item name="colorBackground">@color/background_device_default_light</item> <item name="colorBackgroundFloating">@color/background_device_default_light</item> <item name="layout_gravity">center</item> + <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item> </style> <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification"> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index cf3f51d6599a..322cbd798203 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -204,7 +204,7 @@ applications that come with the platform <permission name="android.permission.UPDATE_DEVICE_STATS"/> </privapp-permissions> - <privapp-permissions package="com.android.providers.media"> + <privapp-permissions package="com.android.providers.media.module"> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.MANAGE_USERS"/> <permission name="android.permission.USE_RESERVED_DISK"/> @@ -369,4 +369,7 @@ applications that come with the platform <permission name="android.permission.REBOOT"/> <permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/> </privapp-permissions> + <privapp-permissions package="com.android.settings"> + <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/> + </privapp-permissions> </permissions> diff --git a/data/sounds/AudioPackageGo.mk b/data/sounds/AudioPackageGo.mk index e3b27f2cd962..e3fb45f6f055 100644 --- a/data/sounds/AudioPackageGo.mk +++ b/data/sounds/AudioPackageGo.mk @@ -47,3 +47,7 @@ PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \ $(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \ $(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \ + $(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \ + $(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \ + $(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \ + diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java index fcebad339f2b..041300c4b1b0 100644 --- a/drm/java/android/drm/DrmManagerClient.java +++ b/drm/java/android/drm/DrmManagerClient.java @@ -16,6 +16,7 @@ package android.drm; +import android.annotation.NonNull; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; @@ -37,6 +38,8 @@ import java.io.FileInputStream; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -370,6 +373,17 @@ public class DrmManagerClient implements AutoCloseable { } /** + * Retrieves information about all the DRM plug-ins (agents) that are + * registered with the DRM framework. + * + * @return List of all the DRM plug-ins (agents) that are registered with + * the DRM framework. + */ + public @NonNull Collection<DrmSupportInfo> getAvailableDrmSupportInfo() { + return Arrays.asList(_getAllSupportInfo(mUniqueId)); + } + + /** * Retrieves constraint information for rights-protected content. * * @param path Path to the content from which you are retrieving DRM constraints. diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index c0041722c475..1f8c1d5352b0 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -164,25 +164,52 @@ public class LocationManager { * Broadcast intent action when the set of enabled location providers changes. To check the * status of a provider, use {@link #isProviderEnabled(String)}. From Android Q and above, will * include a string intent extra, {@link #EXTRA_PROVIDER_NAME}, with the name of the provider - * whose state has changed. + * whose state has changed. From Android R and above, will include a boolean intent extra, + * {@link #EXTRA_PROVIDER_ENABLED}, with the enabled state of the provider. * * @see #EXTRA_PROVIDER_NAME + * @see #EXTRA_PROVIDER_ENABLED + * @see #isProviderEnabled(String) */ public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED"; /** * Intent extra included with {@link #PROVIDERS_CHANGED_ACTION} broadcasts, containing the name - * of the location provider that has changed, to be used with location provider APIs. + * of the location provider that has changed. + * + * @see #PROVIDERS_CHANGED_ACTION + * @see #EXTRA_PROVIDER_ENABLED */ public static final String EXTRA_PROVIDER_NAME = "android.location.extra.PROVIDER_NAME"; /** - * Broadcast intent action when the device location mode changes. To check the location mode, - * use {@link #isLocationEnabled()}. + * Intent extra included with {@link #PROVIDERS_CHANGED_ACTION} broadcasts, containing the + * boolean enabled state of the location provider that has changed. + * + * @see #PROVIDERS_CHANGED_ACTION + * @see #EXTRA_PROVIDER_NAME + */ + public static final String EXTRA_PROVIDER_ENABLED = "android.location.extra.PROVIDER_ENABLED"; + + /** + * Broadcast intent action when the device location enabled state changes. From Android R and + * above, will include a boolean intent extra, {@link #EXTRA_LOCATION_ENABLED}, with the enabled + * state of location. + * + * @see #EXTRA_LOCATION_ENABLED + * @see #isLocationEnabled() */ public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED"; /** + * Intent extra included with {@link #MODE_CHANGED_ACTION} broadcasts, containing the boolean + * enabled state of location. + * + * @see #MODE_CHANGED_ACTION + */ + public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED"; + + /** * Broadcast intent action indicating that a high power location requests * has either started or stopped being active. The current state of * active location requests should be read from AppOpsManager using diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java index c20dc615529b..751bb6a70880 100644 --- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java +++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java @@ -103,10 +103,6 @@ public class GnssMetrics { mPositionAccuracyMeterStatistics = new Statistics(); mTopFourAverageCn0Statistics = new Statistics(); mTopFourAverageCn0StatisticsL5 = new Statistics(); - mNumSvStatus = 0; - mNumL5SvStatus = 0; - mNumSvStatusUsedInFix = 0; - mNumL5SvStatusUsedInFix = 0; reset(); } @@ -410,6 +406,11 @@ public class GnssMetrics { mPositionAccuracyMeterStatistics.reset(); mTopFourAverageCn0Statistics.reset(); resetConstellationTypes(); + mTopFourAverageCn0StatisticsL5.reset(); + mNumSvStatus = 0; + mNumL5SvStatus = 0; + mNumSvStatusUsedInFix = 0; + mNumL5SvStatusUsedInFix = 0; } /** Resets {@link #mConstellationTypes} as an all-false boolean array. */ diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java index 8d857243bc05..515f6a8ce8a2 100644 --- a/media/java/android/media/MediaScannerConnection.java +++ b/media/java/android/media/MediaScannerConnection.java @@ -164,7 +164,7 @@ public class MediaScannerConnection implements ServiceConnection { /** * Convenience for constructing a {@link MediaScannerConnection}, calling - * {@link #connect} on it, and calling {@link #scanFile} with the given + * {@link #connect} on it, and calling {@link #scanFile(String, String)} with the given * <var>path</var> and <var>mimeType</var> when the connection is * established. * @param context The caller's Context, required for establishing a connection to diff --git a/media/java/android/media/tv/tuner/FilterSettings.java b/media/java/android/media/tv/tuner/FilterSettings.java new file mode 100644 index 000000000000..d5f100341dc6 --- /dev/null +++ b/media/java/android/media/tv/tuner/FilterSettings.java @@ -0,0 +1,383 @@ +/* + * Copyright 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.media.tv.tuner; + +import android.annotation.Nullable; +import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.TunerConstants.FilterSettingsType; + +import java.util.List; + +/** + * Demux Filter settings. + * + * @hide + */ +public abstract class FilterSettings { + @Nullable + protected final Settings mSettings; + + protected FilterSettings(Settings settings) { + mSettings = settings; + } + + /** + * Gets filter settings type + */ + @FilterSettingsType public abstract int getType(); + + // TODO: more builders and getters + + /** + * Filter Settings for a TS filter. + */ + public static class TsFilterSettings extends FilterSettings { + private int mTpid; + + private TsFilterSettings(Settings settings, int tpid) { + super(settings); + mTpid = tpid; + } + + @Override + public int getType() { + return TunerConstants.FILTER_SETTINGS_TS; + } + + /** + * Creates a new builder. + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Builder for TsFilterSettings. + */ + public static class Builder { + private Settings mSettings; + private int mTpid; + + /** + * Sets settings. + */ + public Builder setSettings(Settings settings) { + mSettings = settings; + return this; + } + + /** + * Sets TPID. + */ + public Builder setTpid(int tpid) { + mTpid = tpid; + return this; + } + + /** + * Builds a TsFilterSettings instance. + */ + public TsFilterSettings build() { + return new TsFilterSettings(mSettings, mTpid); + } + } + } + + /** + * Filter Settings for a MMTP filter. + */ + public static class MmtpFilterSettings extends FilterSettings { + private int mMmtpPid; + + public MmtpFilterSettings(Settings settings) { + super(settings); + } + + @Override + public int getType() { + return TunerConstants.FILTER_SETTINGS_MMTP; + } + } + + + /** + * Filter Settings for a IP filter. + */ + public static class IpFilterSettings extends FilterSettings { + private byte[] mSrcIpAddress; + private byte[] mDstIpAddress; + private int mSrcPort; + private int mDstPort; + private boolean mPassthrough; + + public IpFilterSettings(Settings settings) { + super(settings); + } + + @Override + public int getType() { + return TunerConstants.FILTER_SETTINGS_IP; + } + } + + + /** + * Filter Settings for a TLV filter. + */ + public static class TlvFilterSettings extends FilterSettings { + private int mPacketType; + private boolean mIsCompressedIpPacket; + private boolean mPassthrough; + + public TlvFilterSettings(Settings settings) { + super(settings); + } + + @Override + public int getType() { + return TunerConstants.FILTER_SETTINGS_TLV; + } + } + + + /** + * Filter Settings for a ALP filter. + */ + public static class AlpFilterSettings extends FilterSettings { + private int mPacketType; + private int mLengthType; + + public AlpFilterSettings(Settings settings) { + super(settings); + } + + @Override + public int getType() { + return TunerConstants.FILTER_SETTINGS_ALP; + } + } + + + /** + * Settings for filters of different subtypes. + */ + public abstract static class Settings { + protected final int mType; + + protected Settings(int type) { + mType = type; + } + + /** + * Gets filter settings type. + * @return + */ + int getType() { + return mType; + } + } + + /** + * Filter Settings for Section data according to ISO/IEC 13818-1. + */ + public static class SectionSettings extends Settings { + + private SectionSettings(int mainType) { + super(SectionSettings.findType(mainType)); + } + + private static int findType(int mainType) { + switch (mainType) { + case TunerConstants.FILTER_SETTINGS_TS: + return Constants.DemuxTsFilterType.SECTION; + case TunerConstants.FILTER_SETTINGS_MMTP: + return Constants.DemuxMmtpFilterType.SECTION; + case TunerConstants.FILTER_SETTINGS_IP: + return Constants.DemuxIpFilterType.SECTION; + case TunerConstants.FILTER_SETTINGS_TLV: + return Constants.DemuxTlvFilterType.SECTION; + case TunerConstants.FILTER_SETTINGS_ALP: + return Constants.DemuxAlpFilterType.SECTION; + } + // UNDEFINED + return 0; + } + } + + /** + * Bits Settings for Section Filter. + */ + public static class SectionSettingsWithSectionBits extends SectionSettings { + private List<Byte> mFilter; + private List<Byte> mMask; + private List<Byte> mMode; + + private SectionSettingsWithSectionBits(int mainType) { + super(mainType); + } + } + + /** + * Table information for Section Filter. + */ + public static class SectionSettingsWithTableInfo extends SectionSettings { + private int mTableId; + private int mVersion; + + private SectionSettingsWithTableInfo(int mainType) { + super(mainType); + } + } + + /** + * Filter Settings for a PES Data. + */ + public static class PesSettings extends Settings { + private int mStreamId; + private boolean mIsRaw; + + private PesSettings(int mainType, int streamId, boolean isRaw) { + super(PesSettings.findType(mainType)); + mStreamId = streamId; + mIsRaw = isRaw; + } + + private static int findType(int mainType) { + switch (mainType) { + case TunerConstants.FILTER_SETTINGS_TS: + return Constants.DemuxTsFilterType.PES; + case TunerConstants.FILTER_SETTINGS_MMTP: + return Constants.DemuxMmtpFilterType.PES; + } + // UNDEFINED + return 0; + } + + /** + * Creates a builder for PesSettings. + */ + public static Builder newBuilder(int mainType) { + return new Builder(mainType); + } + + /** + * Builder for PesSettings. + */ + public static class Builder { + private final int mMainType; + private int mStreamId; + private boolean mIsRaw; + + public Builder(int mainType) { + mMainType = mainType; + } + + /** + * Sets stream ID. + */ + public Builder setStreamId(int streamId) { + mStreamId = streamId; + return this; + } + + /** + * Sets whether it's raw. + * true if the filter send onFilterStatus instead of onFilterEvent. + */ + public Builder setIsRaw(boolean isRaw) { + mIsRaw = isRaw; + return this; + } + + /** + * Builds a PesSettings instance. + */ + public PesSettings build() { + return new PesSettings(mMainType, mStreamId, mIsRaw); + } + } + } + + /** + * Filter Settings for a Video and Audio. + */ + public static class AvSettings extends Settings { + private boolean mIsPassthrough; + + private AvSettings(int mainType, boolean isAudio) { + super(AvSettings.findType(mainType, isAudio)); + } + + private static int findType(int mainType, boolean isAudio) { + switch (mainType) { + case TunerConstants.FILTER_SETTINGS_TS: + return isAudio + ? Constants.DemuxTsFilterType.AUDIO + : Constants.DemuxTsFilterType.VIDEO; + case TunerConstants.FILTER_SETTINGS_MMTP: + return isAudio + ? Constants.DemuxMmtpFilterType.AUDIO + : Constants.DemuxMmtpFilterType.VIDEO; + } + // UNDEFINED + return 0; + } + } + + /** + * Filter Settings for a Download. + */ + public static class DownloadSettings extends Settings { + private int mDownloadId; + + public DownloadSettings(int mainType) { + super(DownloadSettings.findType(mainType)); + } + + private static int findType(int mainType) { + if (mainType == TunerConstants.FILTER_SETTINGS_MMTP) { + return Constants.DemuxMmtpFilterType.DOWNLOAD; + } + // UNDEFINED + return 0; + } + } + + /** + * The Settings for the record in DVR. + */ + public static class RecordSettings extends Settings { + private int mIndexType; + private int mIndexMask; + + public RecordSettings(int mainType) { + super(RecordSettings.findType(mainType)); + } + + private static int findType(int mainType) { + switch (mainType) { + case TunerConstants.FILTER_SETTINGS_TS: + return Constants.DemuxTsFilterType.RECORD; + case TunerConstants.FILTER_SETTINGS_MMTP: + return Constants.DemuxMmtpFilterType.RECORD; + } + // UNDEFINED + return 0; + } + } + +} diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 82cef2e43307..6537f6fb5a2b 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -243,9 +243,11 @@ public final class Tuner implements AutoCloseable { private FilterCallback mCallback; int mId; + private native int nativeConfigureFilter(int type, int subType, FilterSettings settings); private native boolean nativeStartFilter(); private native boolean nativeStopFilter(); private native boolean nativeFlushFilter(); + private native int nativeRead(byte[] buffer, int offset, int size); private Filter(int id) { mId = id; @@ -258,6 +260,14 @@ public final class Tuner implements AutoCloseable { } } + public int configure(FilterSettings settings) { + int subType = -1; + if (settings.mSettings != null) { + subType = settings.mSettings.getType(); + } + return nativeConfigureFilter(settings.getType(), subType, settings); + } + public boolean start() { return nativeStartFilter(); } @@ -269,6 +279,11 @@ public final class Tuner implements AutoCloseable { public boolean flush() { return nativeFlushFilter(); } + + public int read(@NonNull byte[] buffer, int offset, int size) { + size = Math.min(size, buffer.length - offset); + return nativeRead(buffer, offset, size); + } } private Filter openFilter(int type, int subType, int bufferSize, FilterCallback cb) { diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java index 458cb1678ded..f2d5e939f532 100644 --- a/media/java/android/media/tv/tuner/TunerConstants.java +++ b/media/java/android/media/tv/tuner/TunerConstants.java @@ -90,6 +90,17 @@ final class TunerConstants { public static final int FRONTEND_SETTINGS_ISDBS3 = 8; public static final int FRONTEND_SETTINGS_ISDBT = 9; + @Retention(RetentionPolicy.SOURCE) + @IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV, + FILTER_SETTINGS_ALP}) + public @interface FilterSettingsType {} + + public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS; + public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP; + public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP; + public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV; + public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP; + private TunerConstants() { } } diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 12b3e6735d81..4ca23a1084a2 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -135,6 +135,8 @@ cc_library_shared { shared_libs: [ "android.hardware.tv.tuner@1.0", "libandroid_runtime", + "libcutils", + "libfmq", "libhidlbase", "liblog", "libutils", diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp index 5ddfcfce072e..d3151545333c 100644 --- a/media/jni/android_media_MediaMetricsJNI.cpp +++ b/media/jni/android_media_MediaMetricsJNI.cpp @@ -16,213 +16,112 @@ #define LOG_TAG "MediaMetricsJNI" +#include <binder/Parcel.h> #include <jni.h> +#include <media/MediaAnalyticsItem.h> #include <nativehelper/JNIHelp.h> #include "android_media_MediaMetricsJNI.h" #include "android_os_Parcel.h" -#include <media/MediaAnalyticsItem.h> -#include <binder/Parcel.h> - // This source file is compiled and linked into: // core/jni/ (libandroid_runtime.so) namespace android { -// place the attributes into a java PersistableBundle object -jobject MediaMetricsJNI::writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle) { - - jclass clazzBundle = env->FindClass("android/os/PersistableBundle"); - if (clazzBundle==NULL) { - ALOGE("can't find android/os/PersistableBundle"); - return NULL; +namespace { +struct BundleHelper { + BundleHelper(JNIEnv* _env, jobject _bundle) + : env(_env) + , clazzBundle(env->FindClass("android/os/PersistableBundle")) + , putIntID(env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V")) + , putLongID(env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V")) + , putDoubleID(env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V")) + , putStringID(env->GetMethodID(clazzBundle, + "putString", "(Ljava/lang/String;Ljava/lang/String;)V")) + , constructID(env->GetMethodID(clazzBundle, "<init>", "()V")) + , bundle(_bundle == nullptr ? env->NewObject(clazzBundle, constructID) : _bundle) + { } + + JNIEnv* const env; + const jclass clazzBundle; + const jmethodID putIntID; + const jmethodID putLongID; + const jmethodID putDoubleID; + const jmethodID putStringID; + const jmethodID constructID; + jobject const bundle; + + // We use templated put to access MediaAnalyticsItem based on data type not type enum. + // See std::variant and std::visit. + template<typename T> + void put(jstring keyName, const T& value) = delete; + + template<> + void put(jstring keyName, const int32_t& value) { + env->CallVoidMethod(bundle, putIntID, keyName, (jint)value); } - // sometimes the caller provides one for us to fill - if (mybundle == NULL) { - // create the bundle - jmethodID constructID = env->GetMethodID(clazzBundle, "<init>", "()V"); - mybundle = env->NewObject(clazzBundle, constructID); - if (mybundle == NULL) { - return NULL; - } - } - - // grab methods that we can invoke - jmethodID setIntID = env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V"); - jmethodID setLongID = env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V"); - jmethodID setDoubleID = env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V"); - jmethodID setStringID = env->GetMethodID(clazzBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V"); - // env, class, method, {parms} - //env->CallVoidMethod(env, mybundle, setIntID, jstr, jint); - - // iterate through my attributes - // -- get name, get type, get value - // -- insert appropriately into the bundle - for (size_t i = 0 ; i < item->mPropCount; i++ ) { - MediaAnalyticsItem::Prop *prop = &item->mProps[i]; - // build the key parameter from prop->mName - jstring keyName = env->NewStringUTF(prop->mName); - // invoke the appropriate method to insert - switch (prop->mType) { - case MediaAnalyticsItem::kTypeInt32: - env->CallVoidMethod(mybundle, setIntID, - keyName, (jint) prop->u.int32Value); - break; - case MediaAnalyticsItem::kTypeInt64: - env->CallVoidMethod(mybundle, setLongID, - keyName, (jlong) prop->u.int64Value); - break; - case MediaAnalyticsItem::kTypeDouble: - env->CallVoidMethod(mybundle, setDoubleID, - keyName, (jdouble) prop->u.doubleValue); - break; - case MediaAnalyticsItem::kTypeCString: - env->CallVoidMethod(mybundle, setStringID, keyName, - env->NewStringUTF(prop->u.CStringValue)); - break; - default: - ALOGE("to_String bad item type: %d for %s", - prop->mType, prop->mName); - break; - } + template<> + void put(jstring keyName, const int64_t& value) { + env->CallVoidMethod(bundle, putLongID, keyName, (jlong)value); } - return mybundle; -} - -// convert the specified batch metrics attributes to a persistent bundle. -// The encoding of the byte array is specified in -// frameworks/av/media/libmediametrics/MediaAnalyticsItem.cpp -// -// type encodings; matches frameworks/av/media/libmediametrics/MediaAnalyticsItem.cpp -enum { kInt32 = 0, kInt64, kDouble, kRate, kCString}; - -jobject MediaMetricsJNI::writeAttributesToBundle(JNIEnv* env, jobject mybundle, char *buffer, size_t length) { - ALOGV("writeAttributes()"); - - if (buffer == NULL || length <= 0) { - ALOGW("bad parameters to writeAttributesToBundle()"); - return NULL; + template<> + void put(jstring keyName, const double& value) { + env->CallVoidMethod(bundle, putDoubleID, keyName, (jdouble)value); } - jclass clazzBundle = env->FindClass("android/os/PersistableBundle"); - if (clazzBundle==NULL) { - ALOGE("can't find android/os/PersistableBundle"); - return NULL; - } - // sometimes the caller provides one for us to fill - if (mybundle == NULL) { - // create the bundle - jmethodID constructID = env->GetMethodID(clazzBundle, "<init>", "()V"); - mybundle = env->NewObject(clazzBundle, constructID); - if (mybundle == NULL) { - ALOGD("unable to create mybundle"); - return NULL; - } + template<> + void put(jstring keyName, const char * const& value) { + env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value)); } - int left = length; - char *buf = buffer; + template<> + void put(jstring keyName, char * const& value) { + env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value)); + } - // grab methods that we can invoke - jmethodID setIntID = env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V"); - jmethodID setLongID = env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V"); - jmethodID setDoubleID = env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V"); - jmethodID setStringID = env->GetMethodID(clazzBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V"); + template<> + void put(jstring keyName, const std::pair<int64_t, int64_t>& value) { + ; // rate is currently ignored + } + // We allow both jstring and non-jstring variants. + template<typename T> + void put(const char *keyName, const T& value) { + put(env->NewStringUTF(keyName), value); + } +}; +} // namespace -#define _EXTRACT(size, val) \ - { if ((size) > left) goto badness; memcpy(&val, buf, (size)); buf += (size); left -= (size);} -#define _SKIP(size) \ - { if ((size) > left) goto badness; buf += (size); left -= (size);} +// place the attributes into a java PersistableBundle object +jobject MediaMetricsJNI::writeMetricsToBundle( + JNIEnv* env, MediaAnalyticsItem *item, jobject bundle) +{ + BundleHelper bh(env, bundle); + + if (bh.bundle == nullptr) { + ALOGE("%s: unable to create Bundle", __func__); + return nullptr; + } - int32_t bufsize; - _EXTRACT(sizeof(int32_t), bufsize); - if (bufsize != length) { - goto badness; + bh.put("__key", item->getKey().c_str()); + if (item->getPid() != -1) { + bh.put("__pid", (int32_t)item->getPid()); } - int32_t proto; - _EXTRACT(sizeof(int32_t), proto); - if (proto != 0) { - ALOGE("unsupported wire protocol %d", proto); - goto badness; + if (item->getTimestamp() > 0) { + bh.put("__timestamp", (int64_t)item->getTimestamp()); } - - int32_t count; - _EXTRACT(sizeof(int32_t), count); - - // iterate through my attributes - // -- get name, get type, get value, insert into bundle appropriately. - for (int i = 0 ; i < count; i++ ) { - // prop name len (int16) - int16_t keylen; - _EXTRACT(sizeof(int16_t), keylen); - if (keylen <= 0) goto badness; - // prop name itself - char *key = buf; - jstring keyName = env->NewStringUTF(buf); - _SKIP(keylen); - - // prop type (int8_t) - int8_t attrType; - _EXTRACT(sizeof(int8_t), attrType); - - int16_t attrSize; - _EXTRACT(sizeof(int16_t), attrSize); - - switch (attrType) { - case kInt32: - { - int32_t i32; - _EXTRACT(sizeof(int32_t), i32); - env->CallVoidMethod(mybundle, setIntID, - keyName, (jint) i32); - break; - } - case kInt64: - { - int64_t i64; - _EXTRACT(sizeof(int64_t), i64); - env->CallVoidMethod(mybundle, setLongID, - keyName, (jlong) i64); - break; - } - case kDouble: - { - double d64; - _EXTRACT(sizeof(double), d64); - env->CallVoidMethod(mybundle, setDoubleID, - keyName, (jdouble) d64); - break; - } - case kCString: - { - jstring value = env->NewStringUTF(buf); - env->CallVoidMethod(mybundle, setStringID, - keyName, value); - _SKIP(attrSize); - break; - } - default: - ALOGW("ignoring Attribute '%s' unknown type: %d", - key, attrType); - _SKIP(attrSize); - break; - } + if (item->getUid() != -1) { + bh.put("__uid", (int32_t)item->getUid()); } - - // should have consumed it all - if (left != 0) { - ALOGW("did not consume entire buffer; left(%d) != 0", left); - goto badness; + for (const auto &prop : *item) { + const char *name = prop.getName(); + if (name == nullptr) continue; + prop.visit([&] (auto &value) { bh.put(name, value); }); } - - return mybundle; - - badness: - return NULL; + return bh.bundle; } // Helper function to convert a native PersistableBundle to a Java diff --git a/media/jni/android_media_MediaMetricsJNI.h b/media/jni/android_media_MediaMetricsJNI.h index e879da01c6ef..63ec27aa58ee 100644 --- a/media/jni/android_media_MediaMetricsJNI.h +++ b/media/jni/android_media_MediaMetricsJNI.h @@ -28,7 +28,6 @@ namespace android { class MediaMetricsJNI { public: static jobject writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle); - static jobject writeAttributesToBundle(JNIEnv* env, jobject mybundle, char *buffer, size_t length); static jobject nativeToJavaPersistableBundle(JNIEnv*, os::PersistableBundle*); }; diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 5216906d5ec5..a0be12ebecfc 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -28,8 +28,12 @@ using ::android::hardware::Void; using ::android::hardware::hidl_vec; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid; +using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits; using ::android::hardware::tv::tuner::V1_0::DemuxTpid; +using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard; @@ -112,6 +116,26 @@ void FilterCallback::setFilter(const jobject filter) { mFilter = env->NewWeakGlobalRef(filter); } +/////////////// Filter /////////////////////// + +Filter::Filter(sp<IFilter> sp, jweak obj) : mFilterSp(sp), mFilterObj(obj) {} + +Filter::~Filter() { + EventFlag::deleteEventFlag(&mFilterMQEventFlag); +} + +int Filter::close() { + Result r = mFilterSp->close(); + if (r == Result::SUCCESS) { + EventFlag::deleteEventFlag(&mFilterMQEventFlag); + } + return (int)r; +} + +sp<IFilter> Filter::getIFilter() { + return mFilterSp; +} + /////////////// FrontendCallback /////////////////////// FrontendCallback::FrontendCallback(jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {} @@ -211,6 +235,7 @@ jobject JTuner::openFrontendById(int id) { fe->setCallback(feCb); jint jId = (jint) id; + JNIEnv *env = AndroidRuntime::getJNIEnv(); // TODO: add more fields to frontend return env->NewObject( @@ -327,18 +352,18 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { } } - sp<IFilter> filterSp; + sp<IFilter> iFilterSp; sp<FilterCallback> callback = new FilterCallback(); mDemux->openFilter(type, bufferSize, callback, [&](Result, const sp<IFilter>& filter) { - filterSp = filter; + iFilterSp = filter; }); - if (filterSp == NULL) { + if (iFilterSp == NULL) { ALOGD("Failed to open filter, type = %d", type.mainType); return NULL; } int fId; - filterSp->getId([&](Result, uint32_t filterId) { + iFilterSp->getId([&](Result, uint32_t filterId) { fId = filterId; }); @@ -350,6 +375,7 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { mObject, (jint) fId); + sp<Filter> filterSp = new Filter(iFilterSp, filterObj); filterSp->incStrong(filterObj); env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get()); @@ -432,7 +458,7 @@ static DemuxPid getDemuxPid(int pidType, int pid) { static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) { FrontendSettings frontendSettings; jclass clazz = env->FindClass("android/media/tv/tuner/FrontendSettings"); - jfieldID freqField = env->GetFieldID(clazz, "frequency", "I"); + jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "I"); uint32_t freq = static_cast<uint32_t>(env->GetIntField(clazz, freqField)); // TODO: handle the other 8 types of settings @@ -455,8 +481,8 @@ static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject setti return frontendSettings; } -static sp<IFilter> getFilter(JNIEnv *env, jobject filter) { - return (IFilter *)env->GetLongField(filter, gFields.filterContext); +static sp<Filter> getFilter(JNIEnv *env, jobject filter) { + return (Filter *)env->GetLongField(filter, gFields.filterContext); } static sp<IDvr> getDvr(JNIEnv *env, jobject dvr) { @@ -542,8 +568,100 @@ static jobject android_media_tv_Tuner_open_filter( return tuner->openFilter(filterType, bufferSize); } +static DemuxFilterSettings getFilterSettings( + JNIEnv *env, int type, int subtype, jobject filterSettingsObj) { + DemuxFilterSettings filterSettings; + // TODO: more setting types + jobject settingsObj = + env->GetObjectField( + filterSettingsObj, + env->GetFieldID( + env->FindClass("android/media/tv/tuner/FilterSettings"), + "mSettings", + "Landroid/media/tv/tuner/FilterSettings$Settings;")); + if (type == (int)DemuxFilterMainType::TS) { + // DemuxTsFilterSettings + jclass clazz = env->FindClass("android/media/tv/tuner/FilterSettings$TsFilterSettings"); + int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I")); + if (subtype == (int)DemuxTsFilterType::PES) { + // DemuxFilterPesDataSettings + jclass settingClazz = + env->FindClass("android/media/tv/tuner/FilterSettings$PesSettings"); + int streamId = env->GetIntField( + settingsObj, env->GetFieldID(settingClazz, "mStreamId", "I")); + bool isRaw = (bool)env->GetBooleanField( + settingsObj, env->GetFieldID(settingClazz, "mIsRaw", "Z")); + DemuxFilterPesDataSettings filterPesDataSettings { + .streamId = static_cast<uint16_t>(streamId), + .isRaw = isRaw, + }; + DemuxTsFilterSettings tsFilterSettings { + .tpid = static_cast<uint16_t>(tpid), + }; + tsFilterSettings.filterSettings.pesData(filterPesDataSettings); + filterSettings.ts(tsFilterSettings); + } + } + return filterSettings; +} + +static int copyData(JNIEnv *env, sp<Filter> filter, jbyteArray buffer, jint offset, int size) { + ALOGD("copyData, size=%d, offset=%d", size, offset); + + int available = filter->mFilterMQ->availableToRead(); + ALOGD("copyData, available=%d", available); + size = std::min(size, available); + + jboolean isCopy; + jbyte *dst = env->GetByteArrayElements(buffer, &isCopy); + ALOGD("copyData, isCopy=%d", isCopy); + if (dst == nullptr) { + ALOGD("Failed to GetByteArrayElements"); + return 0; + } + + if (filter->mFilterMQ->read(reinterpret_cast<unsigned char*>(dst) + offset, size)) { + env->ReleaseByteArrayElements(buffer, dst, 0); + filter->mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED)); + } else { + ALOGD("Failed to read FMQ"); + env->ReleaseByteArrayElements(buffer, dst, 0); + return 0; + } + return size; +} + +static int android_media_tv_Tuner_configure_filter( + JNIEnv *env, jobject filter, int type, int subtype, jobject settings) { + ALOGD("configure filter type=%d, subtype=%d", type, subtype); + sp<Filter> filterSp = getFilter(env, filter); + sp<IFilter> iFilterSp = filterSp->getIFilter(); + if (iFilterSp == NULL) { + ALOGD("Failed to configure filter: filter not found"); + return (int)Result::INVALID_STATE; + } + DemuxFilterSettings filterSettings = getFilterSettings(env, type, subtype, settings); + Result res = iFilterSp->configure(filterSettings); + MQDescriptorSync<uint8_t> filterMQDesc; + if (res == Result::SUCCESS && filterSp->mFilterMQ == NULL) { + Result getQueueDescResult = Result::UNKNOWN_ERROR; + iFilterSp->getQueueDesc( + [&](Result r, const MQDescriptorSync<uint8_t>& desc) { + filterMQDesc = desc; + getQueueDescResult = r; + ALOGD("getFilterQueueDesc"); + }); + if (getQueueDescResult == Result::SUCCESS) { + filterSp->mFilterMQ = std::make_unique<FilterMQ>(filterMQDesc, true); + EventFlag::createEventFlag( + filterSp->mFilterMQ->getEventFlagWord(), &(filterSp->mFilterMQEventFlag)); + } + } + return (int)res; +} + static bool android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) { - sp<IFilter> filterSp = getFilter(env, filter); + sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); if (filterSp == NULL) { ALOGD("Failed to start filter: filter not found"); return false; @@ -552,7 +670,7 @@ static bool android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) { } static bool android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) { - sp<IFilter> filterSp = getFilter(env, filter); + sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); if (filterSp == NULL) { ALOGD("Failed to stop filter: filter not found"); return false; @@ -561,7 +679,7 @@ static bool android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) { } static bool android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) { - sp<IFilter> filterSp = getFilter(env, filter); + sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); if (filterSp == NULL) { ALOGD("Failed to flush filter: filter not found"); return false; @@ -569,6 +687,16 @@ static bool android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) { return filterSp->flush() == Result::SUCCESS; } +static int android_media_tv_Tuner_read_filter_fmq( + JNIEnv *env, jobject filter, jbyteArray buffer, jint offset, jint size) { + sp<Filter> filterSp = getFilter(env, filter); + if (filterSp == NULL) { + ALOGD("Failed to read filter FMQ: filter not found"); + return 0; + } + return copyData(env, filterSp, buffer, offset, size); +} + static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->openDescrambler(); @@ -580,7 +708,7 @@ static bool android_media_tv_Tuner_add_pid( if (descramblerSp == NULL) { return false; } - sp<IFilter> filterSp = getFilter(env, filter); + sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), filterSp); return result == Result::SUCCESS; } @@ -591,7 +719,7 @@ static bool android_media_tv_Tuner_remove_pid( if (descramblerSp == NULL) { return false; } - sp<IFilter> filterSp = getFilter(env, filter); + sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), filterSp); return result == Result::SUCCESS; } @@ -603,7 +731,7 @@ static jobject android_media_tv_Tuner_open_dvr(JNIEnv *env, jobject thiz, jint t static bool android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) { sp<IDvr> dvrSp = getDvr(env, dvr); - sp<IFilter> filterSp = getFilter(env, filter); + sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); if (dvrSp == NULL || filterSp == NULL) { return false; } @@ -613,7 +741,7 @@ static bool android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobje static bool android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) { sp<IDvr> dvrSp = getDvr(env, dvr); - sp<IFilter> filterSp = getFilter(env, filter); + sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); if (dvrSp == NULL || filterSp == NULL) { return false; } @@ -670,9 +798,12 @@ static const JNINativeMethod gTunerMethods[] = { }; static const JNINativeMethod gFilterMethods[] = { + { "nativeConfigureFilter", "(IILandroid/media/tv/tuner/FilterSettings;)I", + (void *)android_media_tv_Tuner_configure_filter }, { "nativeStartFilter", "()Z", (void *)android_media_tv_Tuner_start_filter }, { "nativeStopFilter", "()Z", (void *)android_media_tv_Tuner_stop_filter }, { "nativeFlushFilter", "()Z", (void *)android_media_tv_Tuner_flush_filter }, + { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_filter_fmq }, }; static const JNINativeMethod gDescramblerMethods[] = { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index f856791e2618..467acb8cdbdd 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -18,13 +18,18 @@ #define _ANDROID_MEDIA_TV_TUNER_H_ #include <android/hardware/tv/tuner/1.0/ITuner.h> +#include <fmq/MessageQueue.h> #include <unordered_map> #include <utils/RefBase.h> #include "jni.h" +using ::android::hardware::EventFlag; +using ::android::hardware::MQDescriptorSync; +using ::android::hardware::MessageQueue; using ::android::hardware::Return; using ::android::hardware::hidl_vec; +using ::android::hardware::kSynchronizedReadWrite; using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus; using ::android::hardware::tv::tuner::V1_0::DemuxFilterType; @@ -51,6 +56,8 @@ using ::android::hardware::tv::tuner::V1_0::LnbId; using ::android::hardware::tv::tuner::V1_0::PlaybackStatus; using ::android::hardware::tv::tuner::V1_0::RecordStatus; +using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; + namespace android { struct LnbCallback : public ILnbCallback { @@ -91,6 +98,17 @@ struct FrontendCallback : public IFrontendCallback { FrontendId mId; }; +struct Filter : public RefBase { + Filter(sp<IFilter> sp, jweak obj); + ~Filter(); + int close(); + sp<IFilter> getIFilter(); + sp<IFilter> mFilterSp; + std::unique_ptr<FilterMQ> mFilterMQ; + EventFlag* mFilterMQEventFlag; + jweak mFilterObj; +}; + struct JTuner : public RefBase { JTuner(JNIEnv *env, jobject thiz); sp<ITuner> getTunerService(); diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 79e4d8ae6e26..c8f0ff10ca3f 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -38,7 +38,7 @@ static constexpr bool kStealActiveStream_OldestFirst = true; // kPlayOnCallingThread = true prior to R. // Changing to false means calls to play() are almost instantaneous instead of taking around // ~10ms to launch the AudioTrack. It is perhaps 100x faster. -static constexpr bool kPlayOnCallingThread = true; +static constexpr bool kPlayOnCallingThread = false; // Amount of time for a StreamManager thread to wait before closing. static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND; @@ -170,7 +170,6 @@ int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound, if (stream->getSoundID() == soundID) { ALOGV("%s: found soundID %d in restart queue", __func__, soundID); newStream = stream; - fromAvailableQueue = false; break; } else if (newStream == nullptr) { ALOGV("%s: found stream in restart queue", __func__); diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java index 45430197fdd8..291cdd5ea3d8 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java @@ -35,6 +35,7 @@ import android.view.ViewStub; import androidx.recyclerview.widget.GridLayoutManager; +import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; import com.android.systemui.car.CarServiceProvider; import com.android.systemui.dagger.qualifiers.MainResources; @@ -135,8 +136,9 @@ public class FullscreenUserSwitcher { /* isAddUser= */ false, /* isForeground= */ true); - // If the initial user has trusted device, display the unlock dialog on the keyguard. - if (hasTrustedDevice(initialUser)) { + // If the initial user has screen lock and trusted device, display the unlock dialog on the + // keyguard. + if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) { mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser, mOnHideListener); } else { @@ -178,7 +180,7 @@ public class FullscreenUserSwitcher { */ private void onUserSelected(UserGridRecyclerView.UserRecord record) { mSelectedUser = record; - if (hasTrustedDevice(record.mInfo.id)) { + if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) { mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener); return; } @@ -216,6 +218,12 @@ public class FullscreenUserSwitcher { } + private boolean hasScreenLock(int uid) { + LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); + return lockPatternUtils.getCredentialTypeForUser(uid) + != LockPatternUtils.CREDENTIAL_TYPE_NONE; + } + private boolean hasTrustedDevice(int uid) { if (mEnrollmentManager == null) { // car service not ready, so it cannot be available. return false; diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index 26ec5726a4a2..9f13a7b861a0 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -9,12 +9,6 @@ "include-filter": "android.platform.test.scenario.sysui" }, { - "include-filter": "android.platform.test.scenario.quicksettings" - }, - { - "include-filter": "android.platform.test.scenario.notification" - }, - { "include-annotation": "android.platform.test.scenario.annotation.Scenario" }, { diff --git a/packages/SystemUI/docs/executors.md b/packages/SystemUI/docs/executors.md new file mode 100644 index 000000000000..8520ce228c9d --- /dev/null +++ b/packages/SystemUI/docs/executors.md @@ -0,0 +1,321 @@ +# Executors + +go/sysui-executors + +[TOC] + +## TLDR + +In SystemUI, we are encouraging the use of Java's [Executor][Executor] over +Android's [Handler][Handler] when shuffling a [Runnable][Runnable] between +threads or delaying the execution of a Runnable. We have an implementation of +Executor available, as well as our own sub-interface, +[DelayableExecutor][DelayableExecutor] available. For test, +[FakeExecutor][FakeExecutor] is available. + +[Executor]: https://developer.android.com/reference/java/util/concurrent/Executor.html +[Handler]: https://developer.android.com/reference/android/os/Handler +[Runnable]: https://developer.android.com/reference/java/lang/Runnable.html +[DelayableExecutor]: /packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java +[FakeExecutor]: /packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java + +## Rationale + +Executors make testing easier and are generally more flexible than Handlers. +They are defined as an interface, making it easy to swap in fake implementations +for testing. This also makes it easier to supply alternate implementations +generally speaking - shared thread pools; priority queues; etc. + +For testing, whereas a handler involves trying to directly control its +underlying Looper (using things like `Thread.sleep()` as well as overriding +internal behaviors), an Executor implementation can be made to be directly +controllable and inspectable. + +See also go/executors-for-the-android-engineer + +## Available Executors + +At present, there are two interfaces of Executor avaiable, each implemented, and +each with two instances - `@Background` and `@Main`. + +### Executor + +The simplest Executor available implements the interface directly, making +available one method: `Executor.execute()`. You can access an implementation of +this Executor through Dependency Injection: + +```java + public class Foobar { + @Inject + public Foobar(@Background Executor bgExecutor) { + bgExecutor.execute(new Runnable() { + // ... + }); + } + } +``` + +`@Main` will give you an Executor that runs on the ui thread. `@Background` will +give you one that runs on a _shared_ non-ui thread. If you ask for an +non-annotated Executor, you will get the `@Background` Executor. + +We do not currently have support for creating an Executor on a new, virgin +thread. We do not currently support any sort of shared pooling of threads. If +you require either of these, please reach out. + +### DelayableExecutor + +[DelayableExecutor][DelayableExecutor] is the closest analogue we provide to +Handler. It adds `executeDelayed(Runnable r, long delayMillis)` and +`executeAtTime(Runnable r, long uptimeMillis)` to the interface, just like +Handler's [postDelayed][postDelayed] and [postAtTime][postAttime]. It also adds +the option to supply a [TimeUnit][TimeUnit] as a third argument. + +A DelayableExecutor can be accessed via Injection just like a standard Executor. +In fact, at this time, it shares the same underlying thread as our basic +Executor. + +```java + public class Foobar { + @Inject + public Foobar(@Background DelayableExecutor bgExecutor) { + bgExecutor.executeDelayed(new Runnable() { + // ... + }, 1, TimeUnit.MINUTES); + } + } +``` + +Unlike Handler, the added methods return a Runnable that, when run, cancels the +originally supplied Runnable if it has not yet started execution: + +```java + public class Foobar { + @Inject + public Foobar(@Background DelayableExecutor bgExecutor) { + Runnable cancel = bgExecutor.executeDelayed(new Runnable() { + // ... + }, 1, TimeUnit.MINUTES); + + cancel.run(); // The supplied Runnable will (probably) not run. + } + } +``` + +[postDelayed]: https://developer.android.com/reference/android/os/Handler#postDelayed(java.lang.Runnable,%20long) +[postAttime]: https://developer.android.com/reference/android/os/Handler#postAtTime(java.lang.Runnable,%20long) +[TimeUnit]: https://developer.android.com/reference/java/util/concurrent/TimeUnit + +## Moving From Handler + +Most use cases of Handlers can easily be handled by the above two interfaces +above. A minor refactor makes the switch: + +Handler | Executor | DelayableExecutor +------------- | --------- | ----------------- +post() | execute() | execute() +postDelayed() | `none` | executeDelayed() +postAtTime() | `none` | executeAtTime() + +There is one notable gap in this implementation: `Handler.postAtFrontOfQueue()`. +If you require this method, or similar, please reach out. The idea of a +PriorityQueueExecutor has been floated, but will not be implemented until there +is a clear need. + +Note also that "canceling" semantics are different. Instead of passing a `token` +object to `Handler.postDelayed()`, you receive a Runnable that, when run, +cancels the originally supplied Runnable. + +### Message Handling + +Executors have no concept of message handling. This is an oft used feature of +Handlers. There are (as of 2019-12-05) 37 places where we subclass Handler to +take advantage of this. However, by-and-large, these subclases take the +following form: + +```Java +mHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_A: + handleMessageA(); + break; + case MSG_B: + handleMessageB((String) msg.obj); + break; + case MSG_C: + handleMessageC((Foobar) msg.obj); + break; + // ... + } + } +}; + +// Elsewhere in the class +void doSomething() { + mHandler.obtainMessage(MSG_B, "some string"); + mHandler.sendMessage(msg); +} +``` + +This could easily be replaced by equivalent, more direct Executor code: + +```Java +void doSomething() { + mExecutor.execute(() -> handleMessageB("some string")); +} +``` + +If you are posting Runnables frequently and you worry that the cost of creating +anonymous Runnables is too high, consider creating pre-defined Runnables as +fields in your class. + +If you feel that you have a use case that this does not cover, please reach out. + +### Handlers Are Still Necessary + +Handlers aren't going away. There are Android APIs that still require them (even +if future API development discourages them). A simple example is +[ContentObserver][ContentObserver]. Use them where necessary. + +[ContentObserver]: https://developer.android.com/reference/android/database/ContentObserver + +## Testing (FakeExecutor) + +We have a [FakeExecutor][FakeExecutor] available. It implements +DelayableExecutor (which in turn is an Executor). It takes a FakeSystemClock in +its constructor that allows you to control the flow of time, executing supplied +Runnables in a deterministic manner. + +The implementation is well documented and tested. You are encouraged to read and +reference it, but here is a quick overview: + +<table> + <tr> + <th>Method</th> + <th>Description</th> + </tr> + <tr> + <td>execute()</td> + <td> + Queues a Runnable so that it is "ready" + to run. (A Runnable is "ready" when its + scheduled time is less than or equal to + the clock.) + </td> + </tr> + <tr> + <td>postDelayed() & postAtTime()</td> + <td> + Queues a runnable to be run at some + point in the future. + </td> + </tr> + <tr> + <td>runNextReady()</td> + <td> + Run one runnable if it is ready to run + according to the supplied clock. + </td> + </tr> + <tr> + <td>runAllReady()</td> + <td> + Calls runNextReady() in a loop until + there are no more "ready" runnables. + </td> + </tr> + <tr> + <td>advanceClockToNext()</td> + <td> + Move the internal clock to the item at + the front of the queue, making it + "ready". + </td> + </tr> + <tr> + <td>advanceClockToLast()</td> + <td> + Makes all currently queued items ready. + </td> + </tr> + <tr> + <td>numPending()</td> + <td> + The number of runnables waiting to be run + They are not necessarily "ready". + </td> + </tr> + <tr> + <td>(static method) exhaustExecutors()</td> + <td> + Given a number of FakeExecutors, it + calls runAllReady() repeated on them + until none of them have ready work. + Useful if you have Executors that post + work to one another back and forth. + </td> + </tr> +</table> + +_If you advance the supplied FakeSystemClock directly, the FakeExecutor will +execute pending Runnables accordingly._ If you use the FakeExecutors +`advanceClockToNext()` and `advanceClockToLast()`, this behavior will not be +seen. You will need to tell the Executor to run its ready items. A quick example +shows the difference: + +Here we advance the clock directly: + +```java +FakeSystemClock clock = new FakeSystemClock(); +FakeExecutor executor = new FakeExecutor(clock); +executor.execute(() -> {}); // Nothing run yet. Runs at time-0 +executor.executeDelayed(() -> {}, 100); // Nothing run yet. Runs at time-100. +executor.executeDelayed(() -> {}, 500); // Nothing run yet. Runs at time-500. + +clock.synchronizeListeners(); // The clock just told the Executor it's time-0. + // One thing run. +clock.setUptimeMillis(500); // The clock just told the Executor it's time-500. + // Two more items run. +``` + +Here we have more fine-grained control: + +```java +FakeSystemClock clock = new FakeSystemClock(); +FakeExecutor executor = new FakeExecutor(clock); +executor.execute(() -> {}); // Nothing run yet. Runs at time-0 +executor.executeDelayed(() -> {}, 100); // Nothing run yet. Runs at time-100. +executor.executeDelayed(() -> {}, 500); // Nothing run yet. Runs at time-500. + +executor.runNextReady(); // One thing run. +executor.advanceClockToNext(); // One more thing ready to run. +executor.runNextReady(); // One thing run. +executor.runNextReady(); // Extra calls do nothing. (Returns false). +executor.advanceClockToNext(); // One more thing ready to run. +executor.runNextReady(); // Last item run. +``` + +One gotcha of direct-clock-advancement: If you have interleaved Runnables split +between two executors like the following: + +```java +FakeSystemClock clock = new FakeSystemClock(); +FakeExecutor executorA = new FakeExecutor(clock); +FakeExecutor executorB = new FakeExecutor(clock); +executorA.executeDelayed(() -> {}, 100); +executorB.executeDelayed(() -> {}, 200); +executorA.executeDelayed(() -> {}, 300); +executorB.executeDelayed(() -> {}, 400); +clock.setUptimeMillis(500); +``` + +The Runnables _will not_ interleave. All of one Executor's callbacks will run, +then all of the other's. + +### TestableLooper.RunWithLooper + +As long as you're using FakeExecutors in all the code under test (and no +Handlers or Loopers) you don't need it. Get rid of it. No more TestableLooper; +no more Looper at all, for that matter. diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java index b21a9f7c4d05..6c4cbdf27fa5 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java @@ -34,7 +34,8 @@ public interface StatusBarStateController { int getState(); /** - * Is device dozing + * Is device dozing. Dozing is when the screen is in AOD or asleep given that + * {@link com.android.systemui.doze.DozeService} is configured. */ boolean isDozing(); diff --git a/packages/SystemUI/res/layout/bubble_menu_view.xml b/packages/SystemUI/res/layout/bubble_menu_view.xml new file mode 100644 index 000000000000..24608d3e9611 --- /dev/null +++ b/packages/SystemUI/res/layout/bubble_menu_view.xml @@ -0,0 +1,43 @@ +<?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. + --> +<com.android.systemui.bubbles.BubbleMenuView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:background="#66000000" + android:visibility="gone" + android:id="@+id/bubble_menu_container"> + + <FrameLayout + android:layout_height="@dimen/individual_bubble_size" + android:layout_width="wrap_content" + android:background="#FFFFFF" + android:id="@+id/bubble_menu_view"> + + <ImageView + android:id="@*android:id/icon" + android:layout_width="@dimen/global_actions_grid_item_icon_width" + android:layout_height="@dimen/global_actions_grid_item_icon_height" + android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin" + android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin" + android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin" + android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin" + android:scaleType="centerInside" + android:tint="@color/global_actions_text" + /> + </FrameLayout> +</com.android.systemui.bubbles.BubbleMenuView> diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml new file mode 100644 index 000000000000..27db294a98d8 --- /dev/null +++ b/packages/SystemUI/res/values-television/config.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- SystemUI Services: The classes of the stuff to start. --> + <string-array name="config_systemUIServiceComponents" translatable="false"> + <item>com.android.systemui.volume.VolumeUI</item> + <item>com.android.systemui.stackdivider.Divider</item> + <item>com.android.systemui.statusbar.tv.TvStatusBar</item> + <item>com.android.systemui.usb.StorageNotification</item> + <item>com.android.systemui.power.PowerUI</item> + <item>com.android.systemui.media.RingtonePlayer</item> + <item>com.android.systemui.keyboard.KeyboardUI</item> + <item>com.android.systemui.pip.PipUI</item> + <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item> + <item>@string/config_systemUIVendorServiceComponent</item> + <item>com.android.systemui.SliceBroadcastRelayHandler</item> + <item>com.android.systemui.SizeCompatModeActivityController</item> + <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item> + </string-array> +</resources> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index dbb193669083..19381940543e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -44,13 +44,19 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.UserIdInt; import android.app.ActivityManager.RunningTaskInfo; +import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.RemoteInput; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.ShortcutManager; import android.content.res.Configuration; import android.graphics.Rect; +import android.net.Uri; +import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.service.notification.NotificationListenerService.RankingMap; @@ -69,6 +75,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.util.ScreenshotHelper; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -86,6 +93,7 @@ import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.policy.ZenModeController; import java.io.FileDescriptor; @@ -93,8 +101,10 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Singleton; @@ -138,6 +148,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer; private final NotificationGroupManager mNotificationGroupManager; private final Lazy<ShadeController> mShadeController; + private final RemoteInputUriController mRemoteInputUriController; + private Handler mHandler = new Handler() {}; private BubbleData mBubbleData; @Nullable private BubbleStackView mStackView; @@ -155,6 +167,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private final StatusBarWindowController mStatusBarWindowController; private final ZenModeController mZenModeController; private StatusBarStateListener mStatusBarStateListener; + private final ScreenshotHelper mScreenshotHelper; + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private IStatusBarService mBarService; @@ -192,6 +206,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } /** + * Listener for handling bubble screenshot events. + */ + public interface BubbleScreenshotListener { + /** + * Called to trigger taking a screenshot and sending the result to a bubble. + */ + void onBubbleScreenshot(Bubble bubble); + } + + /** * Listens for the current state of the status bar and updates the visibility state * of bubbles as needed. */ @@ -226,10 +250,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManager groupManager, - NotificationEntryManager entryManager) { + NotificationEntryManager entryManager, + RemoteInputUriController remoteInputUriController) { this(context, statusBarWindowController, statusBarStateController, shadeController, data, null /* synchronizer */, configurationController, interruptionStateProvider, - zenModeController, notifUserManager, groupManager, entryManager); + zenModeController, notifUserManager, groupManager, entryManager, + remoteInputUriController); } public BubbleController(Context context, @@ -243,11 +269,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManager groupManager, - NotificationEntryManager entryManager) { + NotificationEntryManager entryManager, + RemoteInputUriController remoteInputUriController) { mContext = context; mNotificationInterruptionStateProvider = interruptionStateProvider; mNotifUserManager = notifUserManager; mZenModeController = zenModeController; + mRemoteInputUriController = remoteInputUriController; mZenModeController.addCallback(new ZenModeController.Callback() { @Override public void onZenChanged(int zen) { @@ -320,6 +348,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi }); mUserCreatedBubbles = new HashSet<>(); + + mScreenshotHelper = new ScreenshotHelper(context); } /** @@ -337,6 +367,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); } + if (mBubbleScreenshotListener != null) { + mStackView.setBubbleScreenshotListener(mBubbleScreenshotListener); + } } } @@ -1058,4 +1091,71 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } } + + // TODO: Copied from RemoteInputView. Consolidate RemoteInput intent logic. + private Intent prepareRemoteInputFromData(String contentType, Uri data, + RemoteInput remoteInput, NotificationEntry entry) { + HashMap<String, Uri> results = new HashMap<>(); + results.put(contentType, data); + mRemoteInputUriController.grantInlineReplyUriPermission(entry.getSbn(), data); + Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + RemoteInput.addDataResultToIntent(remoteInput, fillInIntent, results); + + return fillInIntent; + } + + // TODO: Copied from RemoteInputView. Consolidate RemoteInput intent logic. + private void sendRemoteInput(Intent intent, NotificationEntry entry, + PendingIntent pendingIntent) { + // Tell ShortcutManager that this package has been "activated". ShortcutManager + // will reset the throttling for this package. + // Strictly speaking, the intent receiver may be different from the notification publisher, + // but that's an edge case, and also because we can't always know which package will receive + // an intent, so we just reset for the publisher. + mContext.getSystemService(ShortcutManager.class).onApplicationActive( + entry.getSbn().getPackageName(), + entry.getSbn().getUser().getIdentifier()); + + try { + pendingIntent.send(mContext, 0, intent); + } catch (PendingIntent.CanceledException e) { + Log.i(TAG, "Unable to send remote input result", e); + } + } + + private void sendScreenshotToBubble(Bubble bubble) { + // delay allows the bubble menu to disappear before the screenshot + // done here because we already have a Handler to delay with. + // TODO: Hide bubble + menu UI from screenshots entirely instead of just delaying. + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mScreenshotHelper.takeScreenshot( + android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN, + true /* hasStatus */, + true /* hasNav */, + mHandler, + new Consumer<Uri>() { + @Override + public void accept(Uri uri) { + if (uri != null) { + NotificationEntry entry = bubble.getEntry(); + Pair<RemoteInput, Notification.Action> pair = entry.getSbn() + .getNotification().findRemoteInputActionPair(false); + RemoteInput remoteInput = pair.first; + Notification.Action action = pair.second; + Intent dataIntent = prepareRemoteInputFromData("image/png", uri, + remoteInput, entry); + sendRemoteInput(dataIntent, entry, action.actionIntent); + mBubbleData.setSelectedBubble(bubble); + mBubbleData.setExpanded(true); + } + } + }); + } + }, 200); + } + + private final BubbleScreenshotListener mBubbleScreenshotListener = + bubble -> sendScreenshotToBubble(bubble); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java index e138d9387ca6..8299f2261b8e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java @@ -68,6 +68,9 @@ public class BubbleExperimentConfig { private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps"; + private static final String ALLOW_BUBBLE_MENU = "allow_bubble_screenshot_menu"; + private static final boolean ALLOW_BUBBLE_MENU_DEFAULT = false; + /** * When true, if a notification has the information necessary to bubble (i.e. valid * contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata} @@ -123,6 +126,16 @@ public class BubbleExperimentConfig { } /** + * When true, show a menu when a bubble is long-pressed, which will allow the user to take + * actions on that bubble. + */ + static boolean allowBubbleScreenshotMenu(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ALLOW_BUBBLE_MENU, + ALLOW_BUBBLE_MENU_DEFAULT ? 1 : 0) != 0; + } + + /** * If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds * {@link android.app.Notification.BubbleMetadata} to the notification entry as long as * the notification has necessary info for BubbleMetadata. diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java new file mode 100644 index 000000000000..e8eb72e8392f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java @@ -0,0 +1,81 @@ +/* + * 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.bubbles; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.systemui.R; + +/** + * Menu which allows users to take actions on bubbles, ex. screenshots. + */ +public class BubbleMenuView extends FrameLayout { + private FrameLayout mMenu; + private boolean mShowing = false; + + public BubbleMenuView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BubbleMenuView(Context context) { + super(context); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mMenu = findViewById(R.id.bubble_menu_view); + ImageView icon = findViewById(com.android.internal.R.id.icon); + icon.setImageDrawable(mContext.getDrawable(com.android.internal.R.drawable.ic_screenshot)); + } + + /** + * Get the bubble menu view. + */ + public View getMenuView() { + return mMenu; + } + + /** + * Checks whether the bubble menu is currently displayed. + */ + public boolean isShowing() { + return mShowing; + } + + /** + * Show the bubble menu at the specified position on the screen. + */ + public void show(float x, float y) { + mShowing = true; + this.setVisibility(VISIBLE); + mMenu.setTranslationX(x); + mMenu.setTranslationY(y); + } + + /** + * Hide the bubble menu. + */ + public void hide() { + mShowing = false; + this.setVisibility(GONE); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 29de2f049690..29a4bb1fca84 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -110,6 +110,7 @@ public class BubbleStackView extends FrameLayout { /** How long to wait, in milliseconds, before hiding the flyout. */ @VisibleForTesting static final int FLYOUT_HIDE_AFTER = 5000; + private BubbleController.BubbleScreenshotListener mBubbleScreenshotListener; /** * Interface to synchronize {@link View} state and the screen. @@ -163,6 +164,7 @@ public class BubbleStackView extends FrameLayout { private ExpandedAnimationController mExpandedAnimationController; private FrameLayout mExpandedViewContainer; + @Nullable private BubbleMenuView mBubbleMenuView; private BubbleFlyoutView mFlyout; /** Runnable that fades out the flyout and then sets it to GONE. */ @@ -194,6 +196,7 @@ public class BubbleStackView extends FrameLayout { private int mPointerHeight; private int mStatusBarHeight; private int mImeOffset; + private int mBubbleMenuOffset = 252; private BubbleIconFactory mBubbleIconFactory; private Bubble mExpandedBubble; private boolean mIsExpanded; @@ -492,6 +495,9 @@ public class BubbleStackView extends FrameLayout { mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix)); mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint); }); + + mInflater.inflate(R.layout.bubble_menu_view, this); + mBubbleMenuView = findViewById(R.id.bubble_menu_container); } private void setUpFlyout() { @@ -683,6 +689,13 @@ public class BubbleStackView extends FrameLayout { } /** + * Sets the screenshot listener. + */ + public void setBubbleScreenshotListener(BubbleController.BubbleScreenshotListener listener) { + mBubbleScreenshotListener = listener; + } + + /** * Whether the stack of bubbles is expanded or not. */ public boolean isExpanded() { @@ -870,6 +883,12 @@ public class BubbleStackView extends FrameLayout { public View getTargetView(MotionEvent event) { float x = event.getRawX(); float y = event.getRawY(); + if (mBubbleMenuView.isShowing()) { + if (isIntersecting(mBubbleMenuView.getMenuView(), x, y)) { + return mBubbleMenuView; + } + return null; + } if (mIsExpanded) { if (isIntersecting(mBubbleContainer, x, y)) { // Could be tapping or dragging a bubble while expanded @@ -1074,6 +1093,7 @@ public class BubbleStackView extends FrameLayout { return; } + hideBubbleMenu(); mStackAnimationController.cancelStackPositionAnimations(); mBubbleContainer.setActiveController(mStackAnimationController); hideFlyoutImmediate(); @@ -1473,6 +1493,11 @@ public class BubbleStackView extends FrameLayout { @Override public void getBoundsOnScreen(Rect outRect) { + // If the bubble menu is open, the entire screen should capture touch events. + if (mBubbleMenuView.isShowing()) { + outRect.set(0, 0, getWidth(), getHeight()); + return; + } if (!mIsExpanded) { if (mBubbleContainer.getChildCount() > 0) { mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect); @@ -1700,4 +1725,43 @@ public class BubbleStackView extends FrameLayout { } return bubbles; } + + /** + * Show the bubble menu, positioned relative to the stack. + */ + public void showBubbleMenu() { + PointF currentPos = mStackAnimationController.getStackPosition(); + float yPos = currentPos.y; + float xPos = currentPos.x; + if (mStackAnimationController.isStackOnLeftSide()) { + xPos += mBubbleSize; + } else { + //TODO: Use the width of the menu instead of this fixed offset. Offset used for now + // because menu width isn't correct the first time the menu is shown. + xPos -= mBubbleMenuOffset; + } + + mBubbleMenuView.show(xPos, yPos); + } + + /** + * Hide the bubble menu. + */ + public void hideBubbleMenu() { + mBubbleMenuView.hide(); + } + + /** + * Determines whether the bubble menu is currently showing. + */ + public boolean isShowingBubbleMenu() { + return mBubbleMenuView.isShowing(); + } + + /** + * Take a screenshot and send it to the specified bubble. + */ + public void sendScreenshotToBubble(Bubble bubble) { + mBubbleScreenshotListener.onBubbleScreenshot(bubble); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index 44e013a34f54..b1d205c79c99 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java @@ -57,6 +57,7 @@ class BubbleTouchHandler implements View.OnTouchListener { private final PointF mViewPositionOnTouchDown = new PointF(); private final BubbleStackView mStack; private final BubbleData mBubbleData; + private final Context mContext; private BubbleController mController = Dependency.get(BubbleController.class); @@ -75,6 +76,7 @@ class BubbleTouchHandler implements View.OnTouchListener { mTouchSlopSquared = touchSlop * touchSlop; mBubbleData = bubbleData; mStack = stackView; + mContext = context; } @Override @@ -91,15 +93,24 @@ class BubbleTouchHandler implements View.OnTouchListener { // anything, collapse the stack. if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) { mBubbleData.setExpanded(false); + mStack.hideBubbleMenu(); resetForNextGesture(); return false; } + if (mTouchedView instanceof BubbleMenuView) { + mStack.hideBubbleMenu(); + resetForNextGesture(); + mStack.sendScreenshotToBubble(mBubbleData.getSelectedBubble()); + return false; + } + if (!(mTouchedView instanceof BadgedImageView) && !(mTouchedView instanceof BubbleStackView) && !(mTouchedView instanceof BubbleFlyoutView)) { // Not touching anything touchable, but we shouldn't collapse (e.g. touching edge // of expanded view). + mStack.hideBubbleMenu(); resetForNextGesture(); return false; } @@ -132,6 +143,10 @@ class BubbleTouchHandler implements View.OnTouchListener { break; case MotionEvent.ACTION_MOVE: + // block all further touch inputs once the menu is open + if (mStack.isShowingBubbleMenu()) { + return true; + } trackMovement(event); final float deltaX = rawX - mTouchDown.x; final float deltaY = rawY - mTouchDown.y; @@ -148,6 +163,13 @@ class BubbleTouchHandler implements View.OnTouchListener { } else { mStack.onBubbleDragged(mTouchedView, viewX, viewY); } + } else { + float touchTime = event.getEventTime() - event.getDownTime(); + if (touchTime > ViewConfiguration.getLongPressTimeout() && !mStack.isExpanded() + && BubbleExperimentConfig.allowBubbleScreenshotMenu(mContext)) { + mStack.showBubbleMenu(); + return true; + } } final boolean currentlyInDismissTarget = mStack.isInDismissTarget(event); @@ -171,6 +193,10 @@ class BubbleTouchHandler implements View.OnTouchListener { break; case MotionEvent.ACTION_UP: + if (mStack.isShowingBubbleMenu()) { + resetForNextGesture(); + return true; + } trackMovement(event); mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000); final float velX = mVelocityTracker.getXVelocity(); @@ -261,7 +287,6 @@ class BubbleTouchHandler implements View.OnTouchListener { mVelocityTracker.recycle(); mVelocityTracker = null; } - mTouchedView = null; mMovedEnough = false; mInDismissTarget = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index d3a9c2c3c87d..246b0f0e19ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -61,7 +61,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ScrimState; -import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -105,9 +104,6 @@ public class NotificationMediaManager implements Dumpable { private final NotificationEntryManager mEntryManager; - // Late binding, also @Nullable due to being in com.android.systemui.statusbar.phone package - @Nullable - private Lazy<ShadeController> mShadeController; @Nullable private Lazy<StatusBarWindowController> mStatusBarWindowController; @@ -183,7 +179,6 @@ public class NotificationMediaManager implements Dumpable { @Inject public NotificationMediaManager( Context context, - Lazy<ShadeController> shadeController, Lazy<StatusBar> statusBarLazy, Lazy<StatusBarWindowController> statusBarWindowController, NotificationEntryManager notificationEntryManager, @@ -193,11 +188,10 @@ public class NotificationMediaManager implements Dumpable { mMediaArtworkProcessor = mediaArtworkProcessor; mKeyguardBypassController = keyguardBypassController; mMediaListeners = new ArrayList<>(); - mMediaSessionManager - = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); // TODO: use MediaSessionManager.SessionListener to hook us up to future updates // in session state - mShadeController = shadeController; + mMediaSessionManager = (MediaSessionManager) mContext.getSystemService( + Context.MEDIA_SESSION_SERVICE); // TODO: use KeyguardStateController#isOccluded to remove this dependency mStatusBarLazy = statusBarLazy; mStatusBarWindowController = statusBarWindowController; @@ -603,9 +597,7 @@ public class NotificationMediaManager implements Dumpable { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); } - ShadeController shadeController = mShadeController.get(); - boolean cannotAnimateDoze = shadeController != null - && shadeController.isDozing() + boolean cannotAnimateDoze = mStatusBarStateController.isDozing() && !ScrimState.AOD.getAnimateChange(); boolean needsBypassFading = mKeyguardStateController.isBypassFadingAnimation(); if (((mBiometricUnlockController != null && mBiometricUnlockController.getMode() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index c556bc0b39d1..f6f3ac1b5aaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -58,7 +58,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.policy.RemoteInputView; @@ -116,7 +116,7 @@ public class NotificationRemoteInputManager implements Dumpable { private final NotificationEntryManager mEntryManager; private final Handler mMainHandler; - private final Lazy<ShadeController> mShadeController; + private final Lazy<StatusBar> mStatusBarLazy; protected final Context mContext; private final UserManager mUserManager; @@ -136,7 +136,7 @@ public class NotificationRemoteInputManager implements Dumpable { @Override public boolean onClickHandler( View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) { - mShadeController.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view, + mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK"); if (handleRemoteInput(view, pendingIntent)) { @@ -261,7 +261,7 @@ public class NotificationRemoteInputManager implements Dumpable { NotificationLockscreenUserManager lockscreenUserManager, SmartReplyController smartReplyController, NotificationEntryManager notificationEntryManager, - Lazy<ShadeController> shadeController, + Lazy<StatusBar> statusBarLazy, StatusBarStateController statusBarStateController, @MainHandler Handler mainHandler, RemoteInputUriController remoteInputUriController) { @@ -269,7 +269,7 @@ public class NotificationRemoteInputManager implements Dumpable { mLockscreenUserManager = lockscreenUserManager; mSmartReplyController = smartReplyController; mEntryManager = notificationEntryManager; - mShadeController = shadeController; + mStatusBarLazy = statusBarLazy; mMainHandler = mainHandler; mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index e516af590e34..88f148b00cdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -211,7 +211,7 @@ constructor( setUserLocked(mStartingChild!!, false) mStartingChild = null } - if (shadeController.isDozing) { + if (statusBarStateController.isDozing) { isWakingToShadeLocked = true wakeUpCoordinator.willWakeUp = true mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java index 65f3fa94374b..31b7cb0fb5a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java @@ -24,18 +24,16 @@ import android.service.notification.StatusBarNotification; import android.util.Log; import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; -import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.HeadsUpManager; import javax.inject.Inject; import javax.inject.Singleton; -import dagger.Lazy; - /** Handles heads-up and pulsing behavior driven by notification changes. */ @Singleton public class NotificationAlertingManager { @@ -44,7 +42,7 @@ public class NotificationAlertingManager { private final NotificationRemoteInputManager mRemoteInputManager; private final VisualStabilityManager mVisualStabilityManager; - private final Lazy<ShadeController> mShadeController; + private final StatusBarStateController mStatusBarStateController; private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final NotificationListener mNotificationListener; @@ -55,12 +53,12 @@ public class NotificationAlertingManager { NotificationEntryManager notificationEntryManager, NotificationRemoteInputManager remoteInputManager, VisualStabilityManager visualStabilityManager, - Lazy<ShadeController> shadeController, + StatusBarStateController statusBarStateController, NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationListener notificationListener) { mRemoteInputManager = remoteInputManager; mVisualStabilityManager = visualStabilityManager; - mShadeController = shadeController; + mStatusBarStateController = statusBarStateController; mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; mNotificationListener = notificationListener; @@ -102,7 +100,7 @@ public class NotificationAlertingManager { // If it does and we no longer need to heads up, we should free the view. if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) { mHeadsUpManager.showNotification(entry); - if (!mShadeController.get().isDozing()) { + if (!mStatusBarStateController.isDozing()) { // Mark as seen immediately setNotificationShown(entry.getSbn()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java index b5c664116944..8ecf2b8421f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -24,7 +24,7 @@ import android.view.View; import com.android.systemui.DejankUtils; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; /** * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret, @@ -33,14 +33,14 @@ import com.android.systemui.statusbar.phone.ShadeController; public final class NotificationClicker implements View.OnClickListener { private static final String TAG = "NotificationClicker"; - private final ShadeController mShadeController; + private final StatusBar mStatusBar; private final BubbleController mBubbleController; private final NotificationActivityStarter mNotificationActivityStarter; - public NotificationClicker(ShadeController shadeController, + public NotificationClicker(StatusBar statusBar, BubbleController bubbleController, NotificationActivityStarter notificationActivityStarter) { - mShadeController = shadeController; + mStatusBar = statusBar; mBubbleController = bubbleController; mNotificationActivityStarter = notificationActivityStarter; } @@ -52,7 +52,7 @@ public final class NotificationClicker implements View.OnClickListener { return; } - mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK"); + mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK"); final ExpandableNotificationRow row = (ExpandableNotificationRow) v; final StatusBarNotification sbn = row.getEntry().getSbn(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index b61c1ef50cae..6c61923332ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -27,6 +27,7 @@ import android.service.notification.StatusBarNotification; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -45,6 +46,7 @@ public class NotificationFilter { private final NotificationGroupManager mGroupManager = Dependency.get( NotificationGroupManager.class); + private final StatusBarStateController mStatusBarStateController; private NotificationEntryManager.KeyguardEnvironment mEnvironment; private ShadeController mShadeController; @@ -52,7 +54,9 @@ public class NotificationFilter { private NotificationLockscreenUserManager mUserManager; @Inject - public NotificationFilter() {} + public NotificationFilter(StatusBarStateController statusBarStateController) { + mStatusBarStateController = statusBarStateController; + } private NotificationEntryManager.KeyguardEnvironment getEnvironment() { if (mEnvironment == null) { @@ -104,11 +108,11 @@ public class NotificationFilter { return true; } - if (getShadeController().isDozing() && entry.shouldSuppressAmbient()) { + if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) { return true; } - if (!getShadeController().isDozing() && entry.shouldSuppressNotificationList()) { + if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) { return true; } 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 35407c6df690..ed2fb0fdcb1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -2989,7 +2989,7 @@ public class NotificationPanelView extends PanelView implements return true; case StatusBarState.SHADE_LOCKED: if (!mQsExpanded) { - mShadeController.goToKeyguard(); + mStatusBarStateController.setState(StatusBarState.KEYGUARD); } return true; case StatusBarState.SHADE: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java index b31ce6afc281..deea3f17aad0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java @@ -14,11 +14,9 @@ package com.android.systemui.statusbar.phone; -import android.annotation.NonNull; import android.view.View; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; /** * {@link ShadeController} is an abstraction of the work that used to be hard-coded in @@ -30,14 +28,6 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi public interface ShadeController { /** - * Shows the keyguard bouncer - the password challenge on the lock screen - * - * @param scrimmed true when the bouncer should show scrimmed, false when the user will be - * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)} - */ - void showBouncer(boolean scrimmed); - - /** * Make our window larger and the panel expanded */ void instantExpandNotificationsPanel(); @@ -71,12 +61,6 @@ public interface ShadeController { void addPostCollapseAction(Runnable action); /** - * Ask shade controller to set the state to {@link StatusBarState#KEYGUARD}, but only from - * {@link StatusBarState#SHADE_LOCKED} - */ - void goToKeyguard(); - - /** * Notify the shade controller that the current user changed * * @param newUserId userId of the new user @@ -84,22 +68,6 @@ public interface ShadeController { void setLockscreenUser(int newUserId); /** - * Dozing is when the screen is in AOD or asleep - * - * @return true if we are dozing - */ - boolean isDozing(); - - /** - * Ask the display to wake up if currently dozing, else do nothing - * - * @param time when to wake up - * @param view the view requesting the wakeup - * @param why the reason for the wake up - */ - void wakeUpIfDozing(long time, View view, @NonNull String why); - - /** * If secure with redaction: Show bouncer, go to unlocked shade. * * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> @@ -109,11 +77,6 @@ public interface ShadeController { void goToLockedShade(View startingChild); /** - * Adds a {@param runnable} to be executed after Keyguard is gone. - */ - void addAfterKeyguardGoneRunnable(Runnable runnable); - - /** * Close the shade if it was open * * @return true if the shade was open, else false @@ -127,16 +90,4 @@ public interface ShadeController { * @param animate */ void collapsePanel(boolean animate); - - /** - * Callback to tell the shade controller that an activity launch animation was canceled - */ - void onLaunchAnimationCancelled(); - - /** - * Callback to notify the shade controller that a {@link ActivatableNotificationView} has become - * inactive - */ - void onActivationReset(); - } 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 5575d10c7fe1..709143d7275a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1239,6 +1239,7 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController, mNotificationAlertingManager, rowBinder, mKeyguardStateController, + mKeyguardIndicationController, this /* statusBar */, mCommandQueue); mNotificationListController = @@ -1282,17 +1283,13 @@ public class StatusBar extends SystemUI implements DemoMode, mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */); } - @Override - public void addAfterKeyguardGoneRunnable(Runnable runnable) { - mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); - } - - @Override - public boolean isDozing() { - return mDozing; - } - - @Override + /** + * Ask the display to wake up if currently dozing, else do nothing + * + * @param time when to wake up + * @param where the view requesting the wakeup + * @param why the reason for the wake up + */ public void wakeUpIfDozing(long time, View where, String why) { if (mDozing) { mPowerManager.wakeUp( @@ -1708,7 +1705,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { mEntryManager.updateNotifications("onHeadsUpStateChanged"); - if (isDozing() && isHeadsUp) { + if (mStatusBarStateController.isDozing() && isHeadsUp) { entry.setPulseSuppressed(false); mDozeServiceHost.fireNotificationPulse(entry); if (mDozeServiceHost.isPulsing()) { @@ -3453,16 +3450,11 @@ public class StatusBar extends SystemUI implements DemoMode, private void showBouncerIfKeyguard() { if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) && !mKeyguardViewMediator.isHiding()) { - showBouncer(true /* scrimmed */); + mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */); } } @Override - public void showBouncer(boolean scrimmed) { - mStatusBarKeyguardViewManager.showBouncer(scrimmed); - } - - @Override public void instantExpandNotificationsPanel() { // Make our window larger and the panel expanded. makeExpandedVisible(true); @@ -3583,10 +3575,6 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarKeyguardViewManager.isOccluded()); } - public void onActivationReset() { - mKeyguardIndicationController.hideTransientIndication(); - } - public void onTrackingStarted() { runPostCollapseRunnables(); } @@ -3628,7 +3616,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onTrackingStopped(boolean expand) { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { if (!expand && !mKeyguardStateController.canDismissLockScreen()) { - showBouncer(false /* scrimmed */); + mStatusBarKeyguardViewManager.showBouncer(false /* scrimmed */); } } } @@ -3688,15 +3676,6 @@ public class StatusBar extends SystemUI implements DemoMode, } /** - * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}. - */ - public void goToKeyguard() { - if (mState == StatusBarState.SHADE_LOCKED) { - mStatusBarStateController.setState(StatusBarState.KEYGUARD); - } - } - - /** * Propagation of the bouncer state, indicating that it's fully visible. */ public void setBouncerShowing(boolean bouncerShowing) { @@ -4019,7 +3998,8 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean shouldIgnoreTouch() { - return isDozing() && mDozeServiceHost.getIgnoreTouchWhilePulsing(); + return mStatusBarStateController.isDozing() + && mDozeServiceHost.getIgnoreTouchWhilePulsing(); } // Begin Extra BaseStatusBar methods. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 31d03621d23b..dac4e585c1ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -365,6 +365,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb cancelPendingWakeupAction(); } + /** + * Shows the keyguard bouncer - the password challenge on the lock screen + * + * @param scrimmed true when the bouncer should show scrimmed, false when the user will be + * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)} + */ public void showBouncer(boolean scrimmed) { if (mShowing && !mBouncer.isShowing()) { mBouncer.show(false /* resetSecuritySelection */, scrimmed); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index e2832587e2ea..1988b42be14f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -105,6 +105,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationPresenter mPresenter; private final LockPatternUtils mLockPatternUtils; private final HeadsUpManagerPhone mHeadsUpManager; + private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardManager mKeyguardManager; private final ActivityLaunchAnimator mActivityLaunchAnimator; private final IStatusBarService mBarService; @@ -122,7 +123,9 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit NotificationPresenter presenter, NotificationEntryManager entryManager, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, ActivityLaunchAnimator activityLaunchAnimator, IStatusBarService statusBarService, - StatusBarStateController statusBarStateController, KeyguardManager keyguardManager, + StatusBarStateController statusBarStateController, + StatusBarKeyguardViewManager statusBarKeyguardViewManager, + KeyguardManager keyguardManager, IDreamManager dreamManager, NotificationRemoteInputManager remoteInputManager, StatusBarRemoteInputCallback remoteInputCallback, NotificationGroupManager groupManager, NotificationLockscreenUserManager lockscreenUserManager, @@ -139,6 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mActivityLaunchAnimator = activityLaunchAnimator; mBarService = statusBarService; mCommandQueue = commandQueue; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; mRemoteInputManager = remoteInputManager; @@ -258,7 +262,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mShadeController.collapsePanel(true /* animate */); } else if (mKeyguardStateController.isShowing() && mStatusBar.isOccluded()) { - mShadeController.addAfterKeyguardGoneRunnable(runnable); + mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); mShadeController.collapsePanel(); } else { mBackgroundHandler.postAtFrontOfQueue(runnable); @@ -504,6 +508,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final ActivityStarter mActivityStarter; private final IStatusBarService mStatusBarService; private final StatusBarStateController mStatusBarStateController; + private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardManager mKeyguardManager; private final IDreamManager mDreamManager; private final NotificationRemoteInputManager mRemoteInputManager; @@ -533,6 +538,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit ActivityStarter activityStarter, IStatusBarService statusBarService, StatusBarStateController statusBarStateController, + StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, NotificationRemoteInputManager remoteInputManager, @@ -556,6 +562,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mActivityStarter = activityStarter; mStatusBarService = statusBarService; mStatusBarStateController = statusBarStateController; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; mRemoteInputManager = remoteInputManager; @@ -601,6 +608,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mActivityLaunchAnimator, mStatusBarService, mStatusBarStateController, + mStatusBarKeyguardViewManager, mKeyguardManager, mDreamManager, mRemoteInputManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 6650cf6a5af6..2649166bfcbf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -49,6 +49,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; @@ -113,6 +114,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, private final DozeScrimController mDozeScrimController; private final ScrimController mScrimController; private final Context mContext; + private final KeyguardIndicationController mKeyguardIndicationController; private final StatusBar mStatusBar; private final CommandQueue mCommandQueue; @@ -141,6 +143,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, NotificationAlertingManager notificationAlertingManager, NotificationRowBinderImpl notificationRowBinder, KeyguardStateController keyguardStateController, + KeyguardIndicationController keyguardIndicationController, StatusBar statusBar, CommandQueue commandQueue) { mContext = context; @@ -148,6 +151,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mNotificationPanel = panel; mHeadsUpManager = headsUp; mDynamicPrivacyController = dynamicPrivacyController; + mKeyguardIndicationController = keyguardIndicationController; // TODO: use KeyguardStateController#isOccluded to remove this dependency mStatusBar = statusBar; mCommandQueue = commandQueue; @@ -323,7 +327,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mCommandQueue.animateCollapsePanels(); } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED && !isCollapsing()) { - mShadeController.goToKeyguard(); + mStatusBarStateController.setState(StatusBarState.KEYGUARD); } } } @@ -420,7 +424,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, public void onActivationReset(ActivatableNotificationView view) { if (view == mNotificationPanel.getActivatedChild()) { mNotificationPanel.setActivatedChild(null); - mShadeController.onActivationReset(); + mKeyguardIndicationController.hideTransientIndication(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 3e6ba4dacb71..2012b5703504 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -64,6 +64,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, private final ActivityStarter mActivityStarter; private final Lazy<ShadeController> mShadeControllerLazy; private final Context mContext; + private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final ActivityIntentHelper mActivityIntentHelper; private final NotificationGroupManager mGroupManager; private View mPendingWorkRemoteInputView; @@ -81,9 +82,11 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, NotificationLockscreenUserManager notificationLockscreenUserManager, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, + StatusBarKeyguardViewManager statusBarKeyguardViewManager, ActivityStarter activityStarter, Lazy<ShadeController> shadeControllerLazy, CommandQueue commandQueue) { mContext = context; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL, new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null); mLockscreenUserManager = notificationLockscreenUserManager; @@ -118,7 +121,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, if (!row.isPinned()) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); } - mShadeControllerLazy.get().showBouncer(true /* scrimmed */); + mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */); mPendingRemoteInputView = clicked; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java index 3935df02fd91..f8929e01adb8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java @@ -263,7 +263,7 @@ public class StatusBarWindowViewController { if (isDown) { mStackScrollLayout.closeControlsIfOutsideTouch(ev); } - if (mService.isDozing()) { + if (mStatusBarStateController.isDozing()) { mService.mDozeScrimController.extendPulse(); } // In case we start outside of the view bounds (below the status bar), we need to @@ -284,7 +284,8 @@ public class StatusBarWindowViewController { @Override public boolean shouldInterceptTouchEvent(MotionEvent ev) { - if (mService.isDozing() && !mService.isPulsing() && !mDockManager.isDocked()) { + if (mStatusBarStateController.isDozing() && !mService.isPulsing() + && !mDockManager.isDocked()) { // Capture all touch events in always-on. return true; } @@ -292,7 +293,7 @@ public class StatusBarWindowViewController { if (notificationPanelView.isFullyExpanded() && mDragDownHelper.isDragDownEnabled() && !mService.isBouncerShowing() - && !mService.isDozing()) { + && !mStatusBarStateController.isDozing()) { intercept = mDragDownHelper.onInterceptTouchEvent(ev); } @@ -312,7 +313,7 @@ public class StatusBarWindowViewController { @Override public boolean handleTouchEvent(MotionEvent ev) { boolean handled = false; - if (mService.isDozing()) { + if (mStatusBarStateController.isDozing()) { handled = !mService.isPulsing(); } if ((mDragDownHelper.isDragDownEnabled() && !handled) @@ -358,7 +359,7 @@ public class StatusBarWindowViewController { break; case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: - if (mService.isDozing()) { + if (mStatusBarStateController.isDozing()) { MediaSessionLegacyHelper.getHelper(mView.getContext()) .sendVolumeKeyEvent( event, AudioManager.USE_DEFAULT_STREAM_TYPE, true); diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java index 2d6c4a62047d..ac15e2dd6df9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java @@ -62,11 +62,4 @@ public interface DelayableExecutor extends Executor { * @return A Runnable that, when run, removes the supplied argument from the Executor queue. */ Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit); - - /** - * Remove all pending Runnables. - * - */ - void removeAll(); - } diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java index 2a99de3ac5ad..7e7732135e3a 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java @@ -50,9 +50,4 @@ public class ExecutorImpl extends HandlerExecutor implements DelayableExecutor { return () -> mHandler.removeCallbacksAndMessages(token); } - - @Override - public void removeAll() { - mHandler.removeCallbacksAndMessages(null); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 4c707f45efc1..ae43aa2f4118 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -82,6 +82,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.InjectionInflationController; @@ -153,6 +154,8 @@ public class BubbleControllerTest extends SysuiTestCase { private Resources mResources; @Mock private Lazy<ShadeController> mShadeController; + @Mock + private RemoteInputUriController mRemoteInputUriController; private SuperStatusBarViewFactory mSuperStatusBarViewFactory; private BubbleData mBubbleData; @@ -212,7 +215,8 @@ public class BubbleControllerTest extends SysuiTestCase { mZenModeController, mLockscreenUserManager, mNotificationGroupManager, - mNotificationEntryManager); + mNotificationEntryManager, + mRemoteInputUriController); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); mBubbleController.setExpandListener(mBubbleExpandListener); @@ -708,11 +712,13 @@ public class BubbleControllerTest extends SysuiTestCase { ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, NotificationGroupManager groupManager, - NotificationEntryManager entryManager) { + NotificationEntryManager entryManager, + RemoteInputUriController remoteInputUriController) { super(context, statusBarWindowController, statusBarStateController, shadeController, data, Runnable::run, configurationController, interruptionStateProvider, - zenModeController, lockscreenUserManager, groupManager, entryManager); + zenModeController, lockscreenUserManager, groupManager, entryManager, + remoteInputUriController); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index a754a00df940..8aac1891a5e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -30,7 +30,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyH import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.google.android.collect.Sets; @@ -76,7 +76,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext, mLockscreenUserManager, mSmartReplyController, mEntryManager, - () -> mock(ShadeController.class), + () -> mock(StatusBar.class), mStateController, Handler.createAsync(Looper.myLooper()), mRemoteInputUriController); @@ -212,12 +212,12 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { NotificationLockscreenUserManager lockscreenUserManager, SmartReplyController smartReplyController, NotificationEntryManager notificationEntryManager, - Lazy<ShadeController> shadeController, + Lazy<StatusBar> statusBarLazy, StatusBarStateController statusBarStateController, Handler mainHandler, RemoteInputUriController remoteInputUriController) { super(context, lockscreenUserManager, smartReplyController, notificationEntryManager, - shadeController, statusBarStateController, mainHandler, + statusBarLazy, statusBarStateController, mainHandler, remoteInputUriController); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java index 95ce53c58e95..3f624128ac2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java @@ -41,7 +41,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.RemoteInputUriController; import org.junit.Before; @@ -88,7 +88,7 @@ public class SmartReplyControllerTest extends SysuiTestCase { mRemoteInputManager = new NotificationRemoteInputManager(mContext, mock(NotificationLockscreenUserManager.class), mSmartReplyController, - mNotificationEntryManager, () -> mock(ShadeController.class), + mNotificationEntryManager, () -> mock(StatusBar.class), mStatusBarStateController, Handler.createAsync(Looper.myLooper()), mRemoteInputUriController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java index 68730d12dee3..7fabb0fee8e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java @@ -100,7 +100,7 @@ public class NotificationFilterTest extends SysuiTestCase { when(mEnvironment.isDeviceProvisioned()).thenReturn(true); when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); mRow = new NotificationTestHelper(getContext(), mDependency).createRow(); - mNotificationFilter = new NotificationFilter(); + mNotificationFilter = new NotificationFilter(mock(StatusBarStateController.class)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 61e5058b1a20..b27e84a37e3f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -126,7 +126,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test public void showBouncer_onlyWhenShowing() { mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */); - mStatusBar.showBouncer(true /* scrimmed */); + mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */); verify(mBouncer, never()).show(anyBoolean(), anyBoolean()); verify(mBouncer, never()).show(anyBoolean()); } @@ -135,7 +135,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { public void showBouncer_notWhenBouncerAlreadyShowing() { mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */); when(mBouncer.isSecure()).thenReturn(true); - mStatusBar.showBouncer(true /* scrimmed */); + mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */); verify(mBouncer, never()).show(anyBoolean(), anyBoolean()); verify(mBouncer, never()).show(anyBoolean()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 77cdc025bbc9..d7c00cf3038d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -98,6 +98,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; @Mock + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; @@ -167,7 +169,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { getContext(), mock(CommandQueue.class), () -> mAssistManager, mEntryManager, mock(HeadsUpManagerPhone.class), mActivityStarter, mStatusBarService, - mock(StatusBarStateController.class), mock(KeyguardManager.class), + mock(StatusBarStateController.class), mStatusBarKeyguardViewManager, + mock(KeyguardManager.class), mock(IDreamManager.class), mRemoteInputManager, mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class), mock(NotificationLockscreenUserManager.class), @@ -186,7 +189,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg doAnswer(answerVoid(Runnable::run)) - .when(mStatusBar).addAfterKeyguardGoneRunnable(any(Runnable.class)); + .when(mStatusBarKeyguardViewManager) + .addAfterKeyguardGoneRunnable(any(Runnable.class)); // set up addPostCollapseAction to synchronously invoke the Runnable arg doAnswer(answerVoid(Runnable::run)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 14f5795c39f1..fb6e1684c376 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -39,6 +39,7 @@ import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; @@ -113,6 +114,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class), mock(NotificationAlertingManager.class), mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class), + mock(KeyguardIndicationController.class), mStatusBar, mCommandQueue); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java index 9d76b425725f..6dfd0828de9a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java @@ -54,6 +54,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; @Mock private KeyguardStateController mKeyguardStateController; @Mock private SysuiStatusBarStateController mStatusBarStateController; + @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private ActivityStarter mActivityStarter; private int mCurrentUserId = 0; @@ -71,8 +72,8 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext, mock(NotificationGroupManager.class), mNotificationLockscreenUserManager, - mKeyguardStateController, mStatusBarStateController, mActivityStarter, - () -> mShadeController, new CommandQueue(mContext))); + mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager, + mActivityStarter, () -> mShadeController, new CommandQueue(mContext))); mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java index 529333effa2c..00ea18749de3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java @@ -80,7 +80,7 @@ public class StatusBarWindowViewTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mView = new StatusBarWindowView(getContext(), null); - when(mStatusBar.isDozing()).thenReturn(false); + when(mStatusBarStateController.isDozing()).thenReturn(false); mDependency.injectTestDependency(ShadeController.class, mShadeController); when(mDockManager.isDocked()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java index 3ed6c5b2b11b..f3c053058468 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java @@ -168,11 +168,6 @@ public class FakeExecutor implements DelayableExecutor { executeDelayed(command, 0); } - @Override - public void removeAll() { - mQueuedRunnables.clear(); - } - /** * Run all Executors in a loop until they all report they have no ready work to do. * diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java index 7fd694244afa..b1716ff47129 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java @@ -325,36 +325,6 @@ public class FakeExecutorTest extends SysuiTestCase { assertEquals(1, runnable.mRunCount); } - /** - * Test removing everything - */ - @Test - public void testRemoval_all() { - FakeSystemClock clock = new FakeSystemClock(); - clock.setAutoIncrement(false); - FakeExecutor fakeExecutor = new FakeExecutor(clock); - RunnableImpl runnable = new RunnableImpl(); - - // Nothing to remove. - assertEquals(0, runnable.mRunCount); - assertEquals(0, fakeExecutor.numPending()); - - // Two pending items that have not yet run. - fakeExecutor.executeDelayed(runnable, 100); - fakeExecutor.executeDelayed(runnable, 200); - assertEquals(2, fakeExecutor.numPending()); - assertEquals(0, runnable.mRunCount); - - // Remove the items. - fakeExecutor.removeAll(); - - // Nothing to run - fakeExecutor.advanceClockToLast(); - assertEquals(0, fakeExecutor.runAllReady()); - assertEquals(0, fakeExecutor.numPending()); - assertEquals(0, runnable.mRunCount); - } - private static class RunnableImpl implements Runnable { int mRunCount; diff --git a/packages/Tethering/apex/manifest.json b/packages/Tethering/apex/manifest.json index 3fb62f3405a3..078302ac51a4 100644 --- a/packages/Tethering/apex/manifest.json +++ b/packages/Tethering/apex/manifest.json @@ -1,4 +1,4 @@ { "name": "com.android.tethering.apex", - "version": 290000000 + "version": 300000000 } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index a3188443591f..119b987f7ae7 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -614,22 +614,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean isEnabled() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG, "isEnabled(): not allowed for non-active and non system user"); - return false; - } - - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - return mBluetooth.isEnabled(); - } - } catch (RemoteException e) { - Slog.e(TAG, "isEnabled()", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - return false; + return getState() == BluetoothAdapter.STATE_ON; } public int getState() { @@ -1796,14 +1781,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { - if (!mQuietEnable) { - if (!mBluetooth.enable()) { - Slog.e(TAG, "IBluetooth.enable() returned false"); - } - } else { - if (!mBluetooth.enableNoAutoConnect()) { - Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false"); - } + if (!mBluetooth.enable(mQuietEnable)) { + Slog.e(TAG, "IBluetooth.enable() returned false"); } } catch (RemoteException e) { Slog.e(TAG, "Unable to call enable()", e); @@ -2078,14 +2057,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (mBluetooth != null) { //Enable bluetooth try { - if (!mQuietEnable) { - if (!mBluetooth.enable()) { - Slog.e(TAG, "IBluetooth.enable() returned false"); - } - } else { - if (!mBluetooth.enableNoAutoConnect()) { - Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false"); - } + if (!mBluetooth.enable(mQuietEnable)) { + Slog.e(TAG, "IBluetooth.enable() returned false"); } } catch (RemoteException e) { Slog.e(TAG, "Unable to call enable()", e); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index f3089c1092e6..54dfc98e888d 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -81,6 +81,7 @@ import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; @@ -339,7 +340,7 @@ public class LocationManagerService extends ILocationManager.Stub { mSettingsStore.addOnLocationEnabledChangedListener((userId) -> { synchronized (mLock) { - onLocationModeChangedLocked(userId, true); + onLocationModeChangedLocked(userId); } }); mSettingsStore.addOnLocationProvidersAllowedChangedListener((userId) -> { @@ -467,36 +468,24 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void onLocationModeChangedLocked(int userId, boolean broadcast) { - if (!isCurrentProfileLocked(userId)) { - return; - } - + private void onLocationModeChangedLocked(int userId) { if (D) { - Log.d(TAG, "location enabled is now " + isLocationEnabled()); + Log.d(TAG, "[u" + userId + "] location enabled = " + isLocationEnabledForUser(userId)); } - for (LocationProvider p : mProviders) { - p.onLocationModeChangedLocked(); - } + Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION); + intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabled()); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); - if (broadcast) { - // needs to be sent to everyone because we don't know which user may have changed - // LOCATION_MODE state. - mContext.sendBroadcastAsUser( - new Intent(LocationManager.MODE_CHANGED_ACTION), - UserHandle.ALL); + for (LocationProvider p : mProviders) { + p.onLocationModeChangedLocked(userId); } } @GuardedBy("mLock") private void onProviderAllowedChangedLocked(int userId) { - if (!isCurrentProfileLocked(userId)) { - return; - } - for (LocationProvider p : mProviders) { - p.onAllowedChangedLocked(); + p.onAllowedChangedLocked(userId); } } @@ -798,21 +787,14 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "foreground user is changing to " + userId); } - // let providers know the current user is on the way out before changing the user - for (LocationProvider p : mProviders) { - p.onUserChangingLocked(); - } - + int oldUserId = userId; mCurrentUserId = userId; onUserProfilesChangedLocked(); - // if the user changes, per-user settings may also have changed - onLocationModeChangedLocked(userId, false); - onProviderAllowedChangedLocked(userId); - - // always force useability to be rechecked, even if no per-user settings have changed + // let providers know the current user has changed for (LocationProvider p : mProviders) { - p.onUseableChangedLocked(false); + p.onCurrentUserChangedLocked(oldUserId); + p.onCurrentUserChangedLocked(mCurrentUserId); } } @@ -832,7 +814,7 @@ public class LocationManagerService extends ILocationManager.Stub { protected AbstractLocationProvider mProvider; @GuardedBy("mLock") - private boolean mUseable; // combined state + private SparseArray<Boolean> mUseable; // combined state for each user id @GuardedBy("mLock") private boolean mAllowed; // state of LOCATION_PROVIDERS_ALLOWED @GuardedBy("mLock") @@ -851,7 +833,7 @@ public class LocationManagerService extends ILocationManager.Stub { mIsManagedBySettings = isManagedBySettings; mProvider = null; - mUseable = false; + mUseable = new SparseArray<>(1); mAllowed = !mIsManagedBySettings; mEnabled = false; mProperties = null; @@ -876,7 +858,10 @@ public class LocationManagerService extends ILocationManager.Stub { } mProvider = provider; - onUseableChangedLocked(false); + + // it would be more correct to call this for all users, but we know this can only + // affect the current user since providers are disabled for non-current users + onUseableChangedLocked(false, mCurrentUserId); } public String getName() { @@ -943,8 +928,8 @@ public class LocationManagerService extends ILocationManager.Stub { pw.increaseIndent(); - pw.println("useable=" + mUseable); - if (!mUseable) { + pw.println("useable=" + isUseableLocked(mCurrentUserId)); + if (!isUseableLocked(mCurrentUserId)) { pw.println("attached=" + (mProvider != null)); if (mIsManagedBySettings) { pw.println("allowed=" + mAllowed); @@ -1009,7 +994,10 @@ public class LocationManagerService extends ILocationManager.Stub { } mEnabled = enabled; - onUseableChangedLocked(false); + + // it would be more correct to call this for all users, but we know this can only + // affect the current user since providers are disabled for non-current users + onUseableChangedLocked(false, mCurrentUserId); } } @@ -1021,12 +1009,20 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - public void onLocationModeChangedLocked() { - onUseableChangedLocked(false); + public void onLocationModeChangedLocked(int userId) { + if (!isCurrentProfileLocked(userId)) { + return; + } + + onUseableChangedLocked(false, userId); } @GuardedBy("mLock") - public void onAllowedChangedLocked() { + public void onAllowedChangedLocked(int userId) { + if (!isCurrentProfileLocked(userId)) { + return; + } + if (mIsManagedBySettings) { boolean allowed = mSettingsStore.getLocationProvidersAllowed( mCurrentUserId).contains(mName); @@ -1040,37 +1036,36 @@ public class LocationManagerService extends ILocationManager.Stub { } mAllowed = allowed; - onUseableChangedLocked(true); + onUseableChangedLocked(true, userId); } } @GuardedBy("mLock") - public boolean isUseableLocked() { - return isUseableForUserLocked(mCurrentUserId); + public void onCurrentUserChangedLocked(int userId) { + onUseableChangedLocked(false, userId); } @GuardedBy("mLock") - public boolean isUseableForUserLocked(int userId) { - return isCurrentProfileLocked(userId) && mUseable; + public boolean isUseableLocked() { + return isUseableLocked(mCurrentUserId); } @GuardedBy("mLock") - private boolean isUseableIgnoringAllowedLocked() { - return mProvider != null && mProviders.contains(this) && isLocationEnabled() - && mEnabled; + public boolean isUseableLocked(int userId) { + return mUseable.get(userId, Boolean.FALSE); } @GuardedBy("mLock") - public void onUseableChangedLocked(boolean isAllowedChanged) { + public void onUseableChangedLocked(boolean isAllowedChanged, int userId) { // if any property that contributes to "useability" here changes state, it MUST result // in a direct or indrect call to onUseableChangedLocked. this allows the provider to // guarantee that it will always eventually reach the correct state. - boolean useableIgnoringAllowed = isUseableIgnoringAllowedLocked(); + boolean useableIgnoringAllowed = mProvider != null && mProviders.contains(this) + && isCurrentProfileLocked(userId) && isLocationEnabledForUser(userId) + && mEnabled; boolean useable = useableIgnoringAllowed && mAllowed; - // update deprecated provider allowed settings for backwards compatibility, and do this - // even if there is no change in overall useability state. this may result in trying to - // overwrite the same value, but Settings handles deduping this. + // update deprecated provider allowed settings for backwards compatibility if (mIsManagedBySettings) { // a "-" change derived from the allowed setting should not be overwritten, but a // "+" change should be corrected if necessary @@ -1079,33 +1074,31 @@ public class LocationManagerService extends ILocationManager.Stub { mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+" + mName, - mCurrentUserId); + userId); } else if (!useableIgnoringAllowed) { Settings.Secure.putStringForUser( mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-" + mName, - mCurrentUserId); + userId); } - // needs to be sent to all users because whether or not a provider is enabled for - // a given user is complicated... we broadcast to everyone and let them figure it - // out via isProviderEnabled() Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION); intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + intent.putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); } - if (useable == mUseable) { + if (useable == isUseableLocked(userId)) { return; } - mUseable = useable; + mUseable.put(userId, useable); if (D) { - Log.d(TAG, mName + " provider useable is now " + mUseable); + Log.d(TAG, "[u" + userId + "] " + mName + " provider useable = " + useable); } - if (!mUseable) { + if (!useable) { // If any provider has been disabled, clear all last locations for all // providers. This is to be on the safe side in case a provider has location // derived from this disabled provider. @@ -1115,15 +1108,6 @@ public class LocationManagerService extends ILocationManager.Stub { updateProviderUseableLocked(this); } - - @GuardedBy("mLock") - public void onUserChangingLocked() { - // when the user is about to change, we set this provider to un-useable, and notify all - // of the current user clients. when the user is finished changing, useability will be - // updated back via onLocationModeChanged() and onAllowedChanged(). - mUseable = false; - updateProviderUseableLocked(this); - } } private class MockLocationProvider extends LocationProvider { @@ -1557,14 +1541,20 @@ public class LocationManagerService extends ILocationManager.Stub { mProviders.add(provider); - provider.onAllowedChangedLocked(); // allowed state may change while provider was inactive - provider.onUseableChangedLocked(false); + // allowed state may change while provider was inactive + provider.onAllowedChangedLocked(mCurrentUserId); + + // it would be more correct to call this for all users, but we know this can only + // affect the current user since providers are disabled for non-current users + provider.onUseableChangedLocked(false, mCurrentUserId); } @GuardedBy("mLock") private void removeProviderLocked(LocationProvider provider) { if (mProviders.remove(provider)) { - provider.onUseableChangedLocked(false); + // it would be more correct to call this for all users, but we know this can only + // affect the current user since providers are disabled for non-current users + provider.onUseableChangedLocked(false, mCurrentUserId); } } @@ -2851,7 +2841,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { LocationProvider provider = getLocationProviderLocked(providerName); - return provider != null && provider.isUseableForUserLocked(userId); + return provider != null && provider.isUseableLocked(userId); } } @@ -3246,6 +3236,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) { mGnssManagerService.dump(fd, pw, args); + return; } ipw.println("Location Manager State:"); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index e79a2897d86a..840b7af19890 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.OBSERVE_NETWORK_POLICY; import static android.Manifest.permission.SHUTDOWN; @@ -737,7 +738,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { // @Override public String[] listInterfaces() { - NetworkStack.checkNetworkStackPermission(mContext); + // TODO: Remove CONNECTIVITY_INTERNAL after bluetooth tethering has no longer called these + // APIs. + NetworkStack.checkNetworkStackPermissionOr(mContext, CONNECTIVITY_INTERNAL); try { return mNetdService.interfaceGetList(); } catch (RemoteException | ServiceSpecificException e) { @@ -787,7 +790,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public InterfaceConfiguration getInterfaceConfig(String iface) { - NetworkStack.checkNetworkStackPermission(mContext); + // TODO: Remove CONNECTIVITY_INTERNAL after bluetooth tethering has no longer called these + // APIs. + NetworkStack.checkNetworkStackPermissionOr(mContext, CONNECTIVITY_INTERNAL); final InterfaceConfigurationParcel result; try { result = mNetdService.interfaceGetCfg(iface); @@ -805,7 +810,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) { - NetworkStack.checkNetworkStackPermission(mContext); + // TODO: Remove CONNECTIVITY_INTERNAL after bluetooth tethering has no longer called these + // APIs. + NetworkStack.checkNetworkStackPermissionOr(mContext, CONNECTIVITY_INTERNAL); LinkAddress linkAddr = cfg.getLinkAddress(); if (linkAddr == null || linkAddr.getAddress() == null) { throw new IllegalStateException("Null LinkAddress given"); diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 12debbff3e90..521b39305d9d 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -57,6 +57,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; @@ -81,6 +82,22 @@ public class PackageWatchdog { static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED = "watchdog_explicit_health_check_enabled"; + public static final int FAILURE_REASON_UNKNOWN = 0; + public static final int FAILURE_REASON_NATIVE_CRASH = 1; + public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2; + public static final int FAILURE_REASON_APP_CRASH = 3; + public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4; + + @IntDef(prefix = { "FAILURE_REASON_" }, value = { + FAILURE_REASON_UNKNOWN, + FAILURE_REASON_NATIVE_CRASH, + FAILURE_REASON_EXPLICIT_HEALTH_CHECK, + FAILURE_REASON_APP_CRASH, + FAILURE_REASON_APP_NOT_RESPONDING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FailureReasons {} + // Duration to count package failures before it resets to 0 @VisibleForTesting static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS = @@ -305,14 +322,15 @@ public class PackageWatchdog { } /** - * Called when a process fails either due to a crash or ANR. + * Called when a process fails due to a crash, ANR or explicit health check. * * <p>For each package contained in the process, one registered observer with the least user * impact will be notified for mitigation. * * <p>This method could be called frequently if there is a severe problem on the device. */ - public void onPackageFailure(List<VersionedPackage> packages) { + public void onPackageFailure(List<VersionedPackage> packages, + @FailureReasons int failureReason) { mLongTaskHandler.post(() -> { synchronized (mLock) { if (mAllObservers.isEmpty()) { @@ -343,7 +361,7 @@ public class PackageWatchdog { // Execute action with least user impact if (currentObserverToNotify != null) { - currentObserverToNotify.execute(versionedPackage); + currentObserverToNotify.execute(versionedPackage, failureReason); } } } @@ -414,7 +432,7 @@ public class PackageWatchdog { * * @return {@code true} if action was executed successfully, {@code false} otherwise */ - boolean execute(VersionedPackage versionedPackage); + boolean execute(VersionedPackage versionedPackage, @FailureReasons int failureReason); // TODO(b/120598832): Ensure uniqueness? /** @@ -659,7 +677,8 @@ public class PackageWatchdog { while (it.hasNext()) { VersionedPackage versionedPkg = it.next().mPackage; Slog.i(TAG, "Explicit health check failed for package " + versionedPkg); - registeredObserver.execute(versionedPkg); + registeredObserver.execute(versionedPkg, + PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); } } } @@ -770,7 +789,7 @@ public class PackageWatchdog { final List<VersionedPackage> pkgList = Collections.singletonList(pkg); final long failureCount = getTriggerFailureCount(); for (int i = 0; i < failureCount; i++) { - onPackageFailure(pkgList); + onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK); } }); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 1f7d9eab9675..6105c7422153 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -72,7 +72,6 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.ITelephonyRegistry; -import com.android.internal.telephony.PhoneConstantConversions; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyPermissions; @@ -2215,8 +2214,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); - intent.putExtra(PhoneConstants.STATE_KEY, - PhoneConstantConversions.convertCallState(state).toString()); + intent.putExtra(TelephonyManager.EXTRA_STATE, callStateToString(state)); // If a valid subId was specified, we should fire off a subId-specific state // change intent and include the subId. @@ -2249,6 +2247,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_CALL_LOG}); } + /** Converts TelephonyManager#CALL_STATE_* to TelephonyManager#EXTRA_STATE_*. */ + private static String callStateToString(int callState) { + switch (callState) { + case TelephonyManager.CALL_STATE_RINGING: + return TelephonyManager.EXTRA_STATE_RINGING; + case TelephonyManager.CALL_STATE_OFFHOOK: + return TelephonyManager.EXTRA_STATE_OFFHOOK; + default: + return TelephonyManager.EXTRA_STATE_IDLE; + } + } + private void broadcastDataConnectionStateChanged(int state, boolean isDataAllowed, String apn, String apnType, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, @@ -2257,8 +2267,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // status bar takes care of that after taking into account all of the // required info. Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); - intent.putExtra(PhoneConstants.STATE_KEY, - PhoneConstantConversions.convertDataState(state).toString()); + intent.putExtra(TelephonyManager.EXTRA_STATE, dataStateToString(state)); if (!isDataAllowed) { intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true); } @@ -2301,7 +2310,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { String apnType, String apn, LinkProperties linkProperties, @DataFailureCause int failCause) { Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED); - intent.putExtra(PhoneConstants.STATE_KEY, state); + intent.putExtra(TelephonyManager.EXTRA_STATE, state); intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType); if (apnType != null) intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType); if (apn != null) intent.putExtra(PhoneConstants.DATA_APN_KEY, apn); @@ -2642,11 +2651,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } /** - * Convert data state to string + * Convert TelephonyManager.DATA_* to string. * * @return The data state in string format. */ - private String dataStateToString(@TelephonyManager.DataState int state) { + private static String dataStateToString(int state) { switch (state) { case TelephonyManager.DATA_DISCONNECTED: return "DISCONNECTED"; case TelephonyManager.DATA_CONNECTING: return "CONNECTING"; diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index b5702541432a..b1e2c0fe9586 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -866,14 +866,13 @@ final class UiModeManagerService extends SystemService { if (!mHoldingConfiguration) { mConfiguration.uiMode = uiMode; } - // load splash screen instead of screenshot - mWindowManager.clearSnapshotCache(); } private void sendConfigurationLocked() { if (mSetUiMode != mConfiguration.uiMode) { mSetUiMode = mConfiguration.uiMode; - + // load splash screen instead of screenshot + mWindowManager.clearSnapshotCache(); try { ActivityTaskManager.getService().updateConfiguration(mConfiguration); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/ZramWriteback.java b/services/core/java/com/android/server/ZramWriteback.java index 49bf29bff284..5d97def129a4 100644 --- a/services/core/java/com/android/server/ZramWriteback.java +++ b/services/core/java/com/android/server/ZramWriteback.java @@ -60,6 +60,7 @@ public final class ZramWriteback extends JobService { private static final String MARK_IDLE_DELAY_PROP = "ro.zram.mark_idle_delay_mins"; private static final String FIRST_WB_DELAY_PROP = "ro.zram.first_wb_delay_mins"; private static final String PERIODIC_WB_DELAY_PROP = "ro.zram.periodic_wb_delay_hours"; + private static final String FORCE_WRITEBACK_PROP = "zram.force_writeback"; private void markPagesAsIdle() { String idlePath = String.format(IDLE_SYS, sZramDeviceId); @@ -122,11 +123,12 @@ public final class ZramWriteback extends JobService { private static void schedNextWriteback(Context context) { int nextWbDelay = SystemProperties.getInt(PERIODIC_WB_DELAY_PROP, 24); + boolean forceWb = SystemProperties.getBoolean(FORCE_WRITEBACK_PROP, false); JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback) .setMinimumLatency(TimeUnit.HOURS.toMillis(nextWbDelay)) - .setRequiresDeviceIdle(true) + .setRequiresDeviceIdle(!forceWb) .build()); } @@ -167,6 +169,7 @@ public final class ZramWriteback extends JobService { public static void scheduleZramWriteback(Context context) { int markIdleDelay = SystemProperties.getInt(MARK_IDLE_DELAY_PROP, 20); int firstWbDelay = SystemProperties.getInt(FIRST_WB_DELAY_PROP, 180); + boolean forceWb = SystemProperties.getBoolean(FORCE_WRITEBACK_PROP, false); JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); @@ -182,7 +185,7 @@ public final class ZramWriteback extends JobService { // by ro.zram.periodic_wb_delay_hours. js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback) .setMinimumLatency(TimeUnit.MINUTES.toMillis(firstWbDelay)) - .setRequiresDeviceIdle(true) + .setRequiresDeviceIdle(!forceWb) .build()); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b191338dc58c..01f3c2666ff7 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6694,7 +6694,7 @@ public class ActivityManagerService extends IActivityManager.Stub private final long[] mProcessStateStatsLongs = new long[1]; - private boolean isProcessAliveLocked(ProcessRecord proc) { + boolean isProcessAliveLocked(ProcessRecord proc) { if (proc.pid <= 0) { if (DEBUG_OOM_ADJ) Slog.d(TAG, "Process hasn't started yet: " + proc); return false; @@ -6711,7 +6711,10 @@ public class ActivityManagerService extends IActivityManager.Stub final long state = mProcessStateStatsLongs[0]; if (DEBUG_OOM_ADJ) Slog.d(TAG, "RETRIEVED STATE FOR " + proc.procStatFile + ": " + (char)state); - return state != 'Z' && state != 'X' && state != 'x' && state != 'K'; + if (state != 'Z' && state != 'X' && state != 'x' && state != 'K') { + return Process.getUidForPid(proc.pid) == proc.uid; + } + return false; } private String checkContentProviderAssociation(ProcessRecord callingApp, int callingUid, @@ -6784,14 +6787,14 @@ public class ActivityManagerService extends IActivityManager.Stub // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see // how to test this case.) if (cpr.proc.killed && cpr.proc.killedByAm) { - checkTime(startTime, "getContentProviderImpl: before appDied (killedByAm)"); final long iden = Binder.clearCallingIdentity(); try { - appDiedLocked(cpr.proc); + mProcessList.killProcAndWaitIfNecessaryLocked(cpr.proc, false, + cpr.uid == cpr.proc.uid || cpr.proc.isolated, + "getContentProviderImpl: %s (killedByAm)", startTime); } finally { Binder.restoreCallingIdentity(iden); } - checkTime(startTime, "getContentProviderImpl: after appDied (killedByAm)"); } } @@ -6895,9 +6898,8 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString() + " is crashing; detaching " + r); boolean lastRef = decProviderCountLocked(conn, cpr, token, stable); - checkTime(startTime, "getContentProviderImpl: before appDied"); - appDiedLocked(cpr.proc); - checkTime(startTime, "getContentProviderImpl: after appDied"); + mProcessList.killProcAndWaitIfNecessaryLocked(cpr.proc, + false, true, "getContentProviderImpl: %s", startTime); if (!lastRef) { // This wasn't the last ref our process had on // the provider... we have now been killed, bail. diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 51e1718d4c98..83a7341923fa 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -446,7 +446,8 @@ class AppErrors { RescueParty.noteAppCrash(mContext, r.uid); } - mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode()); + mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(), + PackageWatchdog.FAILURE_REASON_APP_CRASH); } final int relaunchReason = r != null @@ -900,7 +901,8 @@ class AppErrors { } // Notify PackageWatchdog without the lock held if (packageList != null) { - mPackageWatchdog.onPackageFailure(packageList); + mPackageWatchdog.onPackageFailure(packageList, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); } } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 38030c248139..cf996a50d5c7 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -125,7 +125,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { // Keep the last WiFi stats so we can compute a delta. @GuardedBy("mWorkerLock") private WifiActivityEnergyInfo mLastInfo = - new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0, 0); + new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0); /** * Timestamp at which all external stats were last collected in diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java index 183e05979c5e..ebfc2a011e88 100644 --- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java @@ -66,9 +66,7 @@ final class CarUserSwitchingDialog extends UserSwitchingDialog { setCancelable(false); Resources res = getContext().getResources(); // Custom view due to alignment and font size requirements - // TODO (b/145021634): disabled because it's delaying user switch by 3 seconds - // getContext() - // .setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert_UserSwitchingDialog); + getContext().setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert_UserSwitchingDialog); View view = LayoutInflater.from(getContext()).inflate( R.layout.car_user_switching_dialog, null); diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS index 2f9a5c952659..7cc2e8eb2954 100644 --- a/services/core/java/com/android/server/am/OWNERS +++ b/services/core/java/com/android/server/am/OWNERS @@ -35,3 +35,5 @@ michaelwr@google.com narayan@google.com per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com + +per-file CarUserSwitchingDialog.java = keunyoung@google.com, felipeal@google.com, gurunagarajan@google.com diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 2bb703545cad..32975d7792f5 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -78,6 +78,9 @@ import android.os.Trace; import android.os.UserHandle; import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; import android.text.TextUtils; import android.util.ArrayMap; import android.util.EventLog; @@ -283,6 +286,16 @@ public final class ProcessList { // lmkd reconnect delay in msecs private final static long LMDK_RECONNECT_DELAY_MS = 1000; + /** + * How long between a process kill and we actually receive its death recipient + */ + private static final long PROC_KILL_TIMEOUT = 2000; // 2 seconds; + + /** + * How long between polls to check if the given process is dead or not. + */ + private static final long PROC_DEATH_POLL_INTERVAL = 100; + ActivityManagerService mService = null; // To kill process groups asynchronously @@ -1421,7 +1434,7 @@ public final class ProcessList { if (app.pendingStart) { return true; } - long startTime = SystemClock.elapsedRealtime(); + long startTime = SystemClock.uptimeMillis(); if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) { checkSlow(startTime, "startProcess: removing from pids map"); mService.mPidsSelfLocked.remove(app); @@ -1856,7 +1869,7 @@ public final class ProcessList { boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord, boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) { - long startTime = SystemClock.elapsedRealtime(); + long startTime = SystemClock.uptimeMillis(); ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(processName, info.uid, keepIfLarge); @@ -1917,10 +1930,9 @@ public final class ProcessList { // An application record is attached to a previous process, // clean it up now. if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app); - checkSlow(startTime, "startProcess: bad proc running, killing"); - ProcessList.killProcessGroup(app.uid, app.pid); - mService.handleAppDiedLocked(app, true, true); - checkSlow(startTime, "startProcess: done killing old proc"); + // do the killing + killProcAndWaitIfNecessaryLocked(app, true, app.uid == info.uid || app.isolated, + "startProcess: bad proc running, killing: %s", startTime); } if (app == null) { @@ -1961,6 +1973,70 @@ public final class ProcessList { return success ? app : null; } + /** + * A lite version of checking if a process is alive or not, by using kill(2) with signal 0. + * + * <p> + * Note that, zombie processes are stil "alive" in this case, use the {@link + * ActivityManagerService#isProcessAliveLocked} if zombie processes need to be excluded. + * </p> + */ + @GuardedBy("mService") + private boolean isProcessAliveLiteLocked(ProcessRecord app) { + try { + Os.kill(app.pid, 0); + } catch (ErrnoException e) { + return e.errno != OsConstants.ESRCH; + } + return true; + } + + /** + * Kill (if asked to) and wait for the given process died if necessary + * @param app - The process record to kill + * @param doKill - Kill the given process record + * @param wait - Wait for the death of the given process + * @param formatString - The log message for slow operation + * @param startTime - The start timestamp of the operation + */ + @GuardedBy("mService") + void killProcAndWaitIfNecessaryLocked(final ProcessRecord app, final boolean doKill, + final boolean wait, final String formatString, final long startTime) { + + checkSlow(startTime, String.format(formatString, "before appDied")); + + if (doKill) { + // do the killing + ProcessList.killProcessGroup(app.uid, app.pid); + } + + // wait for the death + if (wait) { + boolean isAlive = true; + // ideally we should use pidfd_open(2) but it's available on kernel 5.3 or later + + final long timeout = SystemClock.uptimeMillis() + PROC_KILL_TIMEOUT; + isAlive = isProcessAliveLiteLocked(app); + while (timeout > SystemClock.uptimeMillis() && isAlive) { + try { + Thread.sleep(PROC_DEATH_POLL_INTERVAL); + } catch (InterruptedException e) { + } + isAlive = isProcessAliveLiteLocked(app); + } + + if (isAlive) { + // Maybe the process goes into zombie, use an expensive API to check again. + if (mService.isProcessAliveLocked(app)) { + Slog.w(TAG, String.format(formatString, + "waiting for app killing timed out")); + } + } + } + + checkSlow(startTime, String.format(formatString, "after appDied")); + } + @GuardedBy("mService") private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) { StringBuilder sb = null; diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index bbf57728a20a..3dbf2c667c2f 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.os.Message; import android.os.UserHandle; import android.os.UserManager; +import android.util.Slog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewTreeObserver; @@ -46,6 +47,9 @@ class UserSwitchingDialog extends AlertDialog // Time to wait for the onWindowShown() callback before continuing the user switch private static final int WINDOW_SHOWN_TIMEOUT_MS = 3000; + // User switching doesn't happen that frequently, so it doesn't hurt to have it always on + protected static final boolean DEBUG = true; + private final ActivityManagerService mService; private final int mUserId; private static final int MSG_START_USER = 1; @@ -118,7 +122,7 @@ class UserSwitchingDialog extends AlertDialog @Override public void show() { - // Slog.v(TAG, "show called"); + if (DEBUG) Slog.d(TAG, "show called"); super.show(); final View decorView = getWindow().getDecorView(); if (decorView != null) { @@ -132,13 +136,14 @@ class UserSwitchingDialog extends AlertDialog @Override public void onWindowShown() { - // Slog.v(TAG, "onWindowShown called"); + if (DEBUG) Slog.d(TAG, "onWindowShown called"); startUser(); } void startUser() { synchronized (this) { if (!mStartedUser) { + Slog.i(TAG, "starting user " + mUserId); mService.mUserController.startUserInForeground(mUserId); dismiss(); mStartedUser = true; @@ -147,6 +152,8 @@ class UserSwitchingDialog extends AlertDialog decorView.getViewTreeObserver().removeOnWindowShownListener(this); } mHandler.removeMessages(MSG_START_USER); + } else { + Slog.i(TAG, "user " + mUserId + " already started"); } } } @@ -156,6 +163,8 @@ class UserSwitchingDialog extends AlertDialog public void handleMessage(Message msg) { switch (msg.what) { case MSG_START_USER: + Slog.w(TAG, "user switch window not shown in " + + WINDOW_SHOWN_TIMEOUT_MS + " ms"); startUser(); break; } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index a2b3574880c4..37add3da5a48 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -35,7 +35,6 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -45,6 +44,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Set; /** @@ -58,7 +58,7 @@ public class AudioDeviceInventory { // Actual list of connected devices // Key for map created from DeviceInfo.makeDeviceListKey() - private final ArrayMap<String, DeviceInfo> mConnectedDevices = new ArrayMap<>(); + private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>(); private @NonNull AudioDeviceBroker mDeviceBroker; @@ -148,8 +148,7 @@ public class AudioDeviceInventory { */ /*package*/ void onRestoreDevices() { synchronized (mConnectedDevices) { - for (int i = 0; i < mConnectedDevices.size(); i++) { - DeviceInfo di = mConnectedDevices.valueAt(i); + for (DeviceInfo di : mConnectedDevices.values()) { AudioSystem.setDeviceConnectionState( di.mDeviceType, AudioSystem.DEVICE_STATE_AVAILABLE, @@ -799,11 +798,10 @@ public class AudioDeviceInventory { } int delay = 0; Set<Integer> devices = new HashSet<>(); - for (int i = 0; i < mConnectedDevices.size(); i++) { - int dev = mConnectedDevices.valueAt(i).mDeviceType; - if (((dev & AudioSystem.DEVICE_BIT_IN) == 0) - && BECOMING_NOISY_INTENT_DEVICES_SET.contains(dev)) { - devices.add(dev); + for (DeviceInfo di : mConnectedDevices.values()) { + if (((di.mDeviceType & AudioSystem.DEVICE_BIT_IN) == 0) + && BECOMING_NOISY_INTENT_DEVICES_SET.contains(di.mDeviceType)) { + devices.add(di.mDeviceType); } } if (musicDevice == AudioSystem.DEVICE_NONE) { diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index c4ea81a83fdb..1d7c942e8224 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -848,7 +848,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { int[] ports = res.getIntArray( com.android.internal.R.array.config_localPrivateDisplayPorts); if (ports != null) { - int port = physicalAddress.getPort(); + int port = Byte.toUnsignedInt(physicalAddress.getPort()); for (int p : ports) { if (p == port) { return true; diff --git a/services/core/java/com/android/server/integrity/model/BitInputStream.java b/services/core/java/com/android/server/integrity/model/BitInputStream.java index 09bc7e8b9861..e768fe6626ac 100644 --- a/services/core/java/com/android/server/integrity/model/BitInputStream.java +++ b/services/core/java/com/android/server/integrity/model/BitInputStream.java @@ -16,15 +16,29 @@ package com.android.server.integrity.model; +import java.io.IOException; +import java.io.InputStream; + /** A wrapper class for reading a stream of bits. */ public class BitInputStream { - private byte[] mRuleBytes; private long mBitPointer; + private boolean mReadFromStream; + + private byte[] mRuleBytes; + private InputStream mRuleInputStream; + + private byte mCurrentRuleByte; public BitInputStream(byte[] ruleBytes) { this.mRuleBytes = ruleBytes; this.mBitPointer = 0; + this.mReadFromStream = false; + } + + public BitInputStream(InputStream ruleInputStream) { + this.mRuleInputStream = ruleInputStream; + this.mReadFromStream = true; } /** @@ -33,34 +47,43 @@ public class BitInputStream { * @param numOfBits The number of bits to read. * @return The value read from the stream. */ - public int getNext(int numOfBits) { + public int getNext(int numOfBits) throws IOException { int component = 0; int count = 0; - int idx = (int) (mBitPointer / 8); - int offset = 7 - (int) (mBitPointer % 8); - while (count++ < numOfBits) { - if (idx >= mRuleBytes.length) { - throw new IllegalArgumentException(String.format("Invalid byte index: %d", idx)); + if (mBitPointer % 8 == 0) { + mCurrentRuleByte = getNextByte(); } + int offset = 7 - (int) (mBitPointer % 8); component <<= 1; - component |= (mRuleBytes[idx] >>> offset) & 1; + component |= (mCurrentRuleByte >>> offset) & 1; - offset--; - if (offset == -1) { - idx++; - offset = 7; - } + mBitPointer++; } - mBitPointer += numOfBits; return component; } /** Check if there are bits left in the stream. */ - public boolean hasNext() { - return mBitPointer / 8 < mRuleBytes.length; + public boolean hasNext() throws IOException { + if (mReadFromStream) { + return mRuleInputStream.available() > 0; + } else { + return mBitPointer / 8 < mRuleBytes.length; + } + } + + private byte getNextByte() throws IOException { + if (mReadFromStream) { + return (byte) mRuleInputStream.read(); + } else { + int idx = (int) (mBitPointer / 8); + if (idx >= mRuleBytes.length) { + throw new IllegalArgumentException(String.format("Invalid byte index: %d", idx)); + } + return mRuleBytes[idx]; + } } } diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index 8aa0751af11c..3ef45a637bc1 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -36,6 +36,7 @@ import android.content.integrity.Rule; import com.android.server.integrity.model.BitInputStream; +import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -56,15 +57,14 @@ public class RuleBinaryParser implements RuleParser { @Override public List<Rule> parse(InputStream inputStream) throws RuleParseException { try { - byte[] ruleBytes = new byte[inputStream.available()]; - inputStream.read(ruleBytes); - return parse(ruleBytes); + BitInputStream bitInputStream = new BitInputStream(inputStream); + return parseRules(bitInputStream); } catch (Exception e) { throw new RuleParseException(e.getMessage(), e); } } - private List<Rule> parseRules(BitInputStream bitInputStream) { + private List<Rule> parseRules(BitInputStream bitInputStream) throws IOException { List<Rule> parsedRules = new ArrayList<>(); // Read the rule binary file format version. @@ -79,7 +79,7 @@ public class RuleBinaryParser implements RuleParser { return parsedRules; } - private Rule parseRule(BitInputStream bitInputStream) { + private Rule parseRule(BitInputStream bitInputStream) throws IOException { Formula formula = parseFormula(bitInputStream); int effect = bitInputStream.getNext(EFFECT_BITS); @@ -90,7 +90,7 @@ public class RuleBinaryParser implements RuleParser { return new Rule(formula, effect); } - private Formula parseFormula(BitInputStream bitInputStream) { + private Formula parseFormula(BitInputStream bitInputStream) throws IOException { int separator = bitInputStream.getNext(SEPARATOR_BITS); switch (separator) { case ATOMIC_FORMULA_START: @@ -105,7 +105,7 @@ public class RuleBinaryParser implements RuleParser { } } - private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) { + private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException { int connector = bitInputStream.getNext(CONNECTOR_BITS); List<Formula> formulas = new ArrayList<>(); @@ -118,7 +118,7 @@ public class RuleBinaryParser implements RuleParser { return new CompoundFormula(connector, formulas); } - private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) { + private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) throws IOException { int key = bitInputStream.getNext(KEY_BITS); int operator = bitInputStream.getNext(OPERATOR_BITS); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index b988fd4c40f1..fdbb7d9df293 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -127,7 +127,7 @@ public class RuleBinarySerializer implements RuleSerializer { bitOutputStream.setNext(SEPARATOR_BITS, ATOMIC_FORMULA_START); bitOutputStream.setNext(KEY_BITS, atomicFormula.getKey()); - if (atomicFormula instanceof AtomicFormula.StringAtomicFormula) { + if (atomicFormula.getTag() == AtomicFormula.STRING_ATOMIC_FORMULA_TAG) { AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) atomicFormula; bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ); @@ -135,7 +135,7 @@ public class RuleBinarySerializer implements RuleSerializer { stringAtomicFormula.getValue(), stringAtomicFormula.getIsHashedValue(), bitOutputStream); - } else if (atomicFormula instanceof AtomicFormula.IntAtomicFormula) { + } else if (atomicFormula.getTag() == AtomicFormula.INT_ATOMIC_FORMULA_TAG) { AtomicFormula.IntAtomicFormula intAtomicFormula = (AtomicFormula.IntAtomicFormula) atomicFormula; bitOutputStream.setNext(OPERATOR_BITS, intAtomicFormula.getOperator()); @@ -143,7 +143,7 @@ public class RuleBinarySerializer implements RuleSerializer { String.valueOf(intAtomicFormula.getValue()), /* isHashedValue= */ false, bitOutputStream); - } else if (atomicFormula instanceof AtomicFormula.BooleanAtomicFormula) { + } else if (atomicFormula.getTag() == AtomicFormula.BOOLEAN_ATOMIC_FORMULA_TAG) { AtomicFormula.BooleanAtomicFormula booleanAtomicFormula = (AtomicFormula.BooleanAtomicFormula) atomicFormula; bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java index 72068ceeb4f0..cfe50c6c8ac9 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java @@ -127,7 +127,7 @@ public class RuleXmlSerializer implements RuleSerializer { xmlSerializer.startTag(NAMESPACE, ATOMIC_FORMULA_TAG); serializeAttributeValue( KEY_ATTRIBUTE, String.valueOf(atomicFormula.getKey()), xmlSerializer); - if (atomicFormula instanceof AtomicFormula.StringAtomicFormula) { + if (atomicFormula.getTag() == AtomicFormula.STRING_ATOMIC_FORMULA_TAG) { serializeAttributeValue( VALUE_ATTRIBUTE, ((AtomicFormula.StringAtomicFormula) atomicFormula).getValue(), @@ -137,7 +137,7 @@ public class RuleXmlSerializer implements RuleSerializer { String.valueOf( ((AtomicFormula.StringAtomicFormula) atomicFormula).getIsHashedValue()), xmlSerializer); - } else if (atomicFormula instanceof AtomicFormula.IntAtomicFormula) { + } else if (atomicFormula.getTag() == AtomicFormula.INT_ATOMIC_FORMULA_TAG) { serializeAttributeValue( OPERATOR_ATTRIBUTE, String.valueOf(((AtomicFormula.IntAtomicFormula) atomicFormula).getOperator()), @@ -146,7 +146,7 @@ public class RuleXmlSerializer implements RuleSerializer { VALUE_ATTRIBUTE, String.valueOf(((AtomicFormula.IntAtomicFormula) atomicFormula).getValue()), xmlSerializer); - } else if (atomicFormula instanceof AtomicFormula.BooleanAtomicFormula) { + } else if (atomicFormula.getTag() == AtomicFormula.BOOLEAN_ATOMIC_FORMULA_TAG) { serializeAttributeValue( VALUE_ATTRIBUTE, String.valueOf(((AtomicFormula.BooleanAtomicFormula) atomicFormula).getValue()), diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 83b62159f9b0..ec4aedd01cea 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -379,7 +379,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!"); try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) { setLockCredentialInternal(unifiedProfilePassword, managedUserPassword, managedUserId, - false, /* isLockTiedToParent= */ true); + /* isLockTiedToParent= */ true); tieProfileLockToParent(managedUserId, unifiedProfilePassword); } } @@ -1473,17 +1473,12 @@ public class LockSettingsService extends ILockSettings.Stub { setLockCredentialInternal(LockscreenCredential.createNone(), profilePasswordMap.get(managedUserId), managedUserId, - false, /* isLockTiedToParent= */ true); + /* isLockTiedToParent= */ true); + mStorage.removeChildProfileLock(managedUserId); + removeKeystoreProfileKey(managedUserId); } else { - Slog.wtf(TAG, "clear tied profile challenges, but no password supplied."); - // Attempt an untrusted reset by supplying an empty credential. - setLockCredentialInternal(LockscreenCredential.createNone(), - LockscreenCredential.createNone(), - managedUserId, - true, /* isLockTiedToParent= */ true); + Slog.wtf(TAG, "Attempt to clear tied challenge, but no password supplied."); } - mStorage.removeChildProfileLock(managedUserId); - removeKeystoreProfileKey(managedUserId); } } } @@ -1567,7 +1562,7 @@ public class LockSettingsService extends ILockSettings.Stub { // should call setLockCredentialInternal. @Override public boolean setLockCredential(LockscreenCredential credential, - LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange) { + LockscreenCredential savedCredential, int userId) { if (!mLockPatternUtils.hasSecureLockScreen()) { throw new UnsupportedOperationException( @@ -1576,7 +1571,7 @@ public class LockSettingsService extends ILockSettings.Stub { checkWritePermission(userId); synchronized (mSeparateChallengeLock) { if (!setLockCredentialInternal(credential, savedCredential, - userId, allowUntrustedChange, /* isLockTiedToParent= */ false)) { + userId, /* isLockTiedToParent= */ false)) { return false; } setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null); @@ -1598,14 +1593,13 @@ public class LockSettingsService extends ILockSettings.Stub { * credentials are being tied to its parent's credentials. */ private boolean setLockCredentialInternal(LockscreenCredential credential, - LockscreenCredential savedCredential, int userId, - boolean allowUntrustedChange, boolean isLockTiedToParent) { + LockscreenCredential savedCredential, int userId, boolean isLockTiedToParent) { Preconditions.checkNotNull(credential); Preconditions.checkNotNull(savedCredential); synchronized (mSpManager) { if (isSyntheticPasswordBasedCredentialLocked(userId)) { return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId, - allowUntrustedChange, isLockTiedToParent); + isLockTiedToParent); } } @@ -1653,7 +1647,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (shouldMigrateToSyntheticPasswordLocked(userId)) { initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential, userId); return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId, - allowUntrustedChange, isLockTiedToParent); + isLockTiedToParent); } } if (DEBUG) Slog.d(TAG, "setLockCredentialInternal: user=" + userId); @@ -2075,7 +2069,7 @@ public class LockSettingsService extends ILockSettings.Stub { } if (shouldReEnroll) { setLockCredentialInternal(credential, credential, - userId, false, /* isLockTiedToParent= */ false); + userId, /* isLockTiedToParent= */ false); } else { // Now that we've cleared of all required GK migration, let's do the final // migration to synthetic password. @@ -2210,7 +2204,6 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.i(TAG, "RemoveUser: " + userId); mSpManager.removeUser(userId); mStrongAuth.removeUser(userId); - tryRemoveUserFromSpCacheLater(userId); final KeyStore ks = KeyStore.getInstance(); ks.onUserRemoved(userId); @@ -2471,25 +2464,7 @@ public class LockSettingsService extends ILockSettings.Stub { } } - /** - * A user's synthetic password does not change so it must be cached in certain circumstances to - * enable untrusted credential reset. - * - * Untrusted credential reset will be removed in a future version (b/68036371) at which point - * this cache is no longer needed as the SP will always be known when changing the user's - * credential. - */ - @GuardedBy("mSpManager") - private SparseArray<AuthenticationToken> mSpCache = new SparseArray<>(); - private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) { - // Preemptively cache the SP and then try to remove it in a handler. - Slog.i(TAG, "Caching SP for user " + userId); - synchronized (mSpManager) { - mSpCache.put(userId, auth); - } - tryRemoveUserFromSpCacheLater(userId); - if (mInjector.isGsiRunning()) { Slog.w(TAG, "AuthSecret disabled in GSI"); return; @@ -2510,42 +2485,6 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private void tryRemoveUserFromSpCacheLater(@UserIdInt int userId) { - mHandler.post(() -> { - if (!shouldCacheSpForUser(userId)) { - // The transition from 'should not cache' to 'should cache' can only happen if - // certain admin apps are installed after provisioning e.g. via adb. This is not - // a common case and we do not seamlessly support; it may result in the SP not - // being cached when it is needed. The cache can be re-populated by verifying - // the credential again. - Slog.i(TAG, "Removing SP from cache for user " + userId); - synchronized (mSpManager) { - mSpCache.remove(userId); - } - } - }); - } - - /** Do not hold any of the locks from this service when calling. */ - private boolean shouldCacheSpForUser(@UserIdInt int userId) { - // Before the user setup has completed, an admin could be installed that requires the SP to - // be cached (see below). - if (Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 0, userId) == 0) { - return true; - } - - // If the user has an admin which can perform an untrusted credential reset, the SP needs to - // be cached. If there isn't a DevicePolicyManager then there can't be an admin in the first - // place so caching is not necessary. - final DevicePolicyManagerInternal dpmi = LocalServices.getService( - DevicePolicyManagerInternal.class); - if (dpmi == null) { - return false; - } - return dpmi.canUserHaveUntrustedCredentialReset(userId); - } - /** * Precondition: vold and keystore unlocked. * @@ -2579,8 +2518,8 @@ public class LockSettingsService extends ILockSettings.Stub { * This could also happen during an untrusted reset to clear password. * * 3. credentialhash == null and credential != null - * This is the untrusted credential reset, OR the user sets a new lockscreen password - * FOR THE FIRST TIME on a SP-enabled device. New credential and new SID will be created + * The user sets a new lockscreen password FOR THE FIRST TIME on a SP-enabled device. + * New credential and new SID will be created */ @GuardedBy("mSpManager") @VisibleForTesting @@ -2905,8 +2844,7 @@ public class LockSettingsService extends ILockSettings.Stub { */ @GuardedBy("mSpManager") private boolean spBasedSetLockCredentialInternalLocked(LockscreenCredential credential, - LockscreenCredential savedCredential, int userId, - boolean allowUntrustedChange, boolean isLockTiedToParent) { + LockscreenCredential savedCredential, int userId, boolean isLockTiedToParent) { if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId); if (savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) { // get credential from keystore when managed profile has unified lock @@ -2927,50 +2865,24 @@ public class LockSettingsService extends ILockSettings.Stub { VerifyCredentialResponse response = authResult.gkResponse; AuthenticationToken auth = authResult.authToken; - // If existing credential is provided, the existing credential must match. - if (!savedCredential.isNone() && auth == null) { - Slog.w(TAG, "Failed to enroll: incorrect credential"); - return false; - } - boolean untrustedReset = false; - if (auth != null) { - onAuthTokenKnownForUser(userId, auth); - } else if (response == null) { - throw new IllegalStateException("Password change failed."); - } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) { - // We are performing an untrusted credential change, by DevicePolicyManager or other - // internal callers that don't provide the existing credential - Slog.w(TAG, "Untrusted credential change invoked"); - // Try to get a cached auth token, so we can keep SP unchanged. - auth = mSpCache.get(userId); - if (!allowUntrustedChange) { - throw new IllegalStateException("Untrusted credential change was invoked but it was" - + " not allowed. This is likely a bug. Auth token is null: " - + Boolean.toString(auth == null)); - } - untrustedReset = true; - } else /* responseCode == VerifyCredentialResponse.RESPONSE_RETRY */ { - Slog.w(TAG, "Rate limit exceeded, so password was not changed."); - return false; - } - if (auth != null) { - if (untrustedReset) { - // Force change the current SID to mantain existing behaviour that an untrusted - // reset leads to a change of SID. If the untrusted reset is for clearing the - // current password, the nuking of the SID will be done in - // setLockCredentialWithAuthTokenLocked next - mSpManager.newSidForUser(getGateKeeperService(), auth, userId); + if (auth == null) { + if (response == null + || response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) { + Slog.w(TAG, "Failed to enroll: incorrect credential."); + return false; } - setLockCredentialWithAuthTokenLocked(credential, auth, userId); - mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId); - } else { - throw new IllegalStateException( - "Untrusted credential reset not possible without cached SP"); - // Could call initializeSyntheticPasswordLocked(null, credential, credentialType, - // requestedQuality, userId) instead if we still allow untrusted reset that changes - // synthetic password. That would invalidate existing escrow tokens though. + if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { + Slog.w(TAG, "Failed to enroll: rate limit exceeded."); + return false; + } + // Should not be reachable, but just in case. + throw new IllegalStateException("password change failed"); } + + onAuthTokenKnownForUser(userId, auth); + setLockCredentialWithAuthTokenLocked(credential, auth, userId); + mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId); sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent); return true; } @@ -3125,11 +3037,10 @@ public class LockSettingsService extends ILockSettings.Stub { + "verification."); return false; } + onAuthTokenKnownForUser(userId, result.authToken); long oldHandle = getSyntheticPasswordHandleLocked(userId); setLockCredentialWithAuthTokenLocked(credential, result.authToken, userId); mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId); - - onAuthTokenKnownForUser(userId, result.authToken); return true; } @@ -3248,8 +3159,6 @@ public class LockSettingsService extends ILockSettings.Stub { private class DeviceProvisionedObserver extends ContentObserver { private final Uri mDeviceProvisionedUri = Settings.Global.getUriFor( Settings.Global.DEVICE_PROVISIONED); - private final Uri mUserSetupCompleteUri = Settings.Secure.getUriFor( - Settings.Secure.USER_SETUP_COMPLETE); private boolean mRegistered; @@ -3267,8 +3176,6 @@ public class LockSettingsService extends ILockSettings.Stub { reportDeviceSetupComplete(); clearFrpCredentialIfOwnerNotSecure(); } - } else if (mUserSetupCompleteUri.equals(uri)) { - tryRemoveUserFromSpCacheLater(userId); } } @@ -3320,8 +3227,6 @@ public class LockSettingsService extends ILockSettings.Stub { if (register) { mContext.getContentResolver().registerContentObserver(mDeviceProvisionedUri, false, this); - mContext.getContentResolver().registerContentObserver(mUserSetupCompleteUri, - false, this, UserHandle.USER_ALL); } else { mContext.getContentResolver().unregisterContentObserver(this); } diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index e9a8085950b0..c53647d053bc 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -136,8 +136,13 @@ public class SyntheticPasswordManager { "android-synthetic-password-personalization-context".getBytes(); static class AuthenticationResult { - public AuthenticationToken authToken; - public VerifyCredentialResponse gkResponse; + // Non-null if password/token passes verification, null otherwise + @Nullable public AuthenticationToken authToken; + // OK: password / token passes verification, user has a lockscreen + // null: user does not have a lockscreen (but password / token passes verification) + // ERROR: password / token fails verification + // RETRY: password / token verification is throttled at the moment. + @Nullable public VerifyCredentialResponse gkResponse; } /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d0ad47d3d7a6..12afef2cf950 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -171,6 +171,7 @@ import android.os.IDeviceIdleController; import android.os.IInterface; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; @@ -292,6 +293,9 @@ public class NotificationManagerService extends SystemService { public static final boolean ENABLE_CHILD_NOTIFICATIONS = SystemProperties.getBoolean("debug.child_notifs", true); + // pullStats report request: undecorated remote view stats + public static final int REPORT_REMOTE_VIEWS = 0x01; + static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean( "debug.notification.interruptiveness", false); @@ -4080,6 +4084,8 @@ public class NotificationManagerService extends SystemService { try { if (filter.stats) { dumpJson(pw, filter); + } else if (filter.rvStats) { + dumpRemoteViewStats(pw, filter); } else if (filter.proto) { dumpProto(fd, filter); } else if (filter.criticalPriority) { @@ -4556,6 +4562,49 @@ public class NotificationManagerService extends SystemService { new NotificationShellCmd(NotificationManagerService.this) .exec(this, in, out, err, args, callback, resultReceiver); } + + /** + * Get stats committed after startNs + * + * @param startNs Report stats committed after this time in nanoseconds. + * @param report Indicatess which section to include in the stats. + * @param doAgg Whether to aggregate the stats or keep them separated. + * @param out List of protos of individual commits or one representing the + * aggregate. + * @return the report time in nanoseconds, or 0 on error. + */ + @Override + public long pullStats(long startNs, int report, boolean doAgg, + List<ParcelFileDescriptor> out) { + checkCallerIsSystemOrShell(); + long startMs = TimeUnit.MILLISECONDS.convert(startNs, TimeUnit.NANOSECONDS); + + final long identity = Binder.clearCallingIdentity(); + try { + switch (report) { + case REPORT_REMOTE_VIEWS: + Slog.e(TAG, "pullStats REPORT_REMOTE_VIEWS from: " + + startMs + " wtih " + doAgg); + PulledStats stats = mUsageStats.remoteViewStats(startMs, doAgg); + if (stats != null) { + out.add(stats.toParcelFileDescriptor(report)); + Slog.e(TAG, "exiting pullStats with: " + out.size()); + long endNs = TimeUnit.NANOSECONDS + .convert(stats.endTimeMs(), TimeUnit.MILLISECONDS); + return endNs; + } + Slog.e(TAG, "null stats for: " + report); + } + } catch (IOException e) { + + Slog.e(TAG, "exiting pullStats: on error", e); + return 0; + } finally { + Binder.restoreCallingIdentity(identity); + } + Slog.e(TAG, "exiting pullStats: bad request"); + return 0; + } }; @VisibleForTesting @@ -4773,6 +4822,15 @@ public class NotificationManagerService extends SystemService { pw.println(dump); } + private void dumpRemoteViewStats(PrintWriter pw, @NonNull DumpFilter filter) { + PulledStats stats = mUsageStats.remoteViewStats(filter.since, true); + if (stats == null) { + pw.println("no remote view stats reported."); + return; + } + stats.dump(REPORT_REMOTE_VIEWS, pw, filter); + } + private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) { final ProtoOutputStream proto = new ProtoOutputStream(fd); synchronized (mNotificationLock) { @@ -9084,6 +9142,7 @@ public class NotificationManagerService extends SystemService { public boolean zen; public long since; public boolean stats; + public boolean rvStats; public boolean redact = true; public boolean proto = false; public boolean criticalPriority = false; @@ -9119,6 +9178,14 @@ public class NotificationManagerService extends SystemService { } else { filter.since = 0; } + } else if ("--remote-view-stats".equals(a)) { + filter.rvStats = true; + if (ai < args.length-1) { + ai++; + filter.since = Long.parseLong(args[ai]); + } else { + filter.since = 0; + } } else if (PRIORITY_ARG.equals(a)) { // Bugreport will call the service twice with priority arguments, first to dump // critical sections and then non critical ones. Set approriate filters diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index fe3d0eb3e469..ac8d1a9a9ad4 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -149,6 +149,7 @@ public class NotificationUsageStats { stats.numPostedByApp++; stats.updateInterarrivalEstimate(now); stats.countApiUse(notification); + stats.numUndecoratedRemoteViews += (isUndecoratedRemoteView(notification) ? 1 : 0); } releaseAggregatedStatsLocked(aggregatedStatsArray); if (ENABLE_SQLITE_LOG) { @@ -157,6 +158,13 @@ public class NotificationUsageStats { } /** + * Does this notification use RemoveViews without a platform decoration? + */ + protected static boolean isUndecoratedRemoteView(NotificationRecord notification) { + return (notification.getNotification().getNotificationStyle() == null); + } + + /** * Called when a notification has been updated. */ public synchronized void registerUpdatedByApp(NotificationRecord notification, @@ -337,6 +345,15 @@ public class NotificationUsageStats { return dump; } + public PulledStats remoteViewStats(long startMs, boolean aggregate) { + if (ENABLE_SQLITE_LOG) { + if (aggregate) { + return mSQLiteLog.remoteViewAggStats(startMs); + } + } + return null; + } + public synchronized void dump(PrintWriter pw, String indent, DumpFilter filter) { if (ENABLE_AGGREGATED_IN_MEMORY_STATS) { for (AggregatedStats as : mStats.values()) { @@ -414,6 +431,7 @@ public class NotificationUsageStats { public int numRateViolations; public int numAlertViolations; public int numQuotaViolations; + public int numUndecoratedRemoteViews; public long mLastAccessTime; public int numImagesRemoved; @@ -685,6 +703,8 @@ public class NotificationUsageStats { output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n"); output.append(indentPlusTwo).append(quietImportance.toString()).append("\n"); output.append(indentPlusTwo).append(finalImportance.toString()).append("\n"); + output.append(indentPlusTwo); + output.append("numUndecorateRVs=").append(numUndecoratedRemoteViews).append("\n"); output.append(indent).append("}"); return output.toString(); } @@ -1044,7 +1064,7 @@ public class NotificationUsageStats { private static final int MSG_DISMISS = 4; private static final String DB_NAME = "notification_log.db"; - private static final int DB_VERSION = 5; + private static final int DB_VERSION = 7; /** Age in ms after which events are pruned from the DB. */ private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week @@ -1077,6 +1097,7 @@ public class NotificationUsageStats { private static final String COL_FIRST_EXPANSIONTIME_MS = "first_expansion_time_ms"; private static final String COL_AIRTIME_EXPANDED_MS = "expansion_airtime_ms"; private static final String COL_EXPAND_COUNT = "expansion_count"; + private static final String COL_UNDECORATED = "undecorated"; private static final int EVENT_TYPE_POST = 1; @@ -1102,12 +1123,20 @@ public class NotificationUsageStats { "COUNT(*) AS cnt, " + "SUM(" + COL_MUTED + ") as muted, " + "SUM(" + COL_NOISY + ") as noisy, " + - "SUM(" + COL_DEMOTED + ") as demoted " + + "SUM(" + COL_DEMOTED + ") as demoted, " + + "SUM(" + COL_UNDECORATED + ") as undecorated " + "FROM " + TAB_LOG + " " + "WHERE " + COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + " AND " + COL_EVENT_TIME + " > %d " + " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG; + private static final String UNDECORATED_QUERY = "SELECT " + + COL_PKG + ", " + + "MAX(" + COL_EVENT_TIME + ") as max_time " + + "FROM " + TAB_LOG + " " + + "WHERE " + COL_UNDECORATED + "> 0 " + + " AND " + COL_EVENT_TIME + " > %d " + + "GROUP BY " + COL_PKG; public SQLiteLog(Context context) { HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log", @@ -1163,7 +1192,8 @@ public class NotificationUsageStats { COL_AIRTIME_MS + " INT," + COL_FIRST_EXPANSIONTIME_MS + " INT," + COL_AIRTIME_EXPANDED_MS + " INT," + - COL_EXPAND_COUNT + " INT" + + COL_EXPAND_COUNT + " INT," + + COL_UNDECORATED + " INT" + ")"); } @@ -1273,6 +1303,7 @@ public class NotificationUsageStats { } else { putPosttimeVisibility(r, cv); } + cv.put(COL_UNDECORATED, (isUndecoratedRemoteView(r) ? 1 : 0)); SQLiteDatabase db = mHelper.getWritableDatabase(); if (db.insert(TAB_LOG, null, cv) < 0) { Log.wtf(TAG, "Error while trying to insert values: " + cv); @@ -1353,5 +1384,22 @@ public class NotificationUsageStats { } return dump; } + + public PulledStats remoteViewAggStats(long startMs) { + PulledStats stats = new PulledStats(startMs); + SQLiteDatabase db = mHelper.getReadableDatabase(); + String q = String.format(UNDECORATED_QUERY, startMs); + Cursor cursor = db.rawQuery(q, null); + try { + for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + String pkg = cursor.getString(0); + long maxTimeMs = cursor.getLong(1); + stats.addUndecoratedPackage(pkg, maxTimeMs); + } + } finally { + cursor.close(); + } + return stats; + } } } diff --git a/services/core/java/com/android/server/notification/PulledStats.java b/services/core/java/com/android/server/notification/PulledStats.java new file mode 100644 index 000000000000..ada890a10361 --- /dev/null +++ b/services/core/java/com/android/server/notification/PulledStats.java @@ -0,0 +1,129 @@ +/* + * 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.notification; + +import static com.android.server.notification.NotificationManagerService.REPORT_REMOTE_VIEWS; + +import android.os.ParcelFileDescriptor; +import android.service.notification.NotificationRemoteViewsProto; +import android.service.notification.PackageRemoteViewInfoProto; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +public class PulledStats { + static final String TAG = "PulledStats"; + + private final long mTimePeriodStartMs; + private long mTimePeriodEndMs; + private List<String> mUndecoratedPackageNames; + + public PulledStats(long startMs) { + mTimePeriodEndMs = mTimePeriodStartMs = startMs; + mUndecoratedPackageNames = new ArrayList<>(); + } + + ParcelFileDescriptor toParcelFileDescriptor(int report) + throws IOException { + final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); + switch(report) { + case REPORT_REMOTE_VIEWS: + Thread thr = new Thread("NotificationManager pulled metric output") { + public void run() { + try { + FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream( + fds[1]); + final ProtoOutputStream proto = new ProtoOutputStream(fout); + writeToProto(report, proto); + proto.flush(); + fout.close(); + } catch (IOException e) { + Slog.w(TAG, "Failure writing pipe", e); + } + } + }; + thr.start(); + break; + + default: + Slog.w(TAG, "Unknown pulled stats request: " + report); + break; + } + return fds[0]; + } + + /* + * @return the most recent timestamp in the report, as nanoseconds. + */ + public long endTimeMs() { + return mTimePeriodEndMs; + } + + public void dump(int report, PrintWriter pw, NotificationManagerService.DumpFilter filter) { + switch(report) { + case REPORT_REMOTE_VIEWS: + pw.print(" Packages with undecordated notifications ("); + pw.print(mTimePeriodStartMs); + pw.print(" - "); + pw.print(mTimePeriodEndMs); + pw.println("):"); + if (mUndecoratedPackageNames.size() == 0) { + pw.println(" none"); + } else { + for (String pkg : mUndecoratedPackageNames) { + if (!filter.filtered || pkg.equals(filter.pkgFilter)) { + pw.println(" " + pkg); + } + } + } + break; + + default: + pw.println("Unknown pulled stats request: " + report); + break; + } + } + + @VisibleForTesting + void writeToProto(int report, ProtoOutputStream proto) { + switch(report) { + case REPORT_REMOTE_VIEWS: + for (String pkg: mUndecoratedPackageNames) { + long token = proto.start(NotificationRemoteViewsProto.PACKAGE_REMOTE_VIEW_INFO); + proto.write(PackageRemoteViewInfoProto.PACKAGE_NAME, pkg); + proto.end(token); + } + break; + + default: + Slog.w(TAG, "Unknown pulled stats request: " + report); + break; + } + } + + public void addUndecoratedPackage(String packageName, long timestampMs) { + mUndecoratedPackageNames.add(packageName); + mTimePeriodEndMs = Math.max(mTimePeriodEndMs, timestampMs); + } +} diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index f5fcb77710ca..f1947ac15645 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -533,13 +533,13 @@ public final class OverlayManagerService extends SystemService { private final IBinder mService = new IOverlayManager.Stub() { @Override - public Map<String, List<OverlayInfo>> getAllOverlays(int userId) throws RemoteException { + public Map<String, List<OverlayInfo>> getAllOverlays(final int userIdArg) { try { - traceBegin(TRACE_TAG_RRO, "OMS#getAllOverlays " + userId); - userId = handleIncomingUser(userId, "getAllOverlays"); + traceBegin(TRACE_TAG_RRO, "OMS#getAllOverlays " + userIdArg); + final int realUserId = handleIncomingUser(userIdArg, "getAllOverlays"); synchronized (mLock) { - return mImpl.getOverlaysForUser(userId); + return mImpl.getOverlaysForUser(realUserId); } } finally { traceEnd(TRACE_TAG_RRO); @@ -548,16 +548,17 @@ public final class OverlayManagerService extends SystemService { @Override public List<OverlayInfo> getOverlayInfosForTarget(@Nullable final String targetPackageName, - int userId) throws RemoteException { + final int userIdArg) { + if (targetPackageName == null) { + return Collections.emptyList(); + } + try { traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfosForTarget " + targetPackageName); - userId = handleIncomingUser(userId, "getOverlayInfosForTarget"); - if (targetPackageName == null) { - return Collections.emptyList(); - } + final int realUserId = handleIncomingUser(userIdArg, "getOverlayInfosForTarget"); synchronized (mLock) { - return mImpl.getOverlayInfosForTarget(targetPackageName, userId); + return mImpl.getOverlayInfosForTarget(targetPackageName, realUserId); } } finally { traceEnd(TRACE_TAG_RRO); @@ -566,16 +567,17 @@ public final class OverlayManagerService extends SystemService { @Override public OverlayInfo getOverlayInfo(@Nullable final String packageName, - int userId) throws RemoteException { + final int userIdArg) { + if (packageName == null) { + return null; + } + try { traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + packageName); - userId = handleIncomingUser(userId, "getOverlayInfo"); - if (packageName == null) { - return null; - } + final int realUserId = handleIncomingUser(userIdArg, "getOverlayInfo"); synchronized (mLock) { - return mImpl.getOverlayInfo(packageName, userId); + return mImpl.getOverlayInfo(packageName, realUserId); } } finally { traceEnd(TRACE_TAG_RRO); @@ -584,19 +586,20 @@ public final class OverlayManagerService extends SystemService { @Override public boolean setEnabled(@Nullable final String packageName, final boolean enable, - int userId) throws RemoteException { + int userIdArg) { + if (packageName == null) { + return false; + } + try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable); - enforceActor(packageName, "setEnabled", userId); - userId = handleIncomingUser(userId, "setEnabled"); - if (packageName == null) { - return false; - } + final int realUserId = handleIncomingUser(userIdArg, "setEnabled"); + enforceActor(packageName, "setEnabled", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setEnabled(packageName, enable, userId); + return mImpl.setEnabled(packageName, enable, realUserId); } } finally { Binder.restoreCallingIdentity(ident); @@ -608,20 +611,21 @@ public final class OverlayManagerService extends SystemService { @Override public boolean setEnabledExclusive(@Nullable final String packageName, final boolean enable, - int userId) throws RemoteException { + int userIdArg) { + if (packageName == null || !enable) { + return false; + } + try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable); - enforceActor(packageName, "setEnabledExclusive", userId); - userId = handleIncomingUser(userId, "setEnabledExclusive"); - if (packageName == null || !enable) { - return false; - } + final int realUserId = handleIncomingUser(userIdArg, "setEnabledExclusive"); + enforceActor(packageName, "setEnabledExclusive", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { return mImpl.setEnabledExclusive(packageName, false /* withinCategory */, - userId); + realUserId); } } finally { Binder.restoreCallingIdentity(ident); @@ -632,21 +636,23 @@ public final class OverlayManagerService extends SystemService { } @Override - public boolean setEnabledExclusiveInCategory(@Nullable String packageName, int userId) - throws RemoteException { + public boolean setEnabledExclusiveInCategory(@Nullable String packageName, + final int userIdArg) { + if (packageName == null) { + return false; + } + try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName); - enforceActor(packageName, "setEnabledExclusiveInCategory", userId); - userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory"); - if (packageName == null) { - return false; - } + final int realUserId = handleIncomingUser(userIdArg, + "setEnabledExclusiveInCategory"); + enforceActor(packageName, "setEnabledExclusiveInCategory", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { return mImpl.setEnabledExclusive(packageName, true /* withinCategory */, - userId); + realUserId); } } finally { Binder.restoreCallingIdentity(ident); @@ -658,20 +664,21 @@ public final class OverlayManagerService extends SystemService { @Override public boolean setPriority(@Nullable final String packageName, - @Nullable final String parentPackageName, int userId) throws RemoteException { + @Nullable final String parentPackageName, final int userIdArg) { + if (packageName == null || parentPackageName == null) { + return false; + } + try { traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " " + parentPackageName); - enforceActor(packageName, "setPriority", userId); - userId = handleIncomingUser(userId, "setPriority"); - if (packageName == null || parentPackageName == null) { - return false; - } + final int realUserId = handleIncomingUser(userIdArg, "setPriority"); + enforceActor(packageName, "setPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setPriority(packageName, parentPackageName, userId); + return mImpl.setPriority(packageName, parentPackageName, realUserId); } } finally { Binder.restoreCallingIdentity(ident); @@ -682,20 +689,20 @@ public final class OverlayManagerService extends SystemService { } @Override - public boolean setHighestPriority(@Nullable final String packageName, int userId) - throws RemoteException { + public boolean setHighestPriority(@Nullable final String packageName, final int userIdArg) { + if (packageName == null) { + return false; + } + try { traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName); - enforceActor(packageName, "setHighestPriority", userId); - userId = handleIncomingUser(userId, "setHighestPriority"); - if (packageName == null) { - return false; - } + final int realUserId = handleIncomingUser(userIdArg, "setHighestPriority"); + enforceActor(packageName, "setHighestPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setHighestPriority(packageName, userId); + return mImpl.setHighestPriority(packageName, realUserId); } } finally { Binder.restoreCallingIdentity(ident); @@ -706,20 +713,20 @@ public final class OverlayManagerService extends SystemService { } @Override - public boolean setLowestPriority(@Nullable final String packageName, int userId) - throws RemoteException { + public boolean setLowestPriority(@Nullable final String packageName, final int userIdArg) { + if (packageName == null) { + return false; + } + try { traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName); - enforceActor(packageName, "setLowestPriority", userId); - userId = handleIncomingUser(userId, "setLowestPriority"); - if (packageName == null) { - return false; - } + final int realUserId = handleIncomingUser(userIdArg, "setLowestPriority"); + enforceActor(packageName, "setLowestPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - return mImpl.setLowestPriority(packageName, userId); + return mImpl.setLowestPriority(packageName, realUserId); } } finally { Binder.restoreCallingIdentity(ident); @@ -730,7 +737,7 @@ public final class OverlayManagerService extends SystemService { } @Override - public String[] getDefaultOverlayPackages() throws RemoteException { + public String[] getDefaultOverlayPackages() { try { traceBegin(TRACE_TAG_RRO, "OMS#getDefaultOverlayPackages"); getContext().enforceCallingOrSelfPermission( @@ -750,18 +757,17 @@ public final class OverlayManagerService extends SystemService { } @Override - public void invalidateCachesForOverlay(@Nullable String packageName, int userId) - throws RemoteException { + public void invalidateCachesForOverlay(@Nullable String packageName, final int userIdArg) { if (packageName == null) { return; } - enforceActor(packageName, "invalidateCachesForOverlay", userId); - userId = handleIncomingUser(userId, "invalidateCachesForOverlay"); + final int realUserId = handleIncomingUser(userIdArg, "invalidateCachesForOverlay"); + enforceActor(packageName, "invalidateCachesForOverlay", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - mImpl.removeIdmapForOverlay(packageName, userId); + mImpl.removeIdmapForOverlay(packageName, realUserId); } } finally { Binder.restoreCallingIdentity(ident); @@ -876,11 +882,16 @@ public final class OverlayManagerService extends SystemService { getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, message); } - private void enforceActor(String packageName, String methodName, int userId) + private void enforceActor(String packageName, String methodName, int realUserId) throws SecurityException { - OverlayInfo overlayInfo = mImpl.getOverlayInfo(packageName, userId); + OverlayInfo overlayInfo = mImpl.getOverlayInfo(packageName, realUserId); + if (overlayInfo == null) { + throw new IllegalArgumentException("Unable to retrieve overlay information for " + + packageName); + } + int callingUid = Binder.getCallingUid(); - mActorEnforcer.enforceActor(overlayInfo, methodName, callingUid, userId); + mActorEnforcer.enforceActor(overlayInfo, methodName, callingUid, realUserId); } }; diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 1222d9a29baf..2b4b409f329a 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -36,6 +36,7 @@ import android.os.Environment; import android.os.RemoteException; import android.os.ServiceManager; import android.sysprop.ApexProperties; +import android.util.Singleton; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -65,22 +66,31 @@ abstract class ApexManager { static final int MATCH_ACTIVE_PACKAGE = 1 << 0; static final int MATCH_FACTORY_PACKAGE = 1 << 1; + private static final Singleton<ApexManager> sApexManagerSingleton = + new Singleton<ApexManager>() { + @Override + protected ApexManager create() { + if (ApexProperties.updatable().orElse(false)) { + try { + return new ApexManagerImpl(IApexService.Stub.asInterface( + ServiceManager.getServiceOrThrow("apexservice"))); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new IllegalStateException( + "Required service apexservice not available"); + } + } else { + return new ApexManagerFlattenedApex(); + } + } + }; + /** * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex} * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()} * evaluates to {@code true}. */ - static ApexManager create(Context systemContext) { - if (ApexProperties.updatable().orElse(false)) { - try { - return new ApexManagerImpl(systemContext, IApexService.Stub.asInterface( - ServiceManager.getServiceOrThrow("apexservice"))); - } catch (ServiceManager.ServiceNotFoundException e) { - throw new IllegalStateException("Required service apexservice not available"); - } - } else { - return new ApexManagerFlattenedApex(); - } + static ApexManager getInstance() { + return sApexManagerSingleton.get(); } /** @@ -101,7 +111,7 @@ abstract class ApexManager { */ abstract List<ActiveApexInfo> getActiveApexInfos(); - abstract void systemReady(); + abstract void systemReady(Context context); /** * Retrieves information about an APEX package. @@ -248,7 +258,6 @@ abstract class ApexManager { @VisibleForTesting static class ApexManagerImpl extends ApexManager { private final IApexService mApexService; - private final Context mContext; private final Object mLock = new Object(); /** * A map from {@code APEX packageName} to the {@Link PackageInfo} generated from the {@code @@ -260,8 +269,7 @@ abstract class ApexManager { @GuardedBy("mLock") private List<PackageInfo> mAllPackagesCache; - ApexManagerImpl(Context context, IApexService apexService) { - mContext = context; + ApexManagerImpl(IApexService apexService) { mApexService = apexService; } @@ -302,14 +310,14 @@ abstract class ApexManager { } @Override - void systemReady() { - mContext.registerReceiver(new BroadcastReceiver() { + void systemReady(Context context) { + context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // Post populateAllPackagesCacheIfNeeded to a background thread, since it's // expensive to run it in broadcast handler thread. BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded()); - mContext.unregisterReceiver(this); + context.unregisterReceiver(this); } }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); } @@ -643,7 +651,7 @@ abstract class ApexManager { } @Override - void systemReady() { + void systemReady(Context context) { // No-op } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 75d5bffc87a1..73ab82dc01b2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2618,7 +2618,7 @@ public class PackageManagerService extends IPackageManager.Stub mProtectedPackages = new ProtectedPackages(mContext); - mApexManager = ApexManager.create(mContext); + mApexManager = ApexManager.getInstance(); mAppsFilter = mInjector.getAppsFilter(); mDirsToScanAsSystem = new ArrayList<>(); @@ -19849,7 +19849,7 @@ public class PackageManagerService extends IPackageManager.Stub storage.registerListener(mStorageListener); mInstallerService.systemReady(); - mApexManager.systemReady(); + mApexManager.systemReady(mContext); mPackageDexOptimizer.systemReady(); mInjector.getStorageManagerInternal().addExternalStoragePolicy( diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 83891f60d4f7..a62616623cb5 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -16,6 +16,13 @@ package com.android.server.rollback; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; + +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; @@ -41,6 +48,7 @@ import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.server.PackageWatchdog; +import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; @@ -106,10 +114,11 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public boolean execute(VersionedPackage failedPackage) { + public boolean execute(VersionedPackage failedPackage, @FailureReasons int rollbackReason) { RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); RollbackInfo rollback = getAvailableRollback(rollbackManager, failedPackage); + int reasonToLog = mapFailureReasonToMetric(rollbackReason); if (rollback == null) { Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ " @@ -119,7 +128,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } logEvent(moduleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, + reasonToLog, failedPackage.getPackageName()); LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> { int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_FAILURE); @@ -136,11 +146,13 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve moduleMetadataPackage); } else { logEvent(moduleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + reasonToLog, failedPackage.getPackageName()); } } else { logEvent(moduleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + reasonToLog, failedPackage.getPackageName()); } }); @@ -219,12 +231,14 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } if (sessionInfo.isStagedSessionApplied()) { logEvent(oldModuleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); } else if (sessionInfo.isStagedSessionReady()) { // TODO: What do for staged session ready but not applied } else { logEvent(oldModuleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); } } @@ -303,12 +317,16 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve saveLastStagedRollbackId(rollbackId); logEvent(moduleMetadataPackage, StatsLog - .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED); + .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, + ""); mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); } else if (sessionInfo.isStagedSessionFailed() && markStagedSessionHandled(rollbackId)) { logEvent(moduleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, + ""); mContext.unregisterReceiver(listener); } } @@ -355,11 +373,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return rollbackId; } - private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type) { + private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type, + int rollbackReason, @NonNull String failingPackageName) { Slog.i(TAG, "Watchdog event occurred of type: " + type); if (moduleMetadataPackage != null) { StatsLog.logWatchdogRollbackOccurred(type, moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + moduleMetadataPackage.getVersionCode(), rollbackReason, failingPackageName); } } @@ -371,7 +390,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve mNumberOfNativeCrashPollsRemaining--; // Check if native watchdog reported a crash if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { - execute(getModuleMetadataPackage()); + execute(getModuleMetadataPackage(), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); // we stop polling after an attempt to execute rollback, regardless of whether the // attempt succeeds or not } else { @@ -392,4 +411,20 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve + "and mitigate native crashes"); mHandler.post(()->checkAndMitigateNativeCrashes()); } + + private int mapFailureReasonToMetric(@FailureReasons int failureReason) { + switch (failureReason) { + case PackageWatchdog.FAILURE_REASON_NATIVE_CRASH: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; + case PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; + case PackageWatchdog.FAILURE_REASON_APP_CRASH: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; + case PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; + default: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; + } + } + } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5d796055dcc7..83c854bae4b5 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -620,10 +620,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO: Make this final int mTargetSdk; - // Set to true when this app creates a surface while in the middle of an animation. In that - // case do not clear allDrawn until the animation completes. - boolean deferClearAllDrawn; - // Is this window's surface needed? This is almost like visible, except // it will sometimes be true a little earlier: when the activity record has // been shown, but is still waiting for its app transition to execute @@ -771,10 +767,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.print(" primaryColor="); pw.println(Integer.toHexString(taskDescription.getPrimaryColor())); pw.print(prefix + " backgroundColor="); - pw.println(Integer.toHexString(taskDescription.getBackgroundColor())); - pw.print(prefix + " statusBarColor="); - pw.println(Integer.toHexString(taskDescription.getStatusBarColor())); - pw.print(prefix + " navigationBarColor="); + pw.print(Integer.toHexString(taskDescription.getBackgroundColor())); + pw.print(" statusBarColor="); + pw.print(Integer.toHexString(taskDescription.getStatusBarColor())); + pw.print(" navigationBarColor="); pw.println(Integer.toHexString(taskDescription.getNavigationBarColor())); } } @@ -847,14 +843,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.println(requestedVrComponent); } super.dump(pw, prefix, dumpAll); - pw.print(" visible="); pw.print(mVisible); - if (appToken != null) { - pw.println(prefix + "app=true mVoiceInteraction=" + mVoiceInteraction); + if (mVoiceInteraction) { + pw.println(prefix + "mVoiceInteraction=true"); } - pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent); + pw.print(prefix); pw.print("mOccludesParent="); pw.print(mOccludesParent); pw.print(" mOrientation="); pw.println(mOrientation); pw.println(prefix + "mVisibleRequested=" + mVisibleRequested - + " mClientVisible=" + mClientVisible + + " mVisible=" + mVisible + " mClientVisible=" + mClientVisible + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "") + " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible); if (paused) { @@ -901,7 +896,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay); } if (lastVisibleTime != 0 || nowVisible) { - pw.print(prefix); pw.print(" nowVisible="); pw.print(nowVisible); + pw.print(prefix); pw.print("nowVisible="); pw.print(nowVisible); pw.print(" lastVisibleTime="); if (lastVisibleTime == 0) pw.print("0"); else TimeUtils.formatDuration(lastVisibleTime, now, pw); @@ -3235,7 +3230,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // to move that animation to the new one. if (fromActivity.allDrawn) { allDrawn = true; - deferClearAllDrawn = fromActivity.deferClearAllDrawn; } if (fromActivity.firstWindowDrawn) { firstWindowDrawn = true; @@ -3719,7 +3713,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void clearAllDrawn() { allDrawn = false; - deferClearAllDrawn = false; } /** diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index e9ad0d361b07..d7f4b34ba56d 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -389,7 +389,6 @@ public class AppTransitionController { // this guy's animations regardless of whether it's // gotten drawn. wtoken.allDrawn = true; - wtoken.deferClearAllDrawn = false; // Ensure that apps that are mid-starting are also scheduled to have their // starting windows removed after the animation is complete if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) { diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index df7c07055e87..470a02e5bd44 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -642,7 +642,8 @@ class DisplayWindowSettings { if (mIdentifier == IDENTIFIER_PORT && displayInfo.address != null) { // Config suggests using port as identifier for physical displays. if (displayInfo.address instanceof DisplayAddress.Physical) { - return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort(); + byte port = ((DisplayAddress.Physical) displayInfo.address).getPort(); + return "port:" + Byte.toUnsignedInt(port); } } return displayInfo.uniqueId; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index c42029155bd0..175fccbdd0ba 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -439,10 +439,6 @@ class WindowStateAnimator { if (!mWin.mActivityRecord.isAnimating(TRANSITION)) { mWin.mActivityRecord.clearAllDrawn(); - } else { - // Currently animating, persist current state of allDrawn until animation - // is complete. - mWin.mActivityRecord.deferClearAllDrawn = true; } } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index f81713ed4385..53edf9d3086a 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -280,10 +280,11 @@ class WindowToken extends WindowContainer<WindowState> { super.dump(pw, prefix, dumpAll); pw.print(prefix); pw.print("windows="); pw.println(mChildren); pw.print(prefix); pw.print("windowType="); pw.print(windowType); - pw.print(" hasVisible="); pw.println(hasVisible); + pw.print(" hasVisible="); pw.print(hasVisible); if (waitingToShow) { - pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow); + pw.print(" waitingToShow=true"); } + pw.println(); } @Override diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index cb599be82aa6..ee0449d95e00 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4345,6 +4345,40 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + /** + * Get the list of active admins for an affected user: + * <ul> + * <li>The active admins associated with the userHandle itself</li> + * <li>The parent active admins for each managed profile associated with the userHandle</li> + * </ul> + * + * @param userHandle the affected user for whom to get the active admins + * @param parent whether the parent active admins should be included in the list of active + * admins or not + * @return the list of active admins for the affected user + */ + private List<ActiveAdmin> getActiveAdminsForAffectedUser(int userHandle, boolean parent) { + if (!parent) { + return getUserDataUnchecked(userHandle).mAdminList; + } + ArrayList<ActiveAdmin> admins = new ArrayList<>(); + for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) { + DevicePolicyData policy = getUserData(userInfo.id); + if (!userInfo.isManagedProfile()) { + admins.addAll(policy.mAdminList); + } else { + // For managed profiles, policies set on the parent profile will be included + for (int i = 0; i < policy.mAdminList.size(); i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (admin.hasParentActiveAdmin()) { + admins.add(admin.getParentActiveAdmin()); + } + } + } + } + return admins; + } + private boolean isSeparateProfileChallengeEnabled(int userHandle) { long ident = mInjector.binderClearCallingIdentity(); try { @@ -5096,118 +5130,58 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private boolean canPOorDOCallResetPassword(ActiveAdmin admin, @UserIdInt int userId) { - // Only if the admins targets a pre-O SDK - return getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.O; - } - - /* PO or DO could do an untrusted reset in certain conditions. */ - private boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId) { - synchronized (getLockObject()) { - // An active DO or PO might be able to fo an untrusted credential reset - for (final ActiveAdmin admin : getUserData(userId).mAdminList) { - if (!isActiveAdminWithPolicyForUserLocked(admin, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, userId)) { - continue; - } - if (canPOorDOCallResetPassword(admin, userId)) { - return true; - } - } - return false; + private boolean setPasswordPrivileged(@NonNull String password, int flags, int callingUid) { + // Only allow setting password on an unsecured user + if (isLockScreenSecureUnchecked(UserHandle.getUserId(callingUid))) { + throw new SecurityException("Cannot change current password"); } + return resetPasswordInternal(password, 0, null, flags, callingUid); } + @Override - public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException { + public boolean resetPassword(@Nullable String password, int flags) throws RemoteException { if (!mLockPatternUtils.hasSecureLockScreen()) { Slog.w(LOG_TAG, "Cannot reset password when the device has no lock screen"); return false; } - + if (password == null) password = ""; final int callingUid = mInjector.binderGetCallingUid(); final int userHandle = mInjector.userHandleGetCallingUserId(); - String password = passwordOrNull != null ? passwordOrNull : ""; - - // Password resetting to empty/null is not allowed for managed profiles. - if (TextUtils.isEmpty(password)) { - enforceNotManagedProfile(userHandle, "clear the active password"); + // As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to + // set password to an unsecured user. + if (mContext.checkCallingPermission(permission.RESET_PASSWORD) + == PackageManager.PERMISSION_GRANTED) { + return setPasswordPrivileged(password, flags, callingUid); } synchronized (getLockObject()) { - // If caller has PO (or DO) it can change the password, so see if that's the case first. + // If caller has PO (or DO) throw or fail silently depending on its target SDK level. ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked( null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid); - final boolean preN; if (admin != null) { - if (!canPOorDOCallResetPassword(admin, userHandle)) { - throw new SecurityException("resetPassword() is deprecated for DPC targeting O" - + " or later"); - } - preN = getTargetSdk(admin.info.getPackageName(), - userHandle) <= android.os.Build.VERSION_CODES.M; - } else { - // Otherwise, make sure the caller has any active admin with the right policy or - // the required permission. - admin = getActiveAdminOrCheckPermissionForCallerLocked( - null, - DeviceAdminInfo.USES_POLICY_RESET_PASSWORD, - android.Manifest.permission.RESET_PASSWORD); - // Cannot be preN if admin is null because an exception would have been - // thrown before getting here - preN = admin == null ? false : getTargetSdk(admin.info.getPackageName(), - userHandle) <= android.os.Build.VERSION_CODES.M; - - // As of N, password resetting to empty/null is not allowed anymore. - // TODO Should we allow DO/PO to set an empty password? - if (TextUtils.isEmpty(password)) { - if (!preN) { - throw new SecurityException("Cannot call with null password"); - } else { - Slog.e(LOG_TAG, "Cannot call with null password"); - return false; - } - } - // As of N, password cannot be changed by the admin if it is already set. - if (isLockScreenSecureUnchecked(userHandle)) { - if (!preN) { - throw new SecurityException("Cannot change current password"); - } else { - Slog.e(LOG_TAG, "Cannot change current password"); - return false; - } - } - } - // Do not allow to reset password when current user has a managed profile - if (!isManagedProfile(userHandle)) { - for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) { - if (userInfo.isManagedProfile()) { - if (!preN) { - throw new IllegalStateException( - "Cannot reset password on user has managed profile"); - } else { - Slog.e(LOG_TAG, "Cannot reset password on user has managed profile"); - return false; - } - } - } - } - // Do not allow to reset password when user is locked - if (!mUserManager.isUserUnlocked(userHandle)) { - if (!preN) { - throw new IllegalStateException("Cannot reset password when user is locked"); - } else { - Slog.e(LOG_TAG, "Cannot reset password when user is locked"); + if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) { + Slog.e(LOG_TAG, "DPC can no longer call resetPassword()"); return false; } + throw new SecurityException("Device admin can no longer call resetPassword()"); } - } - return resetPasswordInternal(password, 0, null, flags, callingUid, userHandle); + // Legacy device admin cannot call resetPassword either + admin = getActiveAdminForCallerLocked( + null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD, false); + if (getTargetSdk(admin.info.getPackageName(), + userHandle) <= android.os.Build.VERSION_CODES.M) { + Slog.e(LOG_TAG, "Device admin can no longer call resetPassword()"); + return false; + } + throw new SecurityException("Device admin can no longer call resetPassword()"); + } } private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token, - int flags, int callingUid, int userHandle) { + int flags, int callingUid) { + final int userHandle = UserHandle.getUserId(callingUid); synchronized (getLockObject()) { final PasswordMetrics minMetrics = getPasswordMinimumMetrics(userHandle); final List<PasswordValidationError> validationErrors; @@ -5245,21 +5219,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Don't do this with the lock held, because it is going to call // back in to the service. final long ident = mInjector.binderClearCallingIdentity(); - final boolean result; final LockscreenCredential newCredential = LockscreenCredential.createPasswordOrNone(password); try { - if (token == null) { - // This is the legacy reset password for DPM. Here we want to be able to override - // the old device password without necessarily knowing it. - mLockPatternUtils.setLockCredential( - newCredential, - LockscreenCredential.createNone(), - userHandle, /*allowUntrustedChange */true); - result = true; + if (tokenHandle == 0 || token == null) { + if (!mLockPatternUtils.setLockCredential(newCredential, + LockscreenCredential.createNone(), userHandle)) { + return false; + } } else { - result = mLockPatternUtils.setLockCredentialWithToken(newCredential, tokenHandle, - token, userHandle); + if (!mLockPatternUtils.setLockCredentialWithToken(newCredential, tokenHandle, + token, userHandle)) { + return false; + } } boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0; if (requireEntry) { @@ -5276,7 +5248,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } finally { mInjector.binderRestoreCallingIdentity(ident); } - return result; + return true; } private boolean isLockScreenSecureUnchecked(int userId) { @@ -7769,22 +7741,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * Disables all device cameras according to the specified admin. */ @Override - public void setCameraDisabled(ComponentName who, boolean disabled) { + public void setCameraDisabled(ComponentName who, boolean disabled, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); - final int userHandle = mInjector.userHandleGetCallingUserId(); + int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA); + DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent); + if (parent) { + enforceProfileOwnerOfOrganizationOwnedDevice(ap); + } if (ap.disableCamera != disabled) { ap.disableCamera = disabled; saveSettingsLocked(userHandle); } } // Tell the user manager that the restrictions have changed. - pushUserRestrictions(userHandle); + pushUserRestrictions(parent ? getProfileParentId(userHandle) : userHandle); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_CAMERA_DISABLED) .setAdmin(who) @@ -7797,18 +7772,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * active admins. */ @Override - public boolean getCameraDisabled(ComponentName who, int userHandle) { - return getCameraDisabled(who, userHandle, /* mergeDeviceOwnerRestriction= */ true); + public boolean getCameraDisabled(ComponentName who, int userHandle, boolean parent) { + return getCameraDisabled(who, userHandle, /* mergeDeviceOwnerRestriction= */ true, parent); } private boolean getCameraDisabled(ComponentName who, int userHandle, - boolean mergeDeviceOwnerRestriction) { + boolean mergeDeviceOwnerRestriction, boolean parent) { if (!mHasFeature) { return false; } + if (parent) { + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent); + enforceProfileOwnerOfOrganizationOwnedDevice(ap); + } synchronized (getLockObject()) { if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return (admin != null) ? admin.disableCamera : false; } // First, see if DO has set it. If so, it's device-wide. @@ -7818,13 +7798,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } } - - // Then check each device admin on the user. - DevicePolicyData policy = getUserData(userHandle); + // Return the strictest policy across all participating admins. + List<ActiveAdmin> admins = getActiveAdminsForAffectedUser(userHandle, parent); // Determine whether or not the device camera is disabled for any active admins. - final int N = policy.mAdminList.size(); - for (int i = 0; i < N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); + for (ActiveAdmin admin: admins) { if (admin.disableCamera) { return true; } @@ -8636,6 +8613,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } + @GuardedBy("getLockObject()") + ActiveAdmin getProfileOwnerOfOrganizationOwnedDeviceLocked(int userHandle) { + final long ident = mInjector.binderClearCallingIdentity(); + try { + for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) { + if (userInfo.isManagedProfile()) { + if (getProfileOwner(userInfo.id) != null + && canProfileOwnerAccessDeviceIds(userInfo.id)) { + ComponentName who = getProfileOwner(userInfo.id); + return getActiveAdminUncheckedLocked(who, userInfo.id); + } + } + } + } finally { + mInjector.binderRestoreCallingIdentity(ident); + } + return null; + } + @Override public String getProfileOwnerName(int userHandle) { if (!mHasFeature) { @@ -10323,7 +10319,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void pushUserRestrictions(int userId) { synchronized (getLockObject()) { final boolean isDeviceOwner = mOwners.isDeviceOwnerUserId(userId); - final Bundle userRestrictions; + Bundle userRestrictions = null; final int restrictionOwnerType; if (isDeviceOwner) { @@ -10335,42 +10331,60 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { addOrRemoveDisableCameraRestriction(userRestrictions, deviceOwner); restrictionOwnerType = UserManagerInternal.OWNER_TYPE_DEVICE_OWNER; } else { - final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId); - userRestrictions = profileOwner != null ? profileOwner.userRestrictions : null; - addOrRemoveDisableCameraRestriction(userRestrictions, userId); + final ActiveAdmin profileOwnerOfOrganizationOwnedDevice = + getProfileOwnerOfOrganizationOwnedDeviceLocked(userId); - if (isProfileOwnerOfOrganizationOwnedDevice(profileOwner)) { + // If profile owner of an organization owned device, the restrictions will be + // pushed to the parent instance. + if (profileOwnerOfOrganizationOwnedDevice != null && !isManagedProfile(userId)) { restrictionOwnerType = UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE; - } else if (profileOwner != null) { - restrictionOwnerType = UserManagerInternal.OWNER_TYPE_PROFILE_OWNER; + final ActiveAdmin parent = profileOwnerOfOrganizationOwnedDevice + .getParentActiveAdmin(); + userRestrictions = parent.userRestrictions; + userRestrictions = addOrRemoveDisableCameraRestriction(userRestrictions, + parent); } else { - restrictionOwnerType = UserManagerInternal.OWNER_TYPE_NO_OWNER; + final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId); + + if (profileOwner != null) { + userRestrictions = profileOwner.userRestrictions; + restrictionOwnerType = UserManagerInternal.OWNER_TYPE_PROFILE_OWNER; + } else { + restrictionOwnerType = UserManagerInternal.OWNER_TYPE_NO_OWNER; + } + userRestrictions = addOrRemoveDisableCameraRestriction( + userRestrictions, userId); } } - mUserManagerInternal.setDevicePolicyUserRestrictions(userId, userRestrictions, restrictionOwnerType); } } - private void addOrRemoveDisableCameraRestriction(Bundle userRestrictions, ActiveAdmin admin) { - if (userRestrictions == null) return; + private Bundle addOrRemoveDisableCameraRestriction(Bundle userRestrictions, ActiveAdmin admin) { + if (userRestrictions == null) { + userRestrictions = new Bundle(); + } if (admin.disableCamera) { userRestrictions.putBoolean(UserManager.DISALLOW_CAMERA, true); } else { userRestrictions.remove(UserManager.DISALLOW_CAMERA); } + return userRestrictions; } - private void addOrRemoveDisableCameraRestriction(Bundle userRestrictions, int userId) { - if (userRestrictions == null) return; + private Bundle addOrRemoveDisableCameraRestriction(Bundle userRestrictions, int userId) { + if (userRestrictions == null) { + userRestrictions = new Bundle(); + } if (getCameraDisabled(/* who= */ null, userId, /* mergeDeviceOwnerRestriction= */ false)) { userRestrictions.putBoolean(UserManager.DISALLOW_CAMERA, true); } else { userRestrictions.remove(UserManager.DISALLOW_CAMERA); } + return userRestrictions; } @Override @@ -11673,11 +11687,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId) { - return DevicePolicyManagerService.this.canUserHaveUntrustedCredentialReset(userId); - } - - @Override public CharSequence getPrintingDisabledReasonForUser(@UserIdInt int userId) { synchronized (getLockObject()) { if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING, @@ -13890,7 +13899,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (policy.mPasswordTokenHandle != 0) { final String password = passwordOrNull != null ? passwordOrNull : ""; return resetPasswordInternal(password, policy.mPasswordTokenHandle, token, - flags, mInjector.binderGetCallingUid(), userHandle); + flags, mInjector.binderGetCallingUid()); } else { Slog.w(LOG_TAG, "No saved token handle"); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 66f01f367493..21cacd45dcb5 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -47,6 +47,7 @@ import android.net.TetheringManager; import android.os.BaseBundle; import android.os.Binder; import android.os.Build; +import android.os.Debug; import android.os.Environment; import android.os.FactoryTest; import android.os.FileUtils; @@ -447,10 +448,6 @@ public final class SystemServer { // Mmmmmm... more memory! VMRuntime.getRuntime().clearGrowthLimit(); - // The system server has to run all of the time, so it needs to be - // as efficient as possible with its memory usage. - VMRuntime.getRuntime().setTargetHeapUtilization(0.8f); - // Some devices rely on runtime fingerprint generation, so make sure // we've defined it before booting further. Build.ensureFingerprintProperty(); @@ -502,6 +499,24 @@ public final class SystemServer { LocalServices.addService(SystemServiceManager.class, mSystemServiceManager); // Prepare the thread pool for init tasks that can be parallelized SystemServerInitThreadPool.start(); + // Attach JVMTI agent if this is a debuggable build and the system property is set. + if (Build.IS_DEBUGGABLE) { + // Property is of the form "library_path=parameters". + String jvmtiAgent = SystemProperties.get("persist.sys.dalvik.jvmtiagent"); + if (!jvmtiAgent.isEmpty()) { + int equalIndex = jvmtiAgent.indexOf('='); + String libraryPath = jvmtiAgent.substring(0, equalIndex); + String parameterList = + jvmtiAgent.substring(equalIndex + 1, jvmtiAgent.length()); + // Attach the agent. + try { + Debug.attachJvmtiAgent(libraryPath, parameterList, null); + } catch (Exception e) { + Slog.e("System", "*************************************************"); + Slog.e("System", "********** Failed to load jvmti plugin: " + jvmtiAgent); + } + } + } } finally { t.traceEnd(); // InitBeforeStartServices } diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 9e255fe19881..3518dc599cef 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -53,9 +53,12 @@ import java.util.LinkedList; @SmallTest @RunWith(AndroidJUnit4.class) public class LocalDisplayAdapterTest { - private static final long HANDLER_WAIT_MS = 100; + private static final Long DISPLAY_MODEL = Long.valueOf(0xAAAAAAAAL); + private static final int PORT_A = 0; + private static final int PORT_B = 0x80; + private static final int PORT_C = 0xFF; - private static final int PHYSICAL_DISPLAY_ID_MODEL_SHIFT = 8; + private static final long HANDLER_WAIT_MS = 100; private StaticMockitoSession mMockitoSession; @@ -74,7 +77,7 @@ public class LocalDisplayAdapterTest { private TestListener mListener = new TestListener(); - private LinkedList<Long> mDisplayIds = new LinkedList<>(); + private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>(); @Before public void setUp() throws Exception { @@ -106,30 +109,22 @@ public class LocalDisplayAdapterTest { */ @Test public void testPrivateDisplay() throws Exception { - // needs default one always - final long displayId0 = 0; - setUpDisplay(new DisplayConfig(displayId0, createDummyDisplayInfo())); - final long displayId1 = 1; - setUpDisplay(new DisplayConfig(displayId1, createDummyDisplayInfo())); - final long displayId2 = 2; - setUpDisplay(new DisplayConfig(displayId2, createDummyDisplayInfo())); + setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_A), createDummyDisplayInfo())); + setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_B), createDummyDisplayInfo())); + setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_C), createDummyDisplayInfo())); updateAvailableDisplays(); - // display 1 should be marked as private while display 2 is not. - doReturn(new int[]{(int) displayId1}).when(mMockedResources) + doReturn(new int[]{ PORT_B }).when(mMockedResources) .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts); mAdapter.registerLocked(); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); // This should be public - assertDisplay(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), displayId0, - false); + assertDisplay(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, false); // This should be private - assertDisplay(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), displayId1, - true); + assertDisplay(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_B, true); // This should be public - assertDisplay(mListener.addedDisplays.get(2).getDisplayDeviceInfoLocked(), displayId2, - false); + assertDisplay(mListener.addedDisplays.get(2).getDisplayDeviceInfoLocked(), PORT_C, false); } /** @@ -137,11 +132,8 @@ public class LocalDisplayAdapterTest { */ @Test public void testPublicDisplaysForNoConfigLocalPrivateDisplayPorts() throws Exception { - // needs default one always - final long displayId0 = 0; - setUpDisplay(new DisplayConfig(displayId0, createDummyDisplayInfo())); - final long displayId1 = 1; - setUpDisplay(new DisplayConfig(displayId1, createDummyDisplayInfo())); + setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_A), createDummyDisplayInfo())); + setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_C), createDummyDisplayInfo())); updateAvailableDisplays(); // config_localPrivateDisplayPorts is null mAdapter.registerLocked(); @@ -149,35 +141,36 @@ public class LocalDisplayAdapterTest { waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); // This should be public - assertDisplay(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), displayId0, - false); + assertDisplay(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, false); // This should be public - assertDisplay(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), displayId1, - false); + assertDisplay(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_C, false); } - private void assertDisplay(DisplayDeviceInfo info, long expectedPort, boolean shouldBePrivate) { - DisplayAddress.Physical physical = (DisplayAddress.Physical) info.address; - assertNotNull(physical); - assertEquals(expectedPort, physical.getPort()); + private static void assertDisplay( + DisplayDeviceInfo info, int expectedPort, boolean shouldBePrivate) { + final DisplayAddress.Physical address = (DisplayAddress.Physical) info.address; + assertNotNull(address); + assertEquals((byte) expectedPort, address.getPort()); + assertEquals(DISPLAY_MODEL, address.getModel()); assertEquals(shouldBePrivate, (info.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0); } private class DisplayConfig { - public final long displayId; + public final DisplayAddress.Physical address; public final IBinder displayToken = new Binder(); public final SurfaceControl.PhysicalDisplayInfo displayInfo; - private DisplayConfig(long displayId, SurfaceControl.PhysicalDisplayInfo displayInfo) { - this.displayId = displayId | (0x1 << PHYSICAL_DISPLAY_ID_MODEL_SHIFT); + private DisplayConfig( + DisplayAddress.Physical address, SurfaceControl.PhysicalDisplayInfo displayInfo) { + this.address = address; this.displayInfo = displayInfo; } } private void setUpDisplay(DisplayConfig config) { - mDisplayIds.add(config.displayId); - doReturn(config.displayToken).when( - () -> SurfaceControl.getPhysicalDisplayToken(config.displayId)); + mAddresses.add(config.address); + doReturn(config.displayToken).when(() -> + SurfaceControl.getPhysicalDisplayToken(config.address.getPhysicalDisplayId())); doReturn(new SurfaceControl.PhysicalDisplayInfo[]{ config.displayInfo }).when(() -> SurfaceControl.getDisplayConfigs(config.displayToken)); @@ -192,16 +185,20 @@ public class LocalDisplayAdapterTest { } private void updateAvailableDisplays() { - long[] ids = new long[mDisplayIds.size()]; + long[] ids = new long[mAddresses.size()]; int i = 0; - for (long id : mDisplayIds) { - ids[i] = id; + for (DisplayAddress.Physical address : mAddresses) { + ids[i] = address.getPhysicalDisplayId(); i++; } doReturn(ids).when(() -> SurfaceControl.getPhysicalDisplayIds()); } - private SurfaceControl.PhysicalDisplayInfo createDummyDisplayInfo() { + private static DisplayAddress.Physical createDisplayAddress(int port) { + return DisplayAddress.fromPortAndModel((byte) port, DISPLAY_MODEL); + } + + private static SurfaceControl.PhysicalDisplayInfo createDummyDisplayInfo() { SurfaceControl.PhysicalDisplayInfo info = new SurfaceControl.PhysicalDisplayInfo(); info.density = 100; info.xDpi = 100; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index c5fb0bde579f..5f1f3083361b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -23,7 +23,6 @@ import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManagerInternal; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; @@ -39,6 +38,9 @@ import java.util.Map; import java.util.Set; public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { + + private static final String USER_TYPE_EMPTY = ""; + private DpmMockContext mContext; @Override @@ -52,9 +54,10 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { } public void testMigration() throws Exception { - final File user10dir = getServices().addUser(10, 0); - final File user11dir = getServices().addUser(11, UserInfo.FLAG_MANAGED_PROFILE); - getServices().addUser(12, 0); + final File user10dir = getServices().addUser(10, 0, USER_TYPE_EMPTY); + final File user11dir = getServices().addUser(11, 0, + UserManager.USER_TYPE_PROFILE_MANAGED); + getServices().addUser(12, 0, USER_TYPE_EMPTY); setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); setUpPackageManagerForAdmin(admin2, UserHandle.getUid(10, 123)); @@ -273,7 +276,8 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { // Test setting default restrictions for managed profile. public void testMigration3_managedProfileOwner() throws Exception { // Create a managed profile user. - final File user10dir = getServices().addUser(10, UserInfo.FLAG_MANAGED_PROFILE); + final File user10dir = getServices().addUser(10, 0, + UserManager.USER_TYPE_PROFILE_MANAGED); // Profile owner package for managed profile user. setUpPackageManagerForAdmin(admin1, UserHandle.getUid(10, 123)); // Set up fake UserManager to make it look like a managed profile. diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 06b8716c0926..43d8f927a57e 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -151,6 +151,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { private DpmMockContext mServiceContext; private DpmMockContext mAdmin1Context; public DevicePolicyManager dpm; + public DevicePolicyManager parentDpm; public DevicePolicyManagerServiceTestable dpms; /* @@ -240,6 +241,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm = new DevicePolicyManagerTestable(mContext, dpms); + parentDpm = new DevicePolicyManagerTestable(mServiceContext, dpms, + /* parentInstance= */true); + mContext.binder.restoreCallingIdentity(ident); } @@ -269,7 +273,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { anyString(), any(UserHandle.class)); // Add the first secondary user. - getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0); + getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0, + UserManager.USER_TYPE_FULL_SECONDARY); } private void setAsProfileOwner(ComponentName admin) { @@ -330,7 +335,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testLoadAdminData_noAdmins() throws Exception { final int ANOTHER_USER_ID = 15; - getServices().addUser(ANOTHER_USER_ID, 0); + getServices().addUser(ANOTHER_USER_ID, 0, ""); initializeDpms(); @@ -477,7 +482,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { final int ANOTHER_USER_ID = 100; final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 20456); - getServices().addUser(ANOTHER_USER_ID, 0); // Add one more user. + getServices().addUser(ANOTHER_USER_ID, 0, ""); // Add one more user. // Set up pacakge manager for the other user. setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID); @@ -1343,7 +1348,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { final int ANOTHER_USER_ID = 100; final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 456); - getServices().addUser(ANOTHER_USER_ID, 0); // Add one more user. + getServices().addUser(ANOTHER_USER_ID, 0, ""); // Add one more user. mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_USERS); @@ -1961,35 +1966,30 @@ public class DevicePolicyManagerTest extends DpmTestBase { // TODO Make sure restrictions are written to the file. } + // TODO: (b/138709470) test addUserRestriction as PO of an organization-owned device public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception { - setupProfileOwner(); + final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE; + final int MANAGED_PROFILE_ADMIN_UID = + UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; - final long ident = mServiceContext.binder.clearCallingIdentity(); - configureContextForAccess(mServiceContext, true); + addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1); + configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); - mServiceContext.binder.callingUid = - UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, - DpmMockContext.CALLER_MANAGED_PROVISIONING_UID); - try { - runAsCaller(mServiceContext, dpms, dpm -> { - dpm.markProfileOwnerOnOrganizationOwnedDevice(admin1); - }); - } finally { - mServiceContext.binder.restoreCallingIdentity(ident); - } + when(getServices().userManager.getProfileParent(MANAGED_PROFILE_USER_ID)) + .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); - dpm.addUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME); + parentDpm.setCameraDisabled(admin1, true); verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions( - eq(DpmMockContext.CALLER_USER_HANDLE), - MockUtils.checkUserRestrictions(UserManager.DISALLOW_CONFIG_DATE_TIME), + eq(UserHandle.USER_SYSTEM), + MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA), eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE)); reset(getServices().userManagerInternal); - dpm.setCameraDisabled(admin1, true); + parentDpm.setCameraDisabled(admin1, false); verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions( - eq(DpmMockContext.CALLER_USER_HANDLE), - MockUtils.checkUserRestrictions(UserManager.DISALLOW_CONFIG_DATE_TIME, - UserManager.DISALLOW_CAMERA), + eq(UserHandle.USER_SYSTEM), + MockUtils.checkUserRestrictions(), eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE)); reset(getServices().userManagerInternal); } @@ -3861,7 +3861,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Add a secondary user, it should never talk with. final int ANOTHER_USER_ID = 36; - getServices().addUser(ANOTHER_USER_ID, 0); + getServices().addUser(ANOTHER_USER_ID, 0, UserManager.USER_TYPE_FULL_SECONDARY); // Since the managed profile is not affiliated, they should not be allowed to talk to each // other. @@ -5206,8 +5206,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } public void testRevertProfileOwnership_adminAndProfileMigrated() throws Exception { - getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, UserInfo.FLAG_MANAGED_PROFILE, - UserHandle.USER_SYSTEM); + getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0, + UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); DpmTestUtils.writeInputStreamToFile( getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), getProfileOwnerPoliciesFile()); @@ -5218,8 +5218,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } public void testRevertProfileOwnership_profileNotMigrated() throws Exception { - getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, UserInfo.FLAG_MANAGED_PROFILE, - UserHandle.USER_SYSTEM); + getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0, + UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); DpmTestUtils.writeInputStreamToFile( getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), getProfileOwnerPoliciesFile()); @@ -5230,8 +5230,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } public void testRevertProfileOwnership_adminAndProfileNotMigrated() throws Exception { - getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, UserInfo.FLAG_MANAGED_PROFILE, - UserHandle.USER_SYSTEM); + getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0, + UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); DpmTestUtils.writeInputStreamToFile( getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_not_migrated), getProfileOwnerPoliciesFile()); @@ -5405,11 +5405,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY); setAsProfileOwner(admin1); - new DevicePolicyManagerTestable( - mServiceContext, - dpms, - /* parentInstance= */ true) - .getPasswordComplexity(); + parentDpm.getPasswordComplexity(); assertEquals(PASSWORD_COMPLEXITY_NONE, dpm.getPasswordComplexity()); } @@ -5685,7 +5681,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void addManagedProfile( ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { final int userId = UserHandle.getUserId(adminUid); - getServices().addUser(userId, UserInfo.FLAG_MANAGED_PROFILE, UserHandle.USER_SYSTEM); + getServices().addUser(userId, 0, UserManager.USER_TYPE_PROFILE_MANAGED, + UserHandle.USER_SYSTEM); mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); setUpPackageManagerForFakeAdmin(admin, adminUid, copyFromAdmin); dpm.setActiveAdmin(admin, false, userId); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 6a0d9265f594..7a2350eb4402 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -185,8 +185,8 @@ public class MockSystemServices { // Add the system user with a fake profile group already set up (this can happen in the real // world if a managed profile is added and then removed). - systemUserDataDir = - addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY, UserHandle.USER_SYSTEM); + systemUserDataDir = addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY, + UserManager.USER_TYPE_FULL_SYSTEM, UserHandle.USER_SYSTEM); // System user is always running. setUserRunning(UserHandle.USER_SYSTEM, true); @@ -208,26 +208,21 @@ public class MockSystemServices { mBroadcastReceivers.removeIf(r -> r.receiver == receiver); } - public File addUser(int userId, int flags) { - return addUser(userId, flags, UserInfo.NO_PROFILE_GROUP_ID); + public File addUser(int userId, int flags, String type) { + return addUser(userId, flags, type, UserInfo.NO_PROFILE_GROUP_ID); } - public File addUser(int userId, int flags, int profileGroupId) { + public File addUser(int userId, int flags, String type, int profileGroupId) { // Set up (default) UserInfo for CALLER_USER_HANDLE. final UserInfo uh = new UserInfo(userId, "user" + userId, flags); + + uh.userType = type; uh.profileGroupId = profileGroupId; when(userManager.getUserInfo(eq(userId))).thenReturn(uh); - mUserInfos.add(uh); when(userManager.getUsers()).thenReturn(mUserInfos); when(userManager.getUsers(anyBoolean())).thenReturn(mUserInfos); when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(true); - when(userManager.getUserInfo(anyInt())).thenAnswer( - invocation -> { - final int userId1 = (int) invocation.getArguments()[0]; - return getUserInfo(userId1); - } - ); when(userManager.getProfileParent(anyInt())).thenAnswer( invocation -> { final int userId1 = (int) invocation.getArguments()[0]; @@ -308,7 +303,7 @@ public class MockSystemServices { */ public void addUsers(int... userIds) { for (final int userId : userIds) { - addUser(userId, 0); + addUser(userId, 0, ""); } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java deleted file mode 100644 index d1c2fd0fa9dd..000000000000 --- a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2018 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.locksettings; - -import static com.android.server.testutils.TestUtils.assertExpectException; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.os.RemoteException; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.widget.LockscreenCredential; -import com.android.internal.widget.VerifyCredentialResponse; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; - -import java.util.ArrayList; - -/** - * Run the synthetic password tests with caching enabled. - * - * By default, those tests run without caching. Untrusted credential reset depends on caching so - * this class included those tests. - */ -@SmallTest -@Presubmit -@RunWith(AndroidJUnit4.class) -public class CachedSyntheticPasswordTests extends SyntheticPasswordTests { - - @Before - public void enableSpCache() throws Exception { - enableSpCaching(true); - } - - private void enableSpCaching(boolean enable) { - when(mDevicePolicyManagerInternal - .canUserHaveUntrustedCredentialReset(anyInt())).thenReturn(enable); - } - - @Test - public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException { - final LockscreenCredential password = newPassword("password"); - final LockscreenCredential newPassword = newPassword("newpassword"); - - initializeCredentialUnderSP(password, PRIMARY_USER_ID); - long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - // clear password - mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, true); - assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); - - // set a new password - mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID, - false); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - newPassword, 0, PRIMARY_USER_ID) - .getResponseCode()); - assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); - } - - @Test - public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException { - final LockscreenCredential password = newPassword("password"); - final LockscreenCredential newPassword = newPassword("newpassword"); - - initializeCredentialUnderSP(password, PRIMARY_USER_ID); - long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - // Untrusted change password - mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID, - true); - assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); - assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); - - // Verify the password - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - newPassword, 0, PRIMARY_USER_ID).getResponseCode()); - } - - @Test - public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException { - final LockscreenCredential password = newPassword("password"); - final LockscreenCredential newPassword = newPassword("newpassword"); - - initializeCredentialUnderSP(password, PRIMARY_USER_ID); - // Untrusted change password - mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID, - true); - - // Verify the password - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - newPassword, 0, PRIMARY_USER_ID) - .getResponseCode()); - - // Ensure the same secret was passed each time - ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class); - verify(mAuthSecretService, atLeastOnce()).primaryUserCredential(secret.capture()); - assertEquals(1, secret.getAllValues().stream().distinct().count()); - } - - @Test - public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException { - final LockscreenCredential password = newPassword("password"); - final LockscreenCredential newPassword = newPassword("newpassword"); - - // Disable caching for this test - enableSpCaching(false); - - initializeCredentialUnderSP(password, PRIMARY_USER_ID); - flushHandlerTasks(); - - long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - // Untrusted change password - assertExpectException( - IllegalStateException.class, - /* messageRegex= */ "Untrusted credential reset not possible without cached SP", - () -> mService.setLockCredential(newPassword, nonePassword(), - PRIMARY_USER_ID, true)); - assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); - - // Verify the new password doesn't work but the old one still does - assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential( - newPassword, 0, PRIMARY_USER_ID) - .getResponseCode()); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - password, 0, PRIMARY_USER_ID) - .getResponseCode()); - } - -} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 8c8edfad231f..abfda7725b7d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -93,7 +93,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), sid); assertFalse(mService.setLockCredential(newPassword("newpwd"), newPassword("badpwd"), - PRIMARY_USER_ID, false)); + PRIMARY_USER_ID)); assertVerifyCredentials(PRIMARY_USER_ID, newPassword("password"), sid); } @@ -101,7 +101,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { public void testClearPasswordPrimaryUser() throws RemoteException { initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), 1234); assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"), - PRIMARY_USER_ID, false)); + PRIMARY_USER_ID)); assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); } @@ -111,7 +111,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { final LockscreenCredential firstUnifiedPassword = newPassword("pwd-1"); final LockscreenCredential secondUnifiedPassword = newPassword("pwd-2"); assertTrue(mService.setLockCredential(firstUnifiedPassword, - nonePassword(), PRIMARY_USER_ID, false)); + nonePassword(), PRIMARY_USER_ID)); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); @@ -146,15 +146,14 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mStorageManager.setIgnoreBadUnlock(true); // Change primary password and verify that profile SID remains assertTrue(mService.setLockCredential( - secondUnifiedPassword, firstUnifiedPassword, PRIMARY_USER_ID, false)); + secondUnifiedPassword, firstUnifiedPassword, PRIMARY_USER_ID)); mStorageManager.setIgnoreBadUnlock(false); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID)); // Clear unified challenge assertTrue(mService.setLockCredential(nonePassword(), - secondUnifiedPassword, PRIMARY_USER_ID, - false)); + secondUnifiedPassword, PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(TURNED_OFF_PROFILE_USER_ID)); @@ -166,7 +165,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { final LockscreenCredential profilePassword = newPassword("profile"); assertTrue(mService.setLockCredential(primaryPassword, nonePassword(), - PRIMARY_USER_ID, false)); + PRIMARY_USER_ID)); /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new * credential as part of verifyCredential() before the new credential is committed in * StorageManager. So we relax the check in our mock StorageManager to allow that. @@ -174,7 +173,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mStorageManager.setIgnoreBadUnlock(true); assertTrue(mService.setLockCredential(profilePassword, nonePassword(), - MANAGED_PROFILE_USER_ID, false)); + MANAGED_PROFILE_USER_ID)); mStorageManager.setIgnoreBadUnlock(false); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); @@ -201,7 +200,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { // Change primary credential and make sure we don't affect profile mStorageManager.setIgnoreBadUnlock(true); assertTrue(mService.setLockCredential( - newPassword("pwd"), primaryPassword, PRIMARY_USER_ID, false)); + newPassword("pwd"), primaryPassword, PRIMARY_USER_ID)); mStorageManager.setIgnoreBadUnlock(false); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( profilePassword, 0, MANAGED_PROFILE_USER_ID) @@ -214,8 +213,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertTrue(mService.setLockCredential( newPassword("password"), nonePassword(), - PRIMARY_USER_ID, - false)); + PRIMARY_USER_ID)); verify(mRecoverableKeyStoreManager) .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, "password".getBytes(), @@ -228,8 +226,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertTrue(mService.setLockCredential( newPattern("12345"), nonePassword(), - MANAGED_PROFILE_USER_ID, - false)); + MANAGED_PROFILE_USER_ID)); verify(mRecoverableKeyStoreManager) .lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, "12345".getBytes(), @@ -247,8 +244,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertTrue(mService.setLockCredential( newPassword("newPassword"), newPattern("12345"), - MANAGED_PROFILE_USER_ID, - false)); + MANAGED_PROFILE_USER_ID)); verify(mRecoverableKeyStoreManager) .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, "newPassword".getBytes(), @@ -263,8 +259,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertTrue(mService.setLockCredential( newPattern("12345"), nonePassword(), - PRIMARY_USER_ID, - false)); + PRIMARY_USER_ID)); verify(mRecoverableKeyStoreManager, never()) .lockScreenSecretChanged( @@ -284,8 +279,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertTrue(mService.setLockCredential( newCredential, oldCredential, - PRIMARY_USER_ID, - false)); + PRIMARY_USER_ID)); verify(mRecoverableKeyStoreManager) .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential.getCredential(), @@ -305,8 +299,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertTrue(mService.setLockCredential( nonePassword(), newPassword("oldPassword"), - PRIMARY_USER_ID, - false)); + PRIMARY_USER_ID)); verify(mRecoverableKeyStoreManager) .lockScreenSecretChanged(CREDENTIAL_TYPE_NONE, null, PRIMARY_USER_ID); @@ -322,7 +315,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { 1234); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); - mService.setLockCredential(nonePassword(), newPattern("123654"), PRIMARY_USER_ID, false); + mService.setLockCredential(nonePassword(), newPattern("123654"), PRIMARY_USER_ID); // Verify fingerprint is removed verify(mFingerprintManager).remove(any(), eq(PRIMARY_USER_ID), any()); @@ -343,8 +336,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertTrue(mService.setLockCredential( profilePassword, nonePassword(), - MANAGED_PROFILE_USER_ID, - false)); + MANAGED_PROFILE_USER_ID)); verify(mRecoverableKeyStoreManager) .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, profilePassword.getCredential(), @@ -390,8 +382,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertTrue(mService.setLockCredential( pattern, nonePassword(), - MANAGED_PROFILE_USER_ID, - false)); + MANAGED_PROFILE_USER_ID)); reset(mRecoverableKeyStoreManager); mService.verifyCredential(pattern, 1, MANAGED_PROFILE_USER_ID); @@ -426,7 +417,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { private void testCreateCredential(int userId, LockscreenCredential credential) throws RemoteException { - assertTrue(mService.setLockCredential(credential, nonePassword(), userId, false)); + assertTrue(mService.setLockCredential(credential, nonePassword(), userId)); assertVerifyCredentials(userId, credential, -1); } @@ -435,7 +426,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mHasSecureLockScreen = false; try { - mService.setLockCredential(credential, null, userId, false); + mService.setLockCredential(credential, null, userId); fail("An exception should have been thrown."); } catch (UnsupportedOperationException e) { // Success - the exception was expected. @@ -448,7 +439,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { LockscreenCredential oldCredential) throws RemoteException { final long sid = 1234; initializeStorageWithCredential(userId, oldCredential, sid); - assertTrue(mService.setLockCredential(newCredential, oldCredential, userId, false)); + assertTrue(mService.setLockCredential(newCredential, oldCredential, userId)); assertVerifyCredentials(userId, newCredential, sid); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java index 27af9e2dfd6f..4b3f7b5d08ef 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java @@ -48,7 +48,7 @@ public class LockscreenFrpTest extends BaseLockSettingsServiceTests { @Test public void testFrpCredential_setPin() { - mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP)); assertEquals(VerifyCredentialResponse.RESPONSE_OK, @@ -57,7 +57,7 @@ public class LockscreenFrpTest extends BaseLockSettingsServiceTests { @Test public void testFrpCredential_setPattern() { - mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID); assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP)); assertEquals(VerifyCredentialResponse.RESPONSE_OK, @@ -66,7 +66,7 @@ public class LockscreenFrpTest extends BaseLockSettingsServiceTests { @Test public void testFrpCredential_setPassword() { - mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID); assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP)); assertEquals(VerifyCredentialResponse.RESPONSE_OK, @@ -75,8 +75,8 @@ public class LockscreenFrpTest extends BaseLockSettingsServiceTests { @Test public void testFrpCredential_changeCredential() { - mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false); - mService.setLockCredential(newPattern("5678"), newPassword("1234"), PRIMARY_USER_ID, false); + mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID); + mService.setLockCredential(newPattern("5678"), newPassword("1234"), PRIMARY_USER_ID); assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP)); assertEquals(VerifyCredentialResponse.RESPONSE_OK, @@ -85,16 +85,16 @@ public class LockscreenFrpTest extends BaseLockSettingsServiceTests { @Test public void testFrpCredential_removeCredential() { - mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID); assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP)); - mService.setLockCredential(nonePassword(), newPassword("1234"), PRIMARY_USER_ID, false); + mService.setLockCredential(nonePassword(), newPassword("1234"), PRIMARY_USER_ID); assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(USER_FRP)); } @Test public void testFrpCredential_cannotVerifyAfterProvsioning() { - mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); mSettings.setDeviceProvisioned(true); assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, @@ -103,7 +103,7 @@ public class LockscreenFrpTest extends BaseLockSettingsServiceTests { @Test public void testFrpCredential_legacyPinTypePersistentData() { - mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); PersistentData data = mStorage.readPersistentDataBlock(); // Tweak the existing persistent data to make it look like one with legacy credential type assertEquals(CREDENTIAL_TYPE_PIN, data.payload[3]); @@ -119,7 +119,7 @@ public class LockscreenFrpTest extends BaseLockSettingsServiceTests { @Test public void testFrpCredential_legacyPasswordTypePersistentData() { - mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID); PersistentData data = mStorage.readPersistentDataBlock(); // Tweak the existing persistent data to make it look like one with legacy credential type assertEquals(CREDENTIAL_TYPE_PASSWORD, data.payload[3]); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index 49858482fdf6..d6ef2d459769 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -106,7 +106,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { final LockscreenCredential password = newPassword("testPasswordMigration-password"); disableSyntheticPassword(); - assertTrue(mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false)); + assertTrue(mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID)); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); enableSyntheticPassword(); @@ -128,7 +128,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { protected void initializeCredentialUnderSP(LockscreenCredential password, int userId) throws RemoteException { enableSyntheticPassword(); - mService.setLockCredential(password, nonePassword(), userId, false); + mService.setLockCredential(password, nonePassword(), userId); assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(userId)); assertTrue(mService.isSyntheticPasswordBasedCredential(userId)); } @@ -140,7 +140,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - mService.setLockCredential(newPassword, password, PRIMARY_USER_ID, false); + mService.setLockCredential(newPassword, password, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( newPassword, 0, PRIMARY_USER_ID) .getResponseCode()); @@ -170,12 +170,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // clear password - mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false); + mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID); assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // set a new password - mService.setLockCredential(badPassword, nonePassword(), - PRIMARY_USER_ID, false); + mService.setLockCredential(badPassword, nonePassword(), PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( badPassword, 0, PRIMARY_USER_ID) .getResponseCode()); @@ -188,7 +187,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { LockscreenCredential badPassword = newPassword("new"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); - mService.setLockCredential(badPassword, password, PRIMARY_USER_ID, false); + mService.setLockCredential(badPassword, password, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( badPassword, 0, PRIMARY_USER_ID) .getResponseCode()); @@ -245,7 +244,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException { LockscreenCredential password = newPassword("getASyntheticPassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); - mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false); + mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID); reset(mAuthSecretService); mService.onUnlockUser(PRIMARY_USER_ID); @@ -257,7 +256,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testManagedProfileUnifiedChallengeMigration() throws RemoteException { LockscreenCredential UnifiedPassword = newPassword("unified-pwd"); disableSyntheticPassword(); - mService.setLockCredential(UnifiedPassword, nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(UnifiedPassword, nonePassword(), PRIMARY_USER_ID); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); @@ -292,9 +291,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { LockscreenCredential primaryPassword = newPassword("primary"); LockscreenCredential profilePassword = newPassword("profile"); disableSyntheticPassword(); - mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID, false); - mService.setLockCredential(profilePassword, nonePassword(), - MANAGED_PROFILE_USER_ID, false); + mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID); + mService.setLockCredential(profilePassword, nonePassword(), MANAGED_PROFILE_USER_ID); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); @@ -404,7 +402,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode(); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.setLockCredential(pattern, password, PRIMARY_USER_ID, false); + mService.setLockCredential(pattern, password, PRIMARY_USER_ID); mLocalService.setLockCredentialWithToken(newPassword, handle, token, PRIMARY_USER_ID); @@ -433,7 +431,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { // initialized but the user currently has no password initializeCredentialUnderSP(newPassword("password"), PRIMARY_USER_ID); assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"), - PRIMARY_USER_ID, false)); + PRIMARY_USER_ID)); assertTrue(mService.isSyntheticPasswordBasedCredential(PRIMARY_USER_ID)); long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); @@ -449,7 +447,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { LockscreenCredential password = newPassword("password"); // Set up pre-SP user password disableSyntheticPassword(); - mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID); enableSyntheticPassword(); long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); @@ -507,11 +505,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { @Test public void testGetHashFactorPrimaryUser() throws RemoteException { LockscreenCredential password = newPassword("password"); - mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID); byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID); assertNotNull(hashFactor); - mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false); + mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID); byte[] newHashFactor = mService.getHashFactor(nonePassword(), PRIMARY_USER_ID); assertNotNull(newHashFactor); // Hash factor should never change after password change/removal @@ -521,7 +519,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { @Test public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException { LockscreenCredential pattern = newPattern("1236"); - mService.setLockCredential(pattern, nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(pattern, nonePassword(), PRIMARY_USER_ID); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID)); } @@ -530,9 +528,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException { LockscreenCredential primaryPassword = newPassword("primary"); LockscreenCredential profilePassword = newPassword("profile"); - mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID, false); - mService.setLockCredential(profilePassword, nonePassword(), - MANAGED_PROFILE_USER_ID, false); + mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID); + mService.setLockCredential(profilePassword, nonePassword(), MANAGED_PROFILE_USER_ID); assertNotNull( mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID)); } diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 178f38aac0b7..fb9c68a5b70d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -68,7 +68,7 @@ public class ApexManagerTest { @Before public void setUp() throws RemoteException { mContext = InstrumentationRegistry.getInstrumentation().getContext(); - mApexManager = new ApexManager.ApexManagerImpl(mContext, mApexService); + mApexManager = new ApexManager.ApexManagerImpl(mApexService); } @Test diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 92198fa8cb0c..f608babd062c 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -20,6 +20,7 @@ android_test { "androidx.test.rules", "hamcrest-library", "mockito-target-inline-minus-junit4", "platform-test-annotations", + "platformprotosnano", "hamcrest-library", "testables", "truth-prebuilt", diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java new file mode 100644 index 000000000000..f685c68f4160 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java @@ -0,0 +1,113 @@ +/* + * 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.notification; + +import static com.android.server.notification.NotificationManagerService.REPORT_REMOTE_VIEWS; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotSame; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.service.notification.nano.NotificationRemoteViewsProto; +import android.test.MoreAsserts; +import android.util.proto.ProtoOutputStream; + +import androidx.test.filters.SmallTest; + +import com.android.server.UiServiceTestCase; + +import com.google.protobuf.nano.InvalidProtocolBufferNanoException; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; + +@SmallTest +public class PulledStatsTest extends UiServiceTestCase { + + @Test + public void testPulledStats_Empty() { + PulledStats stats = new PulledStats(0L); + assertEquals(0L, stats.endTimeMs()); + } + + @Test + public void testPulledStats_UnknownReport() { + PulledStats stats = new PulledStats(0L); + stats.addUndecoratedPackage("foo", 456); + stats.addUndecoratedPackage("bar", 123); + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + final ProtoOutputStream proto = new ProtoOutputStream(bytes); + stats.writeToProto(1023123, proto); // a very large number + proto.flush(); + + // expect empty output in response to an unrecognized request + assertEquals(0L, bytes.size()); + } + + @Test + public void testPulledStats_RemoteViewReportPackages() { + List<String> expectedPkgs = new ArrayList<>(2); + expectedPkgs.add("foo"); + expectedPkgs.add("bar"); + + PulledStats stats = new PulledStats(0L); + for(String pkg: expectedPkgs) { + stats.addUndecoratedPackage(pkg, 111); + } + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + final ProtoOutputStream protoStream = new ProtoOutputStream(bytes); + stats.writeToProto(REPORT_REMOTE_VIEWS, protoStream); + protoStream.flush(); + + try { + NotificationRemoteViewsProto proto = + NotificationRemoteViewsProto.parseFrom(bytes.toByteArray()); + List<String> actualPkgs = new ArrayList<>(2); + for(int i = 0 ; i < proto.packageRemoteViewInfo.length; i++) { + actualPkgs.add(proto.packageRemoteViewInfo[i].packageName); + } + assertEquals(2, actualPkgs.size()); + assertTrue("missing packages", actualPkgs.containsAll(expectedPkgs)); + assertTrue("unexpected packages", expectedPkgs.containsAll(actualPkgs)); + } catch (InvalidProtocolBufferNanoException e) { + e.printStackTrace(); + fail("writeToProto generated unparsable output"); + } + + } + @Test + public void testPulledStats_RemoteViewReportEndTime() { + List<String> expectedPkgs = new ArrayList<>(2); + expectedPkgs.add("foo"); + expectedPkgs.add("bar"); + + PulledStats stats = new PulledStats(0L); + long t = 111; + for(String pkg: expectedPkgs) { + t += 1000; + stats.addUndecoratedPackage(pkg, t); + } + assertEquals(t, stats.endTimeMs()); + } + +} 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 4d2183b32392..856641228d80 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -80,6 +80,9 @@ import java.nio.charset.StandardCharsets; @RunWith(WindowTestRunner.class) public class DisplayWindowSettingsTests extends WindowTestsBase { + private static final byte DISPLAY_PORT = (byte) 0xFF; + private static final long DISPLAY_MODEL = 0xEEEEEEEEL; + private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir(); private DisplayWindowSettings mTarget; @@ -479,10 +482,11 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { @Test public void testReadingDisplaySettingsFromStorage_UsePortAsId() { - final DisplayAddress.Physical displayAddress = DisplayAddress.fromPhysicalDisplayId(123456); + final DisplayAddress.Physical displayAddress = + DisplayAddress.fromPortAndModel(DISPLAY_PORT, DISPLAY_MODEL); mPrimaryDisplay.getDisplayInfo().address = displayAddress; - final String displayIdentifier = "port:" + displayAddress.getPort(); + final String displayIdentifier = "port:" + Byte.toUnsignedInt(DISPLAY_PORT); prepareDisplaySettings(displayIdentifier, true /* usePortAsId */); readAndAssertDisplaySettings(mPrimaryDisplay); @@ -521,7 +525,8 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { @Test public void testWritingDisplaySettingsToStorage_UsePortAsId() throws Exception { // Store config to use port as identifier. - final DisplayAddress.Physical displayAddress = DisplayAddress.fromPhysicalDisplayId(123456); + final DisplayAddress.Physical displayAddress = + DisplayAddress.fromPortAndModel(DISPLAY_PORT, DISPLAY_MODEL); mSecondaryDisplay.getDisplayInfo().address = displayAddress; prepareDisplaySettings(null /* displayIdentifier */, true /* usePortAsId */); @@ -532,7 +537,7 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { assertTrue(mStorage.wasWriteSuccessful()); // Verify that settings were stored correctly. - assertEquals("Attribute value must be stored", "port:" + displayAddress.getPort(), + assertEquals("Attribute value must be stored", "port:" + Byte.toUnsignedInt(DISPLAY_PORT), getStoredDisplayAttributeValue("name")); assertEquals("Attribute value must be stored", "true", getStoredDisplayAttributeValue("shouldShowSystemDecors")); diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index e34824c57fb2..1996dd4f6545 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -236,7 +236,7 @@ public class UsageStatsDatabase { return false; } } - } catch (IOException e) { + } catch (Exception e) { Slog.e(TAG, "Failed to check-in", e); return false; } @@ -744,7 +744,7 @@ public class UsageStatsDatabase { IntervalStats stats = new IntervalStats(); readLocked(f, stats); return stats; - } catch (IOException e) { + } catch (Exception e) { Slog.e(TAG, "Failed to read usage stats file", e); } } @@ -862,7 +862,7 @@ public class UsageStatsDatabase { if (beginTime < stats.endTime) { combiner.combine(stats, false, results); } - } catch (IOException e) { + } catch (Exception e) { Slog.e(TAG, "Failed to read usage stats file", e); // We continue so that we return results that are not // corrupt. @@ -1009,7 +1009,8 @@ public class UsageStatsDatabase { } } - private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException { + private void writeLocked(AtomicFile file, IntervalStats stats) + throws IOException, RuntimeException { if (mCurrentVersion <= 3) { Slog.wtf(TAG, "Attempting to write UsageStats as XML with version " + mCurrentVersion); return; @@ -1018,7 +1019,7 @@ public class UsageStatsDatabase { } private static void writeLocked(AtomicFile file, IntervalStats stats, int version, - PackagesTokenData packagesTokenData) throws IOException { + PackagesTokenData packagesTokenData) throws IOException, RuntimeException { FileOutputStream fos = file.startWrite(); try { writeLocked(fos, stats, version, packagesTokenData); @@ -1031,7 +1032,7 @@ public class UsageStatsDatabase { } private static void writeLocked(OutputStream out, IntervalStats stats, int version, - PackagesTokenData packagesTokenData) throws IOException { + PackagesTokenData packagesTokenData) throws RuntimeException { switch (version) { case 1: case 2: @@ -1041,7 +1042,7 @@ public class UsageStatsDatabase { case 4: try { UsageStatsProto.write(out, stats); - } catch (IOException | IllegalArgumentException e) { + } catch (Exception e) { Slog.e(TAG, "Unable to write interval stats to proto.", e); } break; @@ -1049,7 +1050,7 @@ public class UsageStatsDatabase { stats.obfuscateData(packagesTokenData); try { UsageStatsProtoV2.write(out, stats); - } catch (IOException | IllegalArgumentException e) { + } catch (Exception e) { Slog.e(TAG, "Unable to write interval stats to proto.", e); } break; @@ -1060,7 +1061,8 @@ public class UsageStatsDatabase { } } - private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException { + private void readLocked(AtomicFile file, IntervalStats statsOut) + throws IOException, RuntimeException { if (mCurrentVersion <= 3) { Slog.wtf(TAG, "Reading UsageStats as XML; current database version: " + mCurrentVersion); @@ -1072,7 +1074,7 @@ public class UsageStatsDatabase { * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise. */ private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version, - PackagesTokenData packagesTokenData) throws IOException { + PackagesTokenData packagesTokenData) throws IOException, RuntimeException { boolean dataOmitted = false; try { FileInputStream in = file.openRead(); @@ -1098,7 +1100,7 @@ public class UsageStatsDatabase { * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise. */ private static boolean readLocked(InputStream in, IntervalStats statsOut, int version, - PackagesTokenData packagesTokenData) throws IOException { + PackagesTokenData packagesTokenData) throws RuntimeException { boolean dataOmitted = false; switch (version) { case 1: @@ -1114,14 +1116,14 @@ public class UsageStatsDatabase { case 4: try { UsageStatsProto.read(in, statsOut); - } catch (IOException e) { + } catch (Exception e) { Slog.e(TAG, "Unable to read interval stats from proto.", e); } break; case 5: try { UsageStatsProtoV2.read(in, statsOut); - } catch (IOException e) { + } catch (Exception e) { Slog.e(TAG, "Unable to read interval stats from proto.", e); } dataOmitted = statsOut.deobfuscateData(packagesTokenData); @@ -1145,7 +1147,7 @@ public class UsageStatsDatabase { try (FileInputStream in = new AtomicFile(mPackageMappingsFile).openRead()) { UsageStatsProtoV2.readObfuscatedData(in, mPackagesTokenData); - } catch (IOException e) { + } catch (Exception e) { Slog.e(TAG, "Failed to read the obfuscated packages mapping file.", e); return; } @@ -1174,7 +1176,7 @@ public class UsageStatsDatabase { UsageStatsProtoV2.writeObfuscatedData(fos, mPackagesTokenData); file.finishWrite(fos); fos = null; - } catch (IOException | IllegalArgumentException e) { + } catch (Exception e) { Slog.e(TAG, "Unable to write obfuscated data to proto.", e); } finally { file.failWrite(fos); @@ -1414,8 +1416,8 @@ public class UsageStatsDatabase { try { stats.beginTime = in.readLong(); readLocked(in, stats, version, mPackagesTokenData); - } catch (IOException ioe) { - Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe); + } catch (Exception e) { + Slog.d(TAG, "DeSerializing IntervalStats Failed", e); stats = null; } return stats; diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 064922386773..c900f386b438 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -653,18 +653,20 @@ public class UsageStatsService extends SystemService implements } Arrays.sort(pendingEventsFiles); - for (int i = 0; i < pendingEventsFiles.length; i++) { + final int numFiles = pendingEventsFiles.length; + for (int i = 0; i < numFiles; i++) { final AtomicFile af = new AtomicFile(pendingEventsFiles[i]); + final LinkedList<Event> tmpEvents = new LinkedList<>(); try { try (FileInputStream in = af.openRead()) { - UsageStatsProtoV2.readPendingEvents(in, pendingEvents); + UsageStatsProtoV2.readPendingEvents(in, tmpEvents); } - } catch (IOException e) { - // Even if one file read fails, exit here to keep all events in order on disk - - // they will be read and processed the next time user is unlocked. + // only add to the pending events if the read was successful + pendingEvents.addAll(tmpEvents); + } catch (Exception e) { + // Most likely trying to read a corrupted file - log the failure and continue + // reading the other pending event files. Slog.e(TAG, "Could not read " + pendingEventsFiles[i] + " for user " + userId); - pendingEvents.clear(); - return; } } } @@ -691,7 +693,7 @@ public class UsageStatsService extends SystemService implements af.finishWrite(fos); fos = null; pendingEvents.clear(); - } catch (IOException | IllegalArgumentException e) { + } catch (Exception e) { Slog.e(TAG, "Failed to write " + pendingEventsFile.getAbsolutePath() + " for user " + userId); } finally { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 1b86c09ce86c..2e04730a5ea2 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2326,7 +2326,7 @@ public class CarrierConfigManager { * Reference: 3GPP TS 38.215 * * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are: - * "NONE: [-23, threshold1]" + * "NONE: [-20, threshold1]" * "POOR: (threshold1, threshold2]" * "MODERATE: (threshold2, threshold3]" * "GOOD: (threshold3, threshold4]" @@ -2360,15 +2360,26 @@ public class CarrierConfigManager { /** * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP), * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference - * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the - * parameter whose value is smallest is used to indicate the signal bar. + * ratio (SSSINR) for the number of 5G NR signal bars and signal criteria reporting enabling. + * + * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and + * not be used for calculating signal level. If multiple measures are set bit, the parameter + * whose value is smallest is used to indicate the signal level. * * SSRSRP = 1 << 0, * SSRSRQ = 1 << 1, * SSSINR = 1 << 2, * + * The value of this key must be bitwise OR of {@link CellSignalStrengthNr#USE_SSRSRP}, + * {@link CellSignalStrengthNr#USE_SSRSRQ}, {@link CellSignalStrengthNr#USE_SSSINR}. + * + * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2). + * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply. + * * Reference: 3GPP TS 38.215, * 3GPP TS 38.133 10.1.16.1 + * + * @hide */ public static final String KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT = "parameters_use_for_5g_nr_signal_bar_int"; @@ -3748,6 +3759,32 @@ public class CarrierConfigManager { -95, /* SIGNAL_STRENGTH_GOOD */ -85 /* SIGNAL_STRENGTH_GREAT */ }); + sDefaults.putIntArray(KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY, + // Boundaries: [-140 dB, -44 dB] + new int[] { + -125, /* SIGNAL_STRENGTH_POOR */ + -115, /* SIGNAL_STRENGTH_MODERATE */ + -105, /* SIGNAL_STRENGTH_GOOD */ + -95, /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY, + // Boundaries: [-20 dB, -3 dB] + new int[] { + -14, /* SIGNAL_STRENGTH_POOR */ + -12, /* SIGNAL_STRENGTH_MODERATE */ + -10, /* SIGNAL_STRENGTH_GOOD */ + -8 /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY, + // Boundaries: [-23 dB, 40 dB] + new int[] { + -8, /* SIGNAL_STRENGTH_POOR */ + 0, /* SIGNAL_STRENGTH_MODERATE */ + 8, /* SIGNAL_STRENGTH_GOOD */ + 16 /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putInt(KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, + CellSignalStrengthNr.USE_SSRSRP); sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi"); sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false); sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false); diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index f9b7f6dbc193..f31fafe36508 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -16,11 +16,15 @@ package android.telephony; +import android.annotation.IntDef; import android.annotation.IntRange; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; import java.util.Objects; /** @@ -36,13 +40,67 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private static final String TAG = "CellSignalStrengthNr"; + // Lifted from Default carrier configs and max range of SSRSRP + // Boundaries: [-140 dB, -44 dB] + private int[] mSsRsrpThresholds = new int[] { + -125, /* SIGNAL_STRENGTH_POOR */ + -115, /* SIGNAL_STRENGTH_MODERATE */ + -105, /* SIGNAL_STRENGTH_GOOD */ + -95, /* SIGNAL_STRENGTH_GREAT */ + }; + + // Lifted from Default carrier configs and max range of SSRSRQ + // Boundaries: [-20 dB, -3 dB] + private int[] mSsRsrqThresholds = new int[] { + -14, /* SIGNAL_STRENGTH_POOR */ + -12, /* SIGNAL_STRENGTH_MODERATE */ + -10, /* SIGNAL_STRENGTH_GOOD */ + -8 /* SIGNAL_STRENGTH_GREAT */ + }; + + // Lifted from Default carrier configs and max range of SSSINR + // Boundaries: [-23 dB, 40 dB] + private int[] mSsSinrThresholds = new int[] { + -8, /* SIGNAL_STRENGTH_POOR */ + 0, /* SIGNAL_STRENGTH_MODERATE */ + 8, /* SIGNAL_STRENGTH_GOOD */ + 16 /* SIGNAL_STRENGTH_GREAT */ + }; + + /** + * Indicates SSRSRP is considered for {@link #getLevel()} and reporting from modem. + * + * @hide + */ + public static final int USE_SSRSRP = 1 << 0; + /** + * Indicates SSRSRQ is considered for {@link #getLevel()} and reporting from modem. + * + * @hide + */ + public static final int USE_SSRSRQ = 1 << 1; /** - * These threshold values are copied from LTE. - * TODO: make it configurable via CarrierConfig. + * Indicates SSSINR is considered for {@link #getLevel()} and reporting from modem. + * + * @hide */ - private static final int SIGNAL_GREAT_THRESHOLD = -95; - private static final int SIGNAL_GOOD_THRESHOLD = -105; - private static final int SIGNAL_MODERATE_THRESHOLD = -115; + public static final int USE_SSSINR = 1 << 2; + + /** + * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP), + * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference + * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the + * parameter whose value is smallest is used to indicate the signal bar. + * + * @hide + */ + @IntDef(flag = true, prefix = { "USE_" }, value = { + USE_SSRSRP, + USE_SSRSRQ, + USE_SSSINR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SignalLevelAndReportCriteriaSource {} private int mCsiRsrp; private int mCsiRsrq; @@ -52,6 +110,21 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private int mSsSinr; private int mLevel; + /** + * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP), + * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference + * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the + * parameter whose value is smallest is used to indicate the signal bar. + * + * SSRSRP = 1 << 0, + * SSRSRQ = 1 << 1, + * SSSINR = 1 << 2, + * + * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2). + * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply. + */ + private int mParametersUseForLevel; + /** @hide */ public CellSignalStrengthNr() { setDefaultValues(); @@ -182,6 +255,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mSsRsrq = CellInfo.UNAVAILABLE; mSsSinr = CellInfo.UNAVAILABLE; mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + mParametersUseForLevel = USE_SSRSRP; } /** {@inheritDoc} */ @@ -191,20 +265,83 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa return mLevel; } + /** + * Checks if the given parameter type is considered to use for {@link #getLevel()}. + * + * Note: if multiple parameter types are considered, the smaller level for one of the + * parameters would be returned by {@link #getLevel()} + * + * @param parameterType bitwise OR of {@link #USE_SSRSRP}, {@link #USE_SSRSRQ}, + * {@link #USE_SSSINR} + * @return {@code true} if the level is calculated based on the given parameter type; + * {@code false} otherwise. + * + */ + private boolean isLevelForParameter(@SignalLevelAndReportCriteriaSource int parameterType) { + return (parameterType & mParametersUseForLevel) == parameterType; + } + /** @hide */ @Override public void updateLevel(PersistableBundle cc, ServiceState ss) { - if (mSsRsrp == CellInfo.UNAVAILABLE) { - mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; - } else if (mSsRsrp >= SIGNAL_GREAT_THRESHOLD) { - mLevel = SIGNAL_STRENGTH_GREAT; - } else if (mSsRsrp >= SIGNAL_GOOD_THRESHOLD) { - mLevel = SIGNAL_STRENGTH_GOOD; - } else if (mSsRsrp >= SIGNAL_MODERATE_THRESHOLD) { - mLevel = SIGNAL_STRENGTH_MODERATE; + if (cc == null) { + mParametersUseForLevel = USE_SSRSRP; + } else { + mParametersUseForLevel = cc.getInt( + CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, USE_SSRSRP); + Rlog.i(TAG, "Using SSRSRP for Level."); + mSsRsrpThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY); + Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: " + Arrays.toString(mSsRsrpThresholds)); + mSsRsrqThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY); + Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: " + Arrays.toString(mSsRsrqThresholds)); + mSsSinrThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY); + Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: " + Arrays.toString(mSsSinrThresholds)); + } + int ssRsrpLevel = SignalStrength.INVALID; + int ssRsrqLevel = SignalStrength.INVALID; + int ssSinrLevel = SignalStrength.INVALID; + if (isLevelForParameter(USE_SSRSRP)) { + ssRsrpLevel = updateLevelWithMeasure(mSsRsrp, mSsRsrpThresholds); + Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel); + } + if (isLevelForParameter(USE_SSRSRQ)) { + ssRsrqLevel = updateLevelWithMeasure(mSsRsrq, mSsRsrqThresholds); + Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel); + } + if (isLevelForParameter(USE_SSSINR)) { + ssSinrLevel = updateLevelWithMeasure(mSsSinr, mSsSinrThresholds); + Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel); + } + // Apply the smaller value among three levels of three measures. + mLevel = Math.min(Math.min(ssRsrpLevel, ssRsrqLevel), ssSinrLevel); + } + + /** + * Update level with corresponding measure and thresholds. + * + * @param measure corresponding signal measure + * @param thresholds corresponding signal thresholds + * @return level of the signal strength + */ + private int updateLevelWithMeasure(int measure, int[] thresholds) { + int level; + if (measure == CellInfo.UNAVAILABLE) { + level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } else if (measure > thresholds[3]) { + level = SIGNAL_STRENGTH_GREAT; + } else if (measure > thresholds[2]) { + level = SIGNAL_STRENGTH_GOOD; + } else if (measure > thresholds[1]) { + level = SIGNAL_STRENGTH_MODERATE; + } else if (measure > thresholds[0]) { + level = SIGNAL_STRENGTH_POOR; } else { - mLevel = SIGNAL_STRENGTH_POOR; + level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } + return level; } /** @@ -247,6 +384,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mSsRsrq = s.mSsRsrq; mSsSinr = s.mSsSinr; mLevel = s.mLevel; + mParametersUseForLevel = s.mParametersUseForLevel; } /** @hide */ @@ -290,6 +428,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa .append(" ssRsrq = " + mSsRsrq) .append(" ssSinr = " + mSsSinr) .append(" level = " + mLevel) + .append(" parametersUseForLevel = " + mParametersUseForLevel) .append(" }") .toString(); } diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java new file mode 100644 index 000000000000..f6f6d75c37c6 --- /dev/null +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -0,0 +1,256 @@ +/* + * 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.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Objects; + +/** + * Defines the threshold value of the signal strength. + * @hide + */ +public class SignalThresholdInfo implements Parcelable { + /** + * Received Signal Strength Indication. + * Range: -113 dBm and -51 dBm + * Used RAN: GERAN, CDMA2000 + * Reference: 3GPP TS 27.007 section 8.5. + */ + public static final int SIGNAL_RSSI = 1; + + /** + * Received Signal Code Power. + * Range: -120 dBm to -25 dBm; + * Used RAN: UTRAN + * Reference: 3GPP TS 25.123, section 9.1.1.1 + */ + public static final int SIGNAL_RSCP = 2; + + /** + * Reference Signal Received Power. + * Range: -140 dBm to -44 dBm; + * Used RAN: EUTRAN + * Reference: 3GPP TS 36.133 9.1.4 + */ + public static final int SIGNAL_RSRP = 3; + + /** + * Reference Signal Received Quality + * Range: -20 dB to -3 dB; + * Used RAN: EUTRAN + * Reference: 3GPP TS 36.133 9.1.7 + */ + public static final int SIGNAL_RSRQ = 4; + + /** + * Reference Signal Signal to Noise Ratio + * Range: -20 dB to 30 dB; + * Used RAN: EUTRAN + */ + public static final int SIGNAL_RSSNR = 5; + + /** + * 5G SS reference signal received power. + * Range: -140 dBm to -44 dBm. + * Used RAN: NGRAN + * Reference: 3GPP TS 38.215. + */ + public static final int SIGNAL_SSRSRP = 6; + + /** + * 5G SS reference signal received quality. + * Range: -20 dB to -3 dB. + * Used RAN: NGRAN + * Reference: 3GPP TS 38.215. + */ + public static final int SIGNAL_SSRSRQ = 7; + + /** + * 5G SS signal-to-noise and interference ratio. + * Range: -23 dB to 40 dB + * Used RAN: NGRAN + * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1. + */ + public static final int SIGNAL_SSSINR = 8; + + /** @hide */ + @IntDef(prefix = { "SIGNAL_" }, value = { + SIGNAL_RSSI, + SIGNAL_RSCP, + SIGNAL_RSRP, + SIGNAL_RSRQ, + SIGNAL_RSSNR, + SIGNAL_SSRSRP, + SIGNAL_SSRSRQ, + SIGNAL_SSSINR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SignalMeasurementType {} + + @SignalMeasurementType + private int mSignalMeasurement; + + /** + * A hysteresis time in milliseconds to prevent flapping. + * A value of 0 disables hysteresis + */ + private int mHysteresisMs; + + /** + * An interval in dB defining the required magnitude change between reports. + * hysteresisDb must be smaller than the smallest threshold delta. + * An interval value of 0 disables hysteresis. + */ + private int mHysteresisDb; + + /** + * List of threshold values. + * Range and unit must reference specific SignalMeasurementType + * The threshold values for which to apply criteria. + * A vector size of 0 disables the use of thresholds for reporting. + */ + private int[] mThresholds = null; + + /** + * {@code true} means modem must trigger the report based on the criteria; + * {@code false} means modem must not trigger the report based on the criteria. + */ + private boolean mIsEnabled = true; + + /** + * Indicates the hysteresisMs is disabled. + */ + public static final int HYSTERESIS_MS_DISABLED = 0; + + /** + * Indicates the hysteresisDb is disabled. + */ + public static final int HYSTERESIS_DB_DISABLED = 0; + + /** + * Constructor + * + * @param signalMeasurement Signal Measurement Type + * @param hysteresisMs hysteresisMs + * @param hysteresisDb hysteresisDb + * @param thresholds threshold value + * @param isEnabled isEnabled + */ + public SignalThresholdInfo(@SignalMeasurementType int signalMeasurement, + int hysteresisMs, int hysteresisDb, @NonNull int [] thresholds, boolean isEnabled) { + mSignalMeasurement = signalMeasurement; + mHysteresisMs = hysteresisMs < 0 ? HYSTERESIS_MS_DISABLED : hysteresisMs; + mHysteresisDb = hysteresisDb < 0 ? HYSTERESIS_DB_DISABLED : hysteresisDb; + mThresholds = thresholds == null ? null : thresholds.clone(); + mIsEnabled = isEnabled; + } + + public @SignalMeasurementType int getSignalMeasurement() { + return mSignalMeasurement; + } + + public int getHysteresisMs() { + return mHysteresisMs; + } + + public int getHysteresisDb() { + return mHysteresisDb; + } + + public boolean isEnabled() { + return mIsEnabled; + } + + public int[] getThresholds() { + return mThresholds == null ? null : mThresholds.clone(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mSignalMeasurement); + out.writeInt(mHysteresisMs); + out.writeInt(mHysteresisDb); + out.writeIntArray(mThresholds); + out.writeBoolean(mIsEnabled); + } + + private SignalThresholdInfo(Parcel in) { + mSignalMeasurement = in.readInt(); + mHysteresisMs = in.readInt(); + mHysteresisDb = in.readInt(); + mThresholds = in.createIntArray(); + mIsEnabled = in.readBoolean(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (!(o instanceof SignalThresholdInfo)) { + return false; + } + + SignalThresholdInfo other = (SignalThresholdInfo) o; + return mSignalMeasurement == other.mSignalMeasurement + && mHysteresisMs == other.mHysteresisMs + && mHysteresisDb == other.mHysteresisDb + && Arrays.equals(mThresholds, other.mThresholds) + && mIsEnabled == other.mIsEnabled; + } + + @Override + public int hashCode() { + return Objects.hash( + mSignalMeasurement, mHysteresisMs, mHysteresisDb, mThresholds, mIsEnabled); + } + + public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR = + new Parcelable.Creator<SignalThresholdInfo>() { + @Override + public SignalThresholdInfo createFromParcel(Parcel in) { + return new SignalThresholdInfo(in); + } + + @Override + public SignalThresholdInfo[] newArray(int size) { + return new SignalThresholdInfo[size]; + } + }; + + @Override + public String toString() { + return new StringBuilder("SignalThresholdInfo{") + .append("mSignalMeasurement=").append(mSignalMeasurement) + .append("mHysteresisMs=").append(mSignalMeasurement) + .append("mHysteresisDb=").append(mHysteresisDb) + .append("mThresholds=").append(Arrays.toString(mThresholds)) + .append("mIsEnabled=").append(mIsEnabled) + .append("}").toString(); + } +} diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 9eff809eaf5d..ebb517596b6c 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -89,8 +89,8 @@ public class SubscriptionInfo implements Parcelable { private int mCarrierId; /** - * The source of the name, NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SOURCE or - * NAME_SOURCE_USER_INPUT. + * The source of the name, NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SPN, + * NAME_SOURCE_SIM_PNN, or NAME_SOURCE_USER_INPUT. */ private int mNameSource; @@ -334,7 +334,7 @@ public class SubscriptionInfo implements Parcelable { } /** - * @return the source of the name, eg NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SOURCE or + * @return the source of the name, eg NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SPN or * NAME_SOURCE_USER_INPUT. * @hide */ diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index fbbf75a85a9b..cf705f466e48 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -54,6 +54,7 @@ import android.os.ParcelUuid; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.provider.Telephony.SimInfo; import android.telephony.euicc.EuiccManager; import android.telephony.ims.ImsMmTelManager; import android.util.DisplayMetrics; @@ -129,7 +130,7 @@ public class SubscriptionManager { /** @hide */ @UnsupportedAppUsage - public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo"); + public static final Uri CONTENT_URI = SimInfo.CONTENT_URI; /** * Generates a content {@link Uri} used to receive updates on simInfo change @@ -400,19 +401,19 @@ public class SubscriptionManager { public static final String NAME_SOURCE = "name_source"; /** - * The name_source is the default + * The name_source is the default, which is from the carrier id. * @hide */ public static final int NAME_SOURCE_DEFAULT_SOURCE = 0; /** - * The name_source is from the SIM + * The name_source is from SIM EF_SPN. * @hide */ - public static final int NAME_SOURCE_SIM_SOURCE = 1; + public static final int NAME_SOURCE_SIM_SPN = 1; /** - * The name_source is from the user + * The name_source is from user input * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -425,6 +426,24 @@ public class SubscriptionManager { public static final int NAME_SOURCE_CARRIER = 3; /** + * The name_source is from SIM EF_PNN. + * @hide + */ + public static final int NAME_SOURCE_SIM_PNN = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"NAME_SOURCE_"}, + value = { + NAME_SOURCE_DEFAULT_SOURCE, + NAME_SOURCE_SIM_SPN, + NAME_SOURCE_USER_INPUT, + NAME_SOURCE_CARRIER, + NAME_SOURCE_SIM_PNN + }) + public @interface SimDisplayNameSource {} + + /** * TelephonyProvider column name for the color of a SIM. * <P>Type: INTEGER (int)</P> */ @@ -1667,13 +1686,12 @@ public class SubscriptionManager { * Set display name by simInfo index with name source * @param displayName the display name of SIM card * @param subId the unique SubscriptionInfo index in database - * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, - * 2: NAME_SOURCE_USER_INPUT + * @param nameSource SIM display name source * @return the number of records updated or < 0 if invalid subId * @hide */ @UnsupportedAppUsage - public int setDisplayName(String displayName, int subId, int nameSource) { + public int setDisplayName(String displayName, int subId, @SimDisplayNameSource int nameSource) { if (VDBG) { logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId + " nameSource:" + nameSource); diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 5fd0af564d34..057d22cd7eae 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -161,9 +161,13 @@ public class ImsMmTelManager implements RegistrationManager { public void onCapabilitiesStatusChanged(int config) { if (mLocalCallback == null) return; - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged( - new MmTelFeature.MmTelCapabilities(config)))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged( + new MmTelFeature.MmTelCapabilities(config))); + } finally { + restoreCallingIdentity(callingIdentity); + } } @Override diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 21707b0d7cfd..b37d7c759be3 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -67,9 +67,13 @@ public class ImsRcsManager implements RegistrationManager { public void onCapabilitiesStatusChanged(int config) { if (mLocalCallback == null) return; - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged( - new RcsFeature.RcsImsCapabilities(config)))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged( + new RcsFeature.RcsImsCapabilities(config))); + } finally { + restoreCallingIdentity(callingIdentity); + } } @Override diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index e16085e30465..e4d63355625d 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -136,17 +136,24 @@ public class ProvisioningManager { @Override public final void onIntConfigChanged(int item, int value) { - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> - mLocalConfigurationCallback.onProvisioningIntChanged(item, value))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mLocalConfigurationCallback.onProvisioningIntChanged(item, value)); + } finally { + restoreCallingIdentity(callingIdentity); + } } @Override public final void onStringConfigChanged(int item, String value) { - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> - mLocalConfigurationCallback.onProvisioningStringChanged(item, - value))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mLocalConfigurationCallback.onProvisioningStringChanged(item, value)); + } finally { + restoreCallingIdentity(callingIdentity); + } } private void setExecutor(Executor executor) { diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index b47bcb9b119b..75e3f0a6393d 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -245,15 +245,22 @@ public class RcsUceAdapter { IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() { @Override public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { - Binder.withCleanCallingIdentity(() -> - executor.execute(() -> - c.onCapabilitiesReceived(contactCapabilities))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> + c.onCapabilitiesReceived(contactCapabilities)); + } finally { + restoreCallingIdentity(callingIdentity); + } } @Override public void onError(int errorCode) { - Binder.withCleanCallingIdentity(() -> - executor.execute(() -> - c.onError(errorCode))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> c.onError(errorCode)); + } finally { + restoreCallingIdentity(callingIdentity); + } } }; diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index 99bb259602e5..ca081ec1ea51 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -110,42 +110,63 @@ public interface RegistrationManager { public void onRegistered(int imsRadioTech) { if (mLocalCallback == null) return; - Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> - mLocalCallback.onRegistered(getAccessType(imsRadioTech)))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mLocalCallback.onRegistered(getAccessType(imsRadioTech))); + } finally { + restoreCallingIdentity(callingIdentity); + } } @Override public void onRegistering(int imsRadioTech) { if (mLocalCallback == null) return; - Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> - mLocalCallback.onRegistering(getAccessType(imsRadioTech)))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mLocalCallback.onRegistering(getAccessType(imsRadioTech))); + } finally { + restoreCallingIdentity(callingIdentity); + } } @Override public void onDeregistered(ImsReasonInfo info) { if (mLocalCallback == null) return; - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> mLocalCallback.onUnregistered(info))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onUnregistered(info)); + } finally { + restoreCallingIdentity(callingIdentity); + } } @Override public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) { if (mLocalCallback == null) return; - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed( - getAccessType(imsRadioTech), info))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed( + getAccessType(imsRadioTech), info)); + } finally { + restoreCallingIdentity(callingIdentity); + } } @Override public void onSubscriberAssociatedUriChanged(Uri[] uris) { if (mLocalCallback == null) return; - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> - mLocalCallback.onSubscriberAssociatedUriChanged(uris))); + long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onSubscriberAssociatedUriChanged(uris)); + } finally { + restoreCallingIdentity(callingIdentity); + } } private void setExecutor(Executor executor) { diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index e96d082ca953..0eaf8dc2fd51 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -22,7 +22,6 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; import android.net.Uri; -import android.os.Binder; import android.os.RemoteException; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.aidl.IImsCapabilityCallback; @@ -33,7 +32,7 @@ import android.telephony.ims.stub.RcsPresenceExchangeImplBase; import android.telephony.ims.stub.RcsSipOptionsImplBase; import android.util.Log; -import com.android.internal.util.FunctionalUtils; +import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -43,6 +42,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import java.util.function.Supplier; /** * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend @@ -150,13 +150,13 @@ public class RcsFeature extends ImsFeature { // Call the methods with a clean calling identity on the executor and wait indefinitely for // the future to return. - private void executeMethodAsync(FunctionalUtils.ThrowingRunnable r, String errorLogName) + private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { // call with a clean calling identity on the executor and wait indefinitely for the // future to return. try { CompletableFuture.runAsync( - () -> Binder.withCleanCallingIdentity(r), mExecutor).join(); + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); } catch (CancellationException | CompletionException e) { Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: " + e.getMessage()); @@ -164,12 +164,12 @@ public class RcsFeature extends ImsFeature { } } - private <T> T executeMethodAsyncForResult(FunctionalUtils.ThrowingSupplier<T> r, + private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) throws RemoteException { // call with a clean calling identity on the executor and wait indefinitely for the // future to return. CompletableFuture<T> future = CompletableFuture.supplyAsync( - () -> Binder.withCleanCallingIdentity(r), mExecutor); + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); try { return future.get(); } catch (ExecutionException | InterruptedException e) { diff --git a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java index a28d65c9abb6..306b9eeca6e3 100644 --- a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java +++ b/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java @@ -26,6 +26,7 @@ import android.os.RemoteException; import android.os.SystemProperties; import java.io.PrintWriter; +import java.util.function.Supplier; /** * This class provides various util functions @@ -71,4 +72,40 @@ public final class TelephonyUtils { if (resolveInfo.providerInfo != null) return resolveInfo.providerInfo; throw new IllegalStateException("Missing ComponentInfo!"); } + + /** + * Convenience method for running the provided action enclosed in + * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity} + * + * Any exception thrown by the given action will need to be handled by caller. + * + */ + public static void runWithCleanCallingIdentity( + @NonNull Runnable action) { + long callingIdentity = Binder.clearCallingIdentity(); + try { + action.run(); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + + /** + * Convenience method for running the provided action enclosed in + * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity} and return + * the result. + * + * Any exception thrown by the given action will need to be handled by caller. + * + */ + public static <T> T runWithCleanCallingIdentity( + @NonNull Supplier<T> action) { + long callingIdentity = Binder.clearCallingIdentity(); + try { + return action.get(); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } } diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml index d433df56bc00..d1da47f0f9d8 100644 --- a/tests/FlickerTests/AndroidTest.xml +++ b/tests/FlickerTests/AndroidTest.xml @@ -25,6 +25,6 @@ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> - <option name="clean-up" value="false" /> + <option name="clean-up" value="true" /> </metrics_collector> </configuration> diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index e30878157a26..ef8facec9752 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -118,7 +118,8 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // The failed packages should be the same as the registered ones to ensure registration is // done successfully @@ -135,7 +136,8 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION); raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), - new VersionedPackage(APP_B, VERSION_CODE))); + new VersionedPackage(APP_B, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // The failed packages should be the same as the registered ones to ensure registration is // done successfully @@ -151,7 +153,8 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); watchdog.unregisterHealthObserver(observer); raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // We should have no failed packages to ensure unregistration is done successfully assertThat(observer.mHealthCheckFailedPackages).isEmpty(); @@ -167,7 +170,8 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); watchdog.unregisterHealthObserver(observer2); raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // observer1 should receive failed packages as intended. assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A); @@ -183,7 +187,8 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); moveTimeForwardAndDispatch(SHORT_DURATION); raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // We should have no failed packages for the fatal failure is raised after expiration assertThat(observer.mHealthCheckFailedPackages).isEmpty(); @@ -199,7 +204,8 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), LONG_DURATION); moveTimeForwardAndDispatch(SHORT_DURATION); raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // We should have no failed packages for the fatal failure is raised after expiration assertThat(observer1.mHealthCheckFailedPackages).isEmpty(); @@ -226,7 +232,8 @@ public class PackageWatchdogTest { moveTimeForwardAndDispatch((SHORT_DURATION / 2) + 1); raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify that we receive failed packages as expected for APP_A not expired assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); @@ -252,7 +259,8 @@ public class PackageWatchdogTest { watchdog2.registerHealthObserver(observer2); raiseFatalFailureAndDispatch(watchdog2, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), - new VersionedPackage(APP_B, VERSION_CODE))); + new VersionedPackage(APP_B, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // We should receive failed packages as expected to ensure observers are persisted and // resumed correctly @@ -274,7 +282,8 @@ public class PackageWatchdogTest { // Then fail APP_A below the threshold for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers @@ -301,7 +310,8 @@ public class PackageWatchdogTest { // Then fail APP_C (not observed) above the threshold raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify that observers are not notified assertThat(observer1.mHealthCheckFailedPackages).isEmpty(); @@ -331,7 +341,8 @@ public class PackageWatchdogTest { // Then fail APP_A (different version) above the threshold raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, differentVersionCode))); + Arrays.asList(new VersionedPackage(APP_A, differentVersionCode)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify that observers are not notified assertThat(observer.mHealthCheckFailedPackages).isEmpty(); @@ -368,7 +379,8 @@ public class PackageWatchdogTest { Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), new VersionedPackage(APP_B, VERSION_CODE), new VersionedPackage(APP_C, VERSION_CODE), - new VersionedPackage(APP_D, VERSION_CODE))); + new VersionedPackage(APP_D, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify least impact observers are notifed of package failures List<String> observerNonePackages = observerNone.mMitigatedPackages; @@ -411,7 +423,8 @@ public class PackageWatchdogTest { // Then fail APP_A above the threshold raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify only observerFirst is notifed assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A); @@ -424,7 +437,8 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify only observerSecond is notifed cos it has least impact assertThat(observerSecond.mMitigatedPackages).containsExactly(APP_A); @@ -437,7 +451,8 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify only observerFirst is notifed cos it has the only action assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A); @@ -450,7 +465,8 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify no observer is notified cos no actions left assertThat(observerFirst.mMitigatedPackages).isEmpty(); @@ -474,7 +490,8 @@ public class PackageWatchdogTest { // Then fail APP_A above the threshold raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // Verify only one observer is notifed assertThat(observer1.mMitigatedPackages).containsExactly(APP_A); @@ -746,13 +763,15 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); // Fail APP_A below the threshold which should not trigger package failures for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } mTestLooper.dispatchAll(); assertThat(observer.mHealthCheckFailedPackages).isEmpty(); // One more to trigger the package failure - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); } @@ -773,20 +792,24 @@ public class PackageWatchdogTest { TestObserver observer = new TestObserver(OBSERVER_NAME_1); watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); // We shouldn't receive APP_A since the interval of 2 failures is greater than // DEFAULT_TRIGGER_FAILURE_DURATION_MS. assertThat(observer.mHealthCheckFailedPackages).isEmpty(); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS - 1); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); // We should receive APP_B since the interval of 2 failures is less than @@ -809,7 +832,8 @@ public class PackageWatchdogTest { // small timeouts. moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS - 100); raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // We should receive APP_A since the observer hasn't expired assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); @@ -827,7 +851,8 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1); moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1); raiseFatalFailureAndDispatch(watchdog, - Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); // We should receive nothing since the observer has expired assertThat(observer.mHealthCheckFailedPackages).isEmpty(); @@ -850,22 +875,59 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE); // Raise 2 failures at t=0 and t=900 respectively - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); moveTimeForwardAndDispatch(900); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); // Raise 2 failures at t=1100 moveTimeForwardAndDispatch(200); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); // We should receive APP_A since there are 3 failures within 1000ms window assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); } + /** Test that observers execute correctly for different failure reasons */ + @Test + public void testFailureReasons() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); + TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); + TestObserver observer3 = new TestObserver(OBSERVER_NAME_3); + TestObserver observer4 = new TestObserver(OBSERVER_NAME_4); + + watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); + watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION); + watchdog.startObservingHealth(observer3, Arrays.asList(APP_C), SHORT_DURATION); + watchdog.startObservingHealth(observer4, Arrays.asList(APP_D), SHORT_DURATION); + + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, + VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B, + VERSION_CODE)), PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_C, + VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH); + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_D, + VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); + + assertThat(observer1.getLastFailureReason()).isEqualTo( + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + assertThat(observer2.getLastFailureReason()).isEqualTo( + PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); + assertThat(observer3.getLastFailureReason()).isEqualTo( + PackageWatchdog.FAILURE_REASON_APP_CRASH); + assertThat(observer4.getLastFailureReason()).isEqualTo( + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() @@ -900,9 +962,9 @@ public class PackageWatchdogTest { /** Trigger package failures above the threshold. */ private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog, - List<VersionedPackage> packages) { + List<VersionedPackage> packages, int failureReason) { for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { - watchdog.onPackageFailure(packages); + watchdog.onPackageFailure(packages, failureReason); } mTestLooper.dispatchAll(); } @@ -936,6 +998,7 @@ public class PackageWatchdogTest { private static class TestObserver implements PackageHealthObserver { private final String mName; private int mImpact; + private int mLastFailureReason; final List<String> mHealthCheckFailedPackages = new ArrayList<>(); final List<String> mMitigatedPackages = new ArrayList<>(); @@ -954,14 +1017,19 @@ public class PackageWatchdogTest { return mImpact; } - public boolean execute(VersionedPackage versionedPackage) { + public boolean execute(VersionedPackage versionedPackage, int failureReason) { mMitigatedPackages.add(versionedPackage.getPackageName()); + mLastFailureReason = failureReason; return true; } public String getName() { return mName; } + + public int getLastFailureReason() { + return mLastFailureReason; + } } private static class TestController extends ExplicitHealthCheckController { diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp index 6c3bcf039be1..bb541fe2490b 100644 --- a/tools/aapt2/java/JavaClassGenerator.cpp +++ b/tools/aapt2/java/JavaClassGenerator.cpp @@ -304,9 +304,11 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res auto documentation_remove_iter = std::remove_if(documentation_attrs.begin(), documentation_attrs.end(), [&](StyleableAttr entry) -> bool { - StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment(); - return SkipSymbol(entry.symbol) || attr_comment_line.contains("@removed") - || attr_comment_line.contains("@hide"); + if (SkipSymbol(entry.symbol)) { + return true; + } + const StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment(); + return attr_comment_line.contains("@removed") || attr_comment_line.contains("@hide"); }); documentation_attrs.erase(documentation_remove_iter, documentation_attrs.end()); diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp index 4899fbd3b669..d45c4e7efb12 100644 --- a/tools/stats_log_api_gen/java_writer.cpp +++ b/tools/stats_log_api_gen/java_writer.cpp @@ -83,7 +83,7 @@ static int write_java_methods( // Print method body. string indent(""); if (DEFAULT_MODULE_NAME != moduleName) { - fprintf(out, " if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n"); + fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {\n"); indent = " "; } @@ -116,16 +116,19 @@ static int write_java_methods( fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(), argIndex); break; case JAVA_TYPE_BYTE_ARRAY: - fprintf(out, "%s builder.writeByteArray(arg%d);\n", - indent.c_str(), argIndex); + fprintf(out, "%s builder.writeByteArray(null == arg%d ? new byte[0] : arg%d);\n", + indent.c_str(), argIndex, argIndex); break; case JAVA_TYPE_ATTRIBUTION_CHAIN: { const char* uidName = attributionDecl.fields.front().name.c_str(); const char* tagName = attributionDecl.fields.back().name.c_str(); - fprintf(out, "%s builder.writeAttributionChain(%s, %s);\n", - indent.c_str(), uidName, tagName); + fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str()); + fprintf(out, "%s null == %s ? new int[0] : %s,\n", + indent.c_str(), uidName, uidName); + fprintf(out, "%s null == %s ? new String[0] : %s);\n", + indent.c_str(), tagName, tagName); break; } case JAVA_TYPE_KEY_VALUE_PAIR: diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index fccbcf7c101f..b52880e29e30 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -44,7 +44,6 @@ import android.net.wifi.WifiNetworkSuggestion; import android.os.Messenger; import android.os.ResultReceiver; import android.os.WorkSource; -import android.os.connectivity.WifiActivityEnergyInfo; /** * Interface that allows controlling and querying Wi-Fi connectivity. @@ -55,8 +54,6 @@ interface IWifiManager { long getSupportedFeatures(); - WifiActivityEnergyInfo reportActivityInfo(); - oneway void getWifiActivityEnergyInfoAsync(in IOnWifiActivityEnergyInfoListener listener); ParceledListSlice getConfiguredNetworks(String packageName, String featureId); diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index f3f873b4ead8..f728491d33cd 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -21,7 +21,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.net.NetworkInfo.DetailedState; -import android.net.NetworkUtils; +import android.net.shared.Inet4AddressUtils; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -708,7 +708,7 @@ public class WifiInfo implements Parcelable { public int getIpAddress() { int result = 0; if (mIpAddress instanceof Inet4Address) { - result = NetworkUtils.inetAddressToInt((Inet4Address)mIpAddress); + result = Inet4AddressUtils.inet4AddressToIntHTL((Inet4Address) mIpAddress); } return result; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 0108d5aa936c..86c398b770b7 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -2379,25 +2379,6 @@ public class WifiManager { } /** - * Return the record of {@link WifiActivityEnergyInfo} object that - * has the activity and energy info. This can be used to ascertain what - * the controller has been up to, since the last sample. - * - * @return a record with {@link WifiActivityEnergyInfo} or null if - * report is unavailable or unsupported - * @hide - */ - public WifiActivityEnergyInfo getControllerActivityEnergyInfo() { - try { - synchronized(this) { - return mService.reportActivityInfo(); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Interface for Wi-Fi activity energy info listener. Should be implemented by applications and * set when calling {@link WifiManager#getWifiActivityEnergyInfoAsync}. * diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index e78104d3da38..9fd29ae8d14a 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -23,12 +23,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.app.ActivityThread; import android.net.MacAddress; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Parcel; import android.os.Parcelable; -import android.os.Process; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -46,7 +44,6 @@ import java.util.Objects; * {@link WifiManager#addNetworkSuggestions(List)}. */ public final class WifiNetworkSuggestion implements Parcelable { - /** * Builder used to create {@link WifiNetworkSuggestion} objects. */ @@ -563,9 +560,7 @@ public final class WifiNetworkSuggestion implements Parcelable { mPasspointConfiguration, mIsAppInteractionRequired, mIsUserInteractionRequired, - mIsUserAllowed, - Process.myUid(), - ActivityThread.currentApplication().getApplicationContext().getOpPackageName()); + mIsUserAllowed); } } @@ -592,19 +587,6 @@ public final class WifiNetworkSuggestion implements Parcelable { * @hide */ public final boolean isUserInteractionRequired; - - /** - * The UID of the process initializing this network suggestion. - * @hide - */ - public final int suggestorUid; - - /** - * The package name of the process initializing this network suggestion. - * @hide - */ - public final String suggestorPackageName; - /** * Whether app share credential with the user, allow user use provided credential to * connect network manually. @@ -619,8 +601,6 @@ public final class WifiNetworkSuggestion implements Parcelable { this.isAppInteractionRequired = false; this.isUserInteractionRequired = false; this.isUserAllowedToManuallyConnect = true; - this.suggestorUid = -1; - this.suggestorPackageName = null; } /** @hide */ @@ -628,18 +608,14 @@ public final class WifiNetworkSuggestion implements Parcelable { @Nullable PasspointConfiguration passpointConfiguration, boolean isAppInteractionRequired, boolean isUserInteractionRequired, - boolean isUserAllowedToManuallyConnect, - int suggestorUid, @NonNull String suggestorPackageName) { + boolean isUserAllowedToManuallyConnect) { checkNotNull(networkConfiguration); - checkNotNull(suggestorPackageName); this.wifiConfiguration = networkConfiguration; this.passpointConfiguration = passpointConfiguration; this.isAppInteractionRequired = isAppInteractionRequired; this.isUserInteractionRequired = isUserInteractionRequired; this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect; - this.suggestorUid = suggestorUid; - this.suggestorPackageName = suggestorPackageName; } public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR = @@ -651,9 +627,7 @@ public final class WifiNetworkSuggestion implements Parcelable { in.readParcelable(null), // PasspointConfiguration in.readBoolean(), // isAppInteractionRequired in.readBoolean(), // isUserInteractionRequired - in.readBoolean(), // isSharedCredentialWithUser - in.readInt(), // suggestorUid - in.readString() // suggestorPackageName + in.readBoolean() // isSharedCredentialWithUser ); } @@ -675,15 +649,12 @@ public final class WifiNetworkSuggestion implements Parcelable { dest.writeBoolean(isAppInteractionRequired); dest.writeBoolean(isUserInteractionRequired); dest.writeBoolean(isUserAllowedToManuallyConnect); - dest.writeInt(suggestorUid); - dest.writeString(suggestorPackageName); } @Override public int hashCode() { return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID, - wifiConfiguration.allowedKeyManagement, wifiConfiguration.FQDN, - suggestorUid, suggestorPackageName); + wifiConfiguration.allowedKeyManagement, wifiConfiguration.FQDN); } /** @@ -706,23 +677,19 @@ public final class WifiNetworkSuggestion implements Parcelable { && TextUtils.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID) && Objects.equals(this.wifiConfiguration.allowedKeyManagement, lhs.wifiConfiguration.allowedKeyManagement) - && TextUtils.equals(this.wifiConfiguration.FQDN, lhs.wifiConfiguration.FQDN) - && this.suggestorUid == lhs.suggestorUid - && TextUtils.equals(this.suggestorPackageName, lhs.suggestorPackageName); + && TextUtils.equals(this.wifiConfiguration.FQDN, lhs.wifiConfiguration.FQDN); } @Override public String toString() { - StringBuilder sb = new StringBuilder("WifiNetworkSuggestion [") - .append(", SSID=").append(wifiConfiguration.SSID) + StringBuilder sb = new StringBuilder("WifiNetworkSuggestion[ ") + .append("SSID=").append(wifiConfiguration.SSID) .append(", BSSID=").append(wifiConfiguration.BSSID) .append(", FQDN=").append(wifiConfiguration.FQDN) .append(", isAppInteractionRequired=").append(isAppInteractionRequired) .append(", isUserInteractionRequired=").append(isUserInteractionRequired) .append(", isUserAllowedToManuallyConnect=").append(isUserAllowedToManuallyConnect) - .append(", suggestorUid=").append(suggestorUid) - .append(", suggestorPackageName=").append(suggestorPackageName) - .append("]"); + .append(" ]"); return sb.toString(); } } diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java index 1886b7ef4c8d..a8844c1d3812 100644 --- a/wifi/java/android/net/wifi/aware/PublishConfig.java +++ b/wifi/java/android/net/wifi/aware/PublishConfig.java @@ -19,11 +19,10 @@ package android.net.wifi.aware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.wifi.util.HexEncoding; import android.os.Parcel; import android.os.Parcelable; -import libcore.util.HexEncoding; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java index f0f758170bf2..76780f421af2 100644 --- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java +++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java @@ -19,11 +19,10 @@ package android.net.wifi.aware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.wifi.util.HexEncoding; import android.os.Parcel; import android.os.Parcelable; -import libcore.util.HexEncoding; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java index 5ec4c8b13ee8..c66733472d0e 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java @@ -17,12 +17,11 @@ package android.net.wifi.aware; import android.net.NetworkSpecifier; +import android.net.wifi.util.HexEncoding; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; -import libcore.util.HexEncoding; - import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 7b37d652426d..81bf81e40199 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -19,6 +19,7 @@ package android.net.wifi.aware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; @@ -26,6 +27,7 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkRequest; import android.net.NetworkSpecifier; +import android.net.wifi.util.HexEncoding; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -36,8 +38,6 @@ import android.os.Process; import android.os.RemoteException; import android.util.Log; -import libcore.util.HexEncoding; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -397,6 +397,17 @@ public class WifiAwareManager { } /** @hide */ + @RequiresPermission(android.Manifest.permission.NETWORK_STACK) + public void requestMacAddresses(int uid, List<Integer> peerIds, + IWifiAwareMacAddressProvider callback) { + try { + mService.requestMacAddresses(uid, peerIds, callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId, @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) { if (VDBG) { diff --git a/wifi/java/android/net/wifi/util/HexEncoding.java b/wifi/java/android/net/wifi/util/HexEncoding.java new file mode 100644 index 000000000000..9ebf947e2dc3 --- /dev/null +++ b/wifi/java/android/net/wifi/util/HexEncoding.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.util; + +/** + * Hexadecimal encoding where each byte is represented by two hexadecimal digits. + * + * Note: this is copied from {@link libcore.util.HexEncoding}. + * + * @hide + */ +public class HexEncoding { + + private static final char[] LOWER_CASE_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + private static final char[] UPPER_CASE_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + /** Hidden constructor to prevent instantiation. */ + private HexEncoding() {} + + /** + * Encodes the provided byte as a two-digit hexadecimal String value. + */ + public static String encodeToString(byte b, boolean upperCase) { + char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; + char[] buf = new char[2]; // We always want two digits. + buf[0] = digits[(b >> 4) & 0xf]; + buf[1] = digits[b & 0xf]; + return new String(buf, 0, 2); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static char[] encode(byte[] data) { + return encode(data, 0, data.length, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static char[] encode(byte[] data, boolean upperCase) { + return encode(data, 0, data.length, upperCase); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static char[] encode(byte[] data, int offset, int len) { + return encode(data, offset, len, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + private static char[] encode(byte[] data, int offset, int len, boolean upperCase) { + char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; + char[] result = new char[len * 2]; + for (int i = 0; i < len; i++) { + byte b = data[offset + i]; + int resultIndex = 2 * i; + result[resultIndex] = (digits[(b >> 4) & 0x0f]); + result[resultIndex + 1] = (digits[b & 0x0f]); + } + + return result; + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static String encodeToString(byte[] data) { + return encodeToString(data, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static String encodeToString(byte[] data, boolean upperCase) { + return new String(encode(data, upperCase)); + } + + /** + * Decodes the provided hexadecimal string into a byte array. Odd-length inputs + * are not allowed. + * + * Throws an {@code IllegalArgumentException} if the input is malformed. + */ + public static byte[] decode(String encoded) throws IllegalArgumentException { + return decode(encoded.toCharArray()); + } + + /** + * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} + * is {@code true} odd-length inputs are allowed and the first character is interpreted + * as the lower bits of the first result byte. + * + * Throws an {@code IllegalArgumentException} if the input is malformed. + */ + public static byte[] decode(String encoded, boolean allowSingleChar) + throws IllegalArgumentException { + return decode(encoded.toCharArray(), allowSingleChar); + } + + /** + * Decodes the provided hexadecimal string into a byte array. Odd-length inputs + * are not allowed. + * + * Throws an {@code IllegalArgumentException} if the input is malformed. + */ + public static byte[] decode(char[] encoded) throws IllegalArgumentException { + return decode(encoded, false); + } + + /** + * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} + * is {@code true} odd-length inputs are allowed and the first character is interpreted + * as the lower bits of the first result byte. + * + * Throws an {@code IllegalArgumentException} if the input is malformed. + */ + public static byte[] decode(char[] encoded, boolean allowSingleChar) + throws IllegalArgumentException { + int encodedLength = encoded.length; + int resultLengthBytes = (encodedLength + 1) / 2; + byte[] result = new byte[resultLengthBytes]; + + int resultOffset = 0; + int i = 0; + if (allowSingleChar) { + if ((encodedLength % 2) != 0) { + // Odd number of digits -- the first digit is the lower 4 bits of the first result + // byte. + result[resultOffset++] = (byte) toDigit(encoded, i); + i++; + } + } else { + if ((encodedLength % 2) != 0) { + throw new IllegalArgumentException("Invalid input length: " + encodedLength); + } + } + + for (; i < encodedLength; i += 2) { + result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); + } + + return result; + } + + private static int toDigit(char[] str, int offset) throws IllegalArgumentException { + // NOTE: that this isn't really a code point in the traditional sense, since we're + // just rejecting surrogate pairs outright. + int pseudoCodePoint = str[offset]; + + if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { + return pseudoCodePoint - '0'; + } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { + return 10 + (pseudoCodePoint - 'a'); + } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { + return 10 + (pseudoCodePoint - 'A'); + } + + throw new IllegalArgumentException("Illegal char: " + str[offset] + " at offset " + offset); + } +} diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java index 367cfa069e74..2c27037d29aa 100644 --- a/wifi/java/com/android/server/wifi/BaseWifiService.java +++ b/wifi/java/com/android/server/wifi/BaseWifiService.java @@ -76,11 +76,14 @@ public class BaseWifiService extends IWifiManager.Stub { throw new UnsupportedOperationException(); } - @Override + /** @deprecated use {@link #getWifiActivityEnergyInfoAsync} instead */ + @Deprecated public WifiActivityEnergyInfo reportActivityInfo() { throw new UnsupportedOperationException(); } + /** @deprecated use {@link #getWifiActivityEnergyInfoAsync} instead */ + @Deprecated public void requestActivityInfo(ResultReceiver result) { throw new UnsupportedOperationException(); } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index de4514997b1c..f92d38c982b8 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -161,7 +161,7 @@ public class WifiManagerTest { mRunnable.run(); } }; - mWifiActivityEnergyInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0, 0); + mWifiActivityEnergyInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0); } /** @@ -1708,18 +1708,6 @@ public class WifiManagerTest { } /** - * Test behavior of {@link WifiManager#getControllerActivityEnergyInfo()} - */ - @Test - public void testGetControllerActivityEnergyInfo() throws Exception { - WifiActivityEnergyInfo activityEnergyInfo = - new WifiActivityEnergyInfo(5, 3, 3, 5, 5, 5, 5); - when(mWifiService.reportActivityInfo()).thenReturn(activityEnergyInfo); - - assertEquals(activityEnergyInfo, mWifiManager.getControllerActivityEnergyInfo()); - } - - /** * Tests that passing a null Executor to {@link WifiManager#getWifiActivityEnergyInfoAsync} * throws an exception. */ diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index 8a5a0fd6805b..04aaa0bbcad0 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -22,7 +22,6 @@ import android.net.MacAddress; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.PasspointTestUtils; import android.os.Parcel; -import android.os.Process; import androidx.test.filters.SmallTest; @@ -35,8 +34,6 @@ import org.junit.Test; public class WifiNetworkSuggestionTest { private static final int TEST_UID = 45677; private static final int TEST_UID_OTHER = 45673; - private static final String TEST_PACKAGE_NAME = "com.test.packagename"; - private static final String TEST_PACKAGE_NAME_OTHER = "com.test.packagenameother"; private static final String TEST_SSID = "\"Test123\""; private static final String TEST_BSSID = "12:12:12:12:12:12"; private static final String TEST_SSID_1 = "\"Test1234\""; @@ -55,7 +52,6 @@ public class WifiNetworkSuggestionTest { .setIsAppInteractionRequired(true) .build(); - assertEquals(Process.myUid(), suggestion.suggestorUid); assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); assertTrue(suggestion.wifiConfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.NONE)); @@ -448,7 +444,7 @@ public class WifiNetworkSuggestionTest { configuration.BSSID = TEST_BSSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion( - configuration, null, false, true, true, TEST_UID, TEST_PACKAGE_NAME); + configuration, null, false, true, true); Parcel parcelW = Parcel.obtain(); suggestion.writeToParcel(parcelW, 0); @@ -515,16 +511,14 @@ public class WifiNetworkSuggestionTest { configuration.BSSID = TEST_BSSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, null, true, false, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration, null, true, false, true); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID; configuration1.BSSID = TEST_BSSID; configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration1, null, false, true, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration1, null, false, true, true); assertEquals(suggestion, suggestion1); assertEquals(suggestion.hashCode(), suggestion1.hashCode()); @@ -540,15 +534,13 @@ public class WifiNetworkSuggestionTest { configuration.SSID = TEST_SSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration, null, false, false, true); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID_1; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration1, null, false, false, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration1, null, false, false, true); assertNotEquals(suggestion, suggestion1); } @@ -564,15 +556,13 @@ public class WifiNetworkSuggestionTest { configuration.BSSID = TEST_BSSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration, null, false, false, true); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID; configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration1, null, false, false, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration1, null, false, false, true); assertNotEquals(suggestion, suggestion1); } @@ -587,57 +577,18 @@ public class WifiNetworkSuggestionTest { configuration.SSID = TEST_SSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration, null, false, false, true); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID; configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration1, null, false, false, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration1, null, false, false, true); assertNotEquals(suggestion, suggestion1); } /** - * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same - * SSID, BSSID and key mgmt, but different UID. - */ - @Test - public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() { - WifiConfiguration configuration = new WifiConfiguration(); - configuration.SSID = TEST_SSID; - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); - WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID, - TEST_PACKAGE_NAME); - - WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID_OTHER, - TEST_PACKAGE_NAME); - - assertNotEquals(suggestion, suggestion1); - } - - /** - * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same - * SSID, BSSID and key mgmt, but different package name. - */ - @Test - public void testWifiNetworkSuggestionEqualsFailsWhenPackageNameIsDifferent() { - WifiConfiguration configuration = new WifiConfiguration(); - configuration.SSID = TEST_SSID; - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); - WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion( - configuration, null, false, false, true, TEST_UID, TEST_PACKAGE_NAME); - - WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion( - configuration, null, false, false, true, TEST_UID, TEST_PACKAGE_NAME_OTHER); - - assertNotEquals(suggestion, suggestion1); - } - /** * Check NetworkSuggestion equals returns {@code true} for 2 Passpoint network suggestions with * same FQDN. */ diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 3483ff8a4664..200c0e31cb22 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -36,6 +36,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.net.MacAddress; import android.net.wifi.RttManager; +import android.net.wifi.util.HexEncoding; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -44,8 +45,6 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; -import libcore.util.HexEncoding; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; diff --git a/wifi/tests/src/android/net/wifi/util/HexEncodingTest.java b/wifi/tests/src/android/net/wifi/util/HexEncodingTest.java new file mode 100644 index 000000000000..0d751389e244 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/util/HexEncodingTest.java @@ -0,0 +1,130 @@ +/* + * 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.net.wifi.util; + +import static android.net.wifi.util.HexEncoding.decode; +import static android.net.wifi.util.HexEncoding.encode; +import static android.net.wifi.util.HexEncoding.encodeToString; + +import junit.framework.TestCase; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Locale; + +/** Copied from {@link libcore.libcore.util.HexEncodingTest}. */ +public class HexEncodingTest extends TestCase { + + public void testEncodeByte() { + Object[][] testCases = new Object[][]{ + {0x01, "01"}, + {0x09, "09"}, + {0x0A, "0A"}, + {0x0F, "0F"}, + {0x10, "10"}, + {0x1F, "1F"}, + {0x20, "20"}, + {0x7F, "7F"}, + {0x80, "80"}, + {0xFF, "FF"}, + }; + for (Object[] testCase : testCases) { + Number toEncode = (Number) testCase[0]; + String expected = (String) testCase[1]; + + String actualUpper = encodeToString(toEncode.byteValue(), true /* upperCase */); + assertEquals(upper(expected), actualUpper); + + String actualLower = encodeToString(toEncode.byteValue(), false /* upperCase */); + assertEquals(lower(expected), actualLower); + } + } + + public void testEncodeBytes() { + Object[][] testCases = new Object[][]{ + {"avocados".getBytes(StandardCharsets.UTF_8), "61766F6361646F73"}, + }; + + for (Object[] testCase : testCases) { + byte[] bytes = (byte[]) testCase[0]; + String encodedLower = lower((String) testCase[1]); + String encodedUpper = upper((String) testCase[1]); + + assertArraysEqual(encodedUpper.toCharArray(), encode(bytes)); + assertArraysEqual(encodedUpper.toCharArray(), encode(bytes, true /* upperCase */)); + assertArraysEqual(encodedLower.toCharArray(), encode(bytes, false /* upperCase */)); + + assertArraysEqual(bytes, decode(encode(bytes), false /* allowSingleChar */)); + + // Make sure we can handle lower case hex encodings as well. + assertArraysEqual(bytes, + decode(encodedLower.toCharArray(), false /* allowSingleChar */)); + } + } + + public void testDecode_allow4Bit() { + assertArraysEqual(new byte[]{6}, decode("6".toCharArray(), true)); + assertArraysEqual(new byte[]{6, 0x76}, decode("676".toCharArray(), true)); + } + + public void testDecode_disallow4Bit() { + try { + decode("676".toCharArray(), false /* allowSingleChar */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testDecode_invalid() { + try { + decode("DEADBARD".toCharArray(), false /* allowSingleChar */); + fail(); + } catch (IllegalArgumentException expected) { + } + + // This demonstrates a difference in behaviour from apache commons : apache + // commons uses Character.isDigit and would successfully decode a string with + // arabic and devanagari characters. + try { + decode("६१٧٥٥F6361646F73".toCharArray(), false /* allowSingleChar */); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + decode("#%6361646F73".toCharArray(), false /* allowSingleChar */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + private static void assertArraysEqual(char[] lhs, char[] rhs) { + assertEquals(new String(lhs), new String(rhs)); + } + + private static void assertArraysEqual(byte[] lhs, byte[] rhs) { + assertEquals(Arrays.toString(lhs), Arrays.toString(rhs)); + } + + private static String lower(String string) { + return string.toLowerCase(Locale.ROOT); + } + + private static String upper(String string) { + return string.toUpperCase(Locale.ROOT); + } +} |