diff options
488 files changed, 11342 insertions, 4514 deletions
diff --git a/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java new file mode 100644 index 000000000000..760ae12bcc07 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.os; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ParcelObtainPerfTest { + private static final int ITERATIONS = 1_000_000; + + @Test + public void timeContention_01() throws Exception { + timeContention(1); + } + + @Test + public void timeContention_04() throws Exception { + timeContention(4); + } + + @Test + public void timeContention_16() throws Exception { + timeContention(16); + } + + private static void timeContention(int numThreads) throws Exception { + final long start = SystemClock.elapsedRealtime(); + { + final ObtainThread[] threads = new ObtainThread[numThreads]; + for (int i = 0; i < numThreads; i++) { + final ObtainThread thread = new ObtainThread(ITERATIONS / numThreads); + thread.start(); + threads[i] = thread; + } + for (int i = 0; i < numThreads; i++) { + threads[i].join(); + } + } + final long duration = SystemClock.elapsedRealtime() - start; + + final Bundle results = new Bundle(); + results.putLong("duration", duration); + InstrumentationRegistry.getInstrumentation().sendStatus(0, results); + } + + public static class ObtainThread extends Thread { + public int iterations; + + public ObtainThread(int iterations) { + this.iterations = iterations; + } + + @Override + public void run() { + while (iterations-- > 0) { + final Parcel data = Parcel.obtain(); + final Parcel reply = Parcel.obtain(); + try { + data.writeInt(32); + reply.writeInt(32); + } finally { + reply.recycle(); + data.recycle(); + } + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java index 4db9262f7fe3..be2f9d72663e 100644 --- a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java +++ b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java @@ -159,21 +159,6 @@ public class ParcelPerfTest { } @Test - public void timeObtainRecycle() { - // Use up the pooled instances. - // A lot bigger than the actual size but in case someone increased it. - final int POOL_SIZE = 100; - for (int i = 0; i < POOL_SIZE; i++) { - Parcel.obtain(); - } - - final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { - Parcel.obtain().recycle(); - } - } - - @Test public void timeWriteException() { timeWriteException(false); } diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index d8386b5f1153..59915e145c49 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -2178,11 +2178,10 @@ public class DeviceIdleController extends SystemService if (getContext().getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) { - mLocationRequest = LocationRequest.create() + mLocationRequest = new LocationRequest.Builder(/*intervalMillis=*/ 0) .setQuality(LocationRequest.ACCURACY_FINE) - .setInterval(0) - .setFastestInterval(0) - .setNumUpdates(1); + .setMaxUpdates(1) + .build(); } mConstraintController = mInjector.getConstraintController( diff --git a/api/current.txt b/api/current.txt index 9b9e258f23a2..afd93c363e60 100644 --- a/api/current.txt +++ b/api/current.txt @@ -23925,6 +23925,7 @@ package android.location { method @NonNull public java.util.List<java.lang.String> getAllProviders(); method @Nullable public String getBestProvider(@NonNull android.location.Criteria, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); method @NonNull public android.location.GnssCapabilities getGnssCapabilities(); method @Nullable public String getGnssHardwareModelName(); method public int getGnssYearOfHardware(); @@ -23959,6 +23960,8 @@ package android.location { method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, long, float, @NonNull android.app.PendingIntent); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull android.app.PendingIntent); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, @NonNull android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, @NonNull android.location.LocationRequest, @NonNull android.app.PendingIntent); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.location.LocationListener, @Nullable android.os.Looper); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull android.location.Criteria, @NonNull android.location.LocationListener, @Nullable android.os.Looper); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.app.PendingIntent); @@ -24002,6 +24005,30 @@ package android.location { field @Deprecated public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1 } + public final class LocationRequest implements android.os.Parcelable { + method public int describeContents(); + method public long getDurationMillis(); + method public long getIntervalMillis(); + method public int getMaxUpdates(); + method public float getMinUpdateDistanceMeters(); + method public long getMinUpdateIntervalMillis(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR; + field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL + } + + public static final class LocationRequest.Builder { + ctor public LocationRequest.Builder(long); + ctor public LocationRequest.Builder(@NonNull android.location.LocationRequest); + method @NonNull public android.location.LocationRequest build(); + method @NonNull public android.location.LocationRequest.Builder clearMinUpdateIntervalMillis(); + method @NonNull public android.location.LocationRequest.Builder setDurationMillis(@IntRange(from=1) long); + method @NonNull public android.location.LocationRequest.Builder setIntervalMillis(@IntRange(from=0) long); + method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int); + method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float); + method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long); + } + public interface OnNmeaMessageListener { method public void onNmeaMessage(String, long); } diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index c12d897b9d72..4f0e2cd3c768 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -66,6 +66,8 @@ package android.media.session { method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + field public static final int RESULT_MEDIA_KEY_HANDLED = 1; // 0x1 + field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0 } } diff --git a/api/system-current.txt b/api/system-current.txt index 4134711035e5..308da7ce3063 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -671,6 +671,9 @@ package android.app { method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; } public final class RuntimeAppOpAccessMessage implements android.os.Parcelable { @@ -4075,7 +4078,7 @@ package android.location { public class LocationManager { method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); method @Nullable public String getExtraLocationControllerPackage(); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize(); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections); @@ -4086,9 +4089,9 @@ package android.location { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.LOCATION_HARDWARE}) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle); @@ -4097,42 +4100,49 @@ package android.location { } public final class LocationRequest implements android.os.Parcelable { - method @NonNull public static android.location.LocationRequest create(); - method @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean); - method @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean); - method public int describeContents(); + method @Deprecated @NonNull public static android.location.LocationRequest create(); + method @Deprecated @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean); + method @Deprecated @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean); method @Deprecated public long getExpireAt(); - method public long getExpireIn(); - method public long getFastestInterval(); - method public boolean getHideFromAppOps(); - method public long getInterval(); - method public int getNumUpdates(); - method @NonNull public String getProvider(); + method @Deprecated public long getExpireIn(); + method @Deprecated public long getFastestInterval(); + method @Deprecated public boolean getHideFromAppOps(); + method @Deprecated public long getInterval(); + method @Deprecated public int getNumUpdates(); + method @Deprecated @NonNull public String getProvider(); method public int getQuality(); - method public float getSmallestDisplacement(); + method @Deprecated public float getSmallestDisplacement(); method @Nullable public android.os.WorkSource getWorkSource(); + method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); - method public boolean isLowPowerMode(); + method public boolean isLowPower(); + method @Deprecated public boolean isLowPowerMode(); method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long); - method @NonNull public android.location.LocationRequest setExpireIn(long); - method @NonNull public android.location.LocationRequest setFastestInterval(long); - method public void setHideFromAppOps(boolean); - method @NonNull public android.location.LocationRequest setInterval(long); - method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean); - method @NonNull public android.location.LocationRequest setLowPowerMode(boolean); - method @NonNull public android.location.LocationRequest setNumUpdates(int); - method @NonNull public android.location.LocationRequest setProvider(@NonNull String); - method @NonNull public android.location.LocationRequest setQuality(int); - method @NonNull public android.location.LocationRequest setSmallestDisplacement(float); - method public void setWorkSource(@Nullable android.os.WorkSource); - method public void writeToParcel(android.os.Parcel, int); + method @Deprecated @NonNull public android.location.LocationRequest setExpireIn(long); + method @Deprecated @NonNull public android.location.LocationRequest setFastestInterval(long); + method @Deprecated public void setHideFromAppOps(boolean); + method @Deprecated @NonNull public android.location.LocationRequest setInterval(long); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean); + method @Deprecated @NonNull public android.location.LocationRequest setLowPowerMode(boolean); + method @Deprecated @NonNull public android.location.LocationRequest setNumUpdates(int); + method @Deprecated @NonNull public android.location.LocationRequest setProvider(@NonNull String); + method @Deprecated @NonNull public android.location.LocationRequest setQuality(int); + method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float); + method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource); field public static final int ACCURACY_BLOCK = 102; // 0x66 field public static final int ACCURACY_CITY = 104; // 0x68 field public static final int ACCURACY_FINE = 100; // 0x64 - field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR; field public static final int POWER_HIGH = 203; // 0xcb field public static final int POWER_LOW = 201; // 0xc9 - field public static final int POWER_NONE = 200; // 0xc8 + field @Deprecated public static final int POWER_NONE = 200; // 0xc8 + } + + public static final class LocationRequest.Builder { + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean); + method @NonNull public android.location.LocationRequest.Builder setQuality(int); + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource); } } @@ -11521,6 +11531,7 @@ package android.telephony.data { method public int getCause(); method @NonNull public java.util.List<java.net.InetAddress> getDnsAddresses(); method @NonNull public java.util.List<java.net.InetAddress> getGatewayAddresses(); + method public int getHandoverFailureMode(); method public int getId(); method @NonNull public String getInterfaceName(); method public int getLinkStatus(); @@ -11532,6 +11543,11 @@ package android.telephony.data { method public int getSuggestedRetryTime(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; + field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2; // 0x2 + field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1 + field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3 + field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4 + field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0 field public static final int LINK_STATUS_ACTIVE = 2; // 0x2 field public static final int LINK_STATUS_DORMANT = 1; // 0x1 field public static final int LINK_STATUS_INACTIVE = 0; // 0x0 @@ -11545,6 +11561,7 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setCause(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setDnsAddresses(@NonNull java.util.List<java.net.InetAddress>); method @NonNull public android.telephony.data.DataCallResponse.Builder setGatewayAddresses(@NonNull java.util.List<java.net.InetAddress>); + method @NonNull public android.telephony.data.DataCallResponse.Builder setHandoverFailureMode(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setId(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@NonNull String); method @NonNull public android.telephony.data.DataCallResponse.Builder setLinkStatus(int); diff --git a/api/test-current.txt b/api/test-current.txt index de2919b8936a..30972b65a1fd 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1686,45 +1686,34 @@ package android.location { public class LocationManager { method @NonNull public String[] getBackgroundThrottlingWhitelist(); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); method @NonNull public String[] getIgnoreSettingsWhitelist(); method @Deprecated @Nullable @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public java.util.List<java.lang.String> getProviderPackages(@NonNull String); method @NonNull public java.util.List<android.location.LocationRequest> getTestProviderCurrentRequests(String); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle); field public static final String FUSED_PROVIDER = "fused"; } public final class LocationRequest implements android.os.Parcelable { - method @NonNull public static android.location.LocationRequest create(); - method public int describeContents(); - method @Deprecated public long getExpireAt(); - method public long getExpireIn(); - method public long getFastestInterval(); - method public long getInterval(); - method public int getNumUpdates(); - method public int getQuality(); + method @Nullable public android.os.WorkSource getWorkSource(); + method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); - method public boolean isLowPowerMode(); - method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long); - method @NonNull public android.location.LocationRequest setExpireIn(long); - method @NonNull public android.location.LocationRequest setFastestInterval(long); - method @NonNull public android.location.LocationRequest setInterval(long); - method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean); - method @NonNull public android.location.LocationRequest setLowPowerMode(boolean); - method @NonNull public android.location.LocationRequest setNumUpdates(int); - method @NonNull public android.location.LocationRequest setProvider(@NonNull String); - method @NonNull public android.location.LocationRequest setQuality(int); - method public void writeToParcel(android.os.Parcel, int); + method public boolean isLowPower(); field public static final int ACCURACY_BLOCK = 102; // 0x66 field public static final int ACCURACY_CITY = 104; // 0x68 field public static final int ACCURACY_FINE = 100; // 0x64 - field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR; field public static final int POWER_HIGH = 203; // 0xcb field public static final int POWER_LOW = 201; // 0xc9 - field public static final int POWER_NONE = 200; // 0xc8 + } + + public static final class LocationRequest.Builder { + method @NonNull @RequiresPermission("android.permission.UPDATE_APP_OPS_STATS") public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource); } } diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index dec4a567fc81..5c08704a6623 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -30,8 +30,9 @@ #include <binder/ProcessState.h> -#include <gui/SurfaceComposerClient.h> #include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SyncScreenCaptureListener.h> #include <ui/DisplayInfo.h> #include <ui/GraphicTypes.h> @@ -181,13 +182,18 @@ int main(int argc, char** argv) ProcessState::self()->setThreadPoolMaxThreadCount(0); ProcessState::self()->startThreadPool(); - ScreenCaptureResults captureResults; - status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults); + sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener); if (result != NO_ERROR) { close(fd); return 1; } + ScreenCaptureResults captureResults = captureListener->waitForResults(); + if (captureResults.result != NO_ERROR) { + close(fd); + return 1; + } ui::Dataspace dataspace = captureResults.capturedDataspace; sp<GraphicBuffer> buffer = captureResults.buffer; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 94c2305e36f6..fc1455be29b1 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3764,6 +3764,19 @@ message AppStartOccurred { // The reason why the package was optimized. optional int32 package_optimization_compilation_reason = 15; + + enum SourceType { + UNAVAILABLE = 0; + LAUNCHER = 1; + NOTIFICATION = 2; + LOCKSCREEN = 3; + } + // The type of the startup source. + optional SourceType source_type = 16; + + // The time from the startup source to the beginning of handling the startup event. + // -1 means not available. + optional int32 source_event_delay_millis = 17; } message AppStartCanceled { @@ -3808,6 +3821,19 @@ message AppStartFullyDrawn { // App startup time (until call to Activity#reportFullyDrawn()). optional int64 app_startup_time_millis = 6; + + enum SourceType { + UNAVAILABLE = 0; + LAUNCHER = 1; + NOTIFICATION = 2; + LOCKSCREEN = 3; + } + // The type of the startup source. + optional SourceType source_type = 7; + + // The time from the startup source to the beginning of handling the startup event. + // -1 means not available. + optional int32 source_event_delay_millis = 8; } /** @@ -4015,6 +4041,17 @@ message LmkStateChanged { * system/core/lmkd/lmkd.c */ message LmkKillOccurred { + enum Reason { + UNKNOWN = 0; + PRESSURE_AFTER_KILL = 1; + NOT_RESPONDING = 2; + LOW_SWAP_AND_THRASHING = 3; + LOW_MEM_AND_SWAP = 4; + LOW_MEM_AND_THRASHING = 5; + DIRECT_RECL_AND_THRASHING = 6; + LOW_MEM_AND_SWAP_UTIL = 7; + } + // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; @@ -4044,6 +4081,15 @@ message LmkKillOccurred { // Min oom adj score considered by lmkd. optional int32 min_oom_score = 10; + + // Free physical memory on device at LMK time. + optional int32 free_mem_kb = 11; + + // Free swap on device at LMK time. + optional int32 free_swap_kb = 12; + + // What triggered the LMK event. + optional Reason reason = 13; } /* diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java index 57c108326706..4b4ef002ae06 100644 --- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java +++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java @@ -572,7 +572,7 @@ public class ChooseTypeAndAccountActivity extends Activity } /** - * Returns a set of whitelisted accounts given by the intent or null if none specified by the + * Returns a set of allowlisted accounts given by the intent or null if none specified by the * intent. */ private Set<Account> getAllowableAccountSet(final Intent intent) { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 5c4951e23ea2..8c0b4387c27f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -8341,7 +8341,7 @@ public class Activity extends ContextThemeWrapper * * <p>If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns {@code true} * for this component, the current task will be launched directly into LockTask mode. Only apps - * whitelisted by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])} can + * allowlisted by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])} can * be launched while LockTask mode is active. The user will not be able to leave this mode * until this activity calls {@link #stopLockTask()}. Calling this method while the device is * already in LockTask mode has no effect. @@ -8373,7 +8373,7 @@ public class Activity extends ContextThemeWrapper * <p><strong>Note:</strong> If the device is in LockTask mode that is not initially started * by this activity, then calling this method will not terminate the LockTask mode, but only * finish its own task. The device will remain in LockTask mode, until the activity which - * started the LockTask mode calls this method, or until its whitelist authorization is revoked + * started the LockTask mode calls this method, or until its allowlist authorization is revoked * by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])}. * * @see #startLockTask() diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5aecb61b62b1..70ca49d67930 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4725,9 +4725,9 @@ public class ActivityManager { } /** - * Get packages of bugreport-whitelisted apps to handle a bug report. + * Get packages of bugreport-allowlisted apps to handle a bug report. * - * @return packages of bugreport-whitelisted apps to handle a bug report. + * @return packages of bugreport-allowlisted apps to handle a bug report. * @hide */ public List<String> getBugreportWhitelistedPackages() { diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 84a6b43e7175..a61159a23c2c 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -37,6 +38,7 @@ import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; +import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; @@ -54,6 +56,8 @@ import android.view.ViewGroup; import android.view.Window; import android.window.WindowContainerToken; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; /** @@ -290,6 +294,9 @@ public class ActivityOptions { private static final String KEY_EXIT_COORDINATOR_INDEX = "android:activity.exitCoordinatorIndex"; + /** See {@link SourceInfo}. */ + private static final String KEY_SOURCE_INFO = "android.activity.sourceInfo"; + private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport"; private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint"; @@ -369,6 +376,7 @@ public class ActivityOptions { private boolean mAvoidMoveToFront; private boolean mFreezeRecentTasksReordering; private AppTransitionAnimationSpec mAnimSpecs[]; + private SourceInfo mSourceInfo; private int mRotationAnimationHint = -1; private Bundle mAppVerificationBundle; private IAppTransitionAnimationSpecsFuture mSpecsFuture; @@ -1055,6 +1063,7 @@ public class ActivityOptions { mAnimationFinishedListener = IRemoteCallback.Stub.asInterface( opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER)); } + mSourceInfo = opts.getParcelable(KEY_SOURCE_INFO); mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT, -1); mAppVerificationBundle = opts.getBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE); if (opts.containsKey(KEY_SPECS_FUTURE)) { @@ -1696,6 +1705,9 @@ public class ActivityOptions { if (mSpecsFuture != null) { b.putBinder(KEY_SPECS_FUTURE, mSpecsFuture.asBinder()); } + if (mSourceInfo != null) { + b.putParcelable(KEY_SOURCE_INFO, mSourceInfo); + } if (mRotationAnimationHint != -1) { b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint); } @@ -1737,6 +1749,28 @@ public class ActivityOptions { } /** + * Returns the launch source information set by {@link #setSourceInfo}. + * @hide + */ + public @Nullable SourceInfo getSourceInfo() { + return mSourceInfo; + } + + /** + * Sets the source information of the launch event. + * + * @param type The type of the startup source. + * @param uptimeMillis The event time of startup source in milliseconds since boot, not + * including sleep (e.g. from {@link android.view.MotionEvent#getEventTime} + * or {@link android.os.SystemClock#uptimeMillis}). + * @see SourceInfo + * @hide + */ + public void setSourceInfo(@SourceInfo.SourceType int type, long uptimeMillis) { + mSourceInfo = new SourceInfo(type, uptimeMillis); + } + + /** * Return the filtered options only meant to be seen by the target activity itself * @hide */ @@ -1863,4 +1897,58 @@ public class ActivityOptions { } } } + + /** + * The information about the source of activity launch. E.g. describe an activity is launched + * from launcher by receiving a motion event with a timestamp. + * @hide + */ + public static class SourceInfo implements Parcelable { + /** Launched from launcher. */ + public static final int TYPE_LAUNCHER = 1; + /** Launched from notification. */ + public static final int TYPE_NOTIFICATION = 2; + /** Launched from lockscreen, including notification while the device is locked. */ + public static final int TYPE_LOCKSCREEN = 3; + + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + TYPE_LAUNCHER, + TYPE_NOTIFICATION, + TYPE_LOCKSCREEN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SourceType {} + + /** The type of the startup source. */ + public final @SourceType int type; + + /** The timestamp (uptime based) of the source to launch activity. */ + public final long eventTimeMs; + + SourceInfo(@SourceType int srcType, long uptimeMillis) { + type = srcType; + eventTimeMs = uptimeMillis; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(type); + dest.writeLong(eventTimeMs); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<SourceInfo> CREATOR = new Creator<SourceInfo>() { + public SourceInfo createFromParcel(Parcel in) { + return new SourceInfo(in.readInt(), in.readLong()); + } + + public SourceInfo[] newArray(int size) { + return new SourceInfo[size]; + } + }; + } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 763ce6c6fd17..0fd0e02a224a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4074,7 +4074,7 @@ public final class ActivityThread extends ClientTransactionHandler { private void handleCreateBackupAgent(CreateBackupAgentData data) { if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data); - // Sanity check the requested target package's uid against ours + // Validity check the requested target package's uid against ours try { PackageInfo requestedPackage = getPackageManager().getPackageInfo( data.appInfo.packageName, 0, UserHandle.myUserId()); diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 3cb6293f0706..098d090b5e30 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -133,7 +133,7 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd } mSurfaceView = new SurfaceView(context, null, 0, 0, disableSurfaceViewBackgroundLayer); // Since ActivityView#getAlpha has been overridden, we should use parent class's alpha - // as master to synchronize surface view's alpha value. + // as authoritative to synchronize surface view's alpha value. mSurfaceView.setAlpha(super.getAlpha()); mSurfaceView.setUseAlpha(); mSurfaceCallback = new SurfaceCallback(); diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md index 536653ef9daa..b827a0dbbb76 100644 --- a/core/java/android/app/AppOps.md +++ b/core/java/android/app/AppOps.md @@ -53,7 +53,7 @@ To control access the app-op can be set to: : Allow the access but only if the app is currently in the [foreground](#foreground) `MODE_IGNORED` -: Don't allow the access, i.e. don't perform the requested action or return dummy data +: Don't allow the access, i.e. don't perform the requested action or return placeholder data `MODE_ERRORED` : Throw a `SecurityException` on access. This can be suppressed by using a `...noThrow` method to diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 7087b60d75dd..167b5a8029c0 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -115,8 +115,8 @@ import java.util.function.Supplier; * <dt>{@link #MODE_ALLOWED} * <dd>Allow the access * <dt>{@link #MODE_IGNORED} - * <dd>Don't allow the access, i.e. don't perform the requested action or return no or dummy - * data + * <dd>Don't allow the access, i.e. don't perform the requested action or return no or + * placeholder data * <dt>{@link #MODE_ERRORED} * <dd>Throw a {@link SecurityException} on access. This can be suppressed by using a * {@code ...noThrow} method to check the mode @@ -135,7 +135,7 @@ import java.util.function.Supplier; * <p>Each platform defined runtime permission (beside background modifiers) has an associated app * op which is used for tracking but also to allow for silent failures. I.e. if the runtime * permission is denied the caller gets a {@link SecurityException}, but if the permission is - * granted and the app-op is {@link #MODE_IGNORED} then the callers gets dummy behavior, e.g. + * granted and the app-op is {@link #MODE_IGNORED} then the callers gets placeholder behavior, e.g. * location callbacks would not happen. * * <h3>App-op permissions</h3> @@ -1964,7 +1964,7 @@ public class AppOpsManager { null, // no permission for writing clipboard null, // no permission for taking media buttons null, // no permission for taking audio focus - null, // no permission for changing master volume + null, // no permission for changing global volume null, // no permission for changing voice volume null, // no permission for changing ring volume null, // no permission for changing media volume @@ -6525,7 +6525,7 @@ public class AppOpsManager { * Retrieve current operation state for all applications. * * The mode of the ops returned are set for the package but may not reflect their effective - * state due to UID policy or because it's controlled by a different master op. + * state due to UID policy or because it's controlled by a different global op. * * Use {@link #unsafeCheckOp(String, int, String)}} or * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. @@ -6549,7 +6549,7 @@ public class AppOpsManager { * Retrieve current operation state for all applications. * * The mode of the ops returned are set for the package but may not reflect their effective - * state due to UID policy or because it's controlled by a different master op. + * state due to UID policy or because it's controlled by a different global op. * * Use {@link #unsafeCheckOp(String, int, String)}} or * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. @@ -6571,7 +6571,7 @@ public class AppOpsManager { * Retrieve current operation state for one application. * * The mode of the ops returned are set for the package but may not reflect their effective - * state due to UID policy or because it's controlled by a different master op. + * state due to UID policy or because it's controlled by a different global op. * * Use {@link #unsafeCheckOp(String, int, String)}} or * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. @@ -6604,7 +6604,7 @@ public class AppOpsManager { * package must match. * * The mode of the ops returned are set for the package but may not reflect their effective - * state due to UID policy or because it's controlled by a different master op. + * state due to UID policy or because it's controlled by a different global op. * * Use {@link #unsafeCheckOp(String, int, String)}} or * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 161e2ad06ec0..3789a44e1d55 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -36,7 +36,7 @@ public class BroadcastOptions { private boolean mAllowBackgroundActivityStarts; /** - * How long to temporarily put an app on the power whitelist when executing this broadcast + * How long to temporarily put an app on the power allowlist when executing this broadcast * to it. */ static final String KEY_TEMPORARY_APP_WHITELIST_DURATION @@ -87,8 +87,8 @@ public class BroadcastOptions { /** * Set a duration for which the system should temporary place an application on the - * power whitelist when this broadcast is being delivered to it. - * @param duration The duration in milliseconds; 0 means to not place on whitelist. + * power allowlist when this broadcast is being delivered to it. + * @param duration The duration in milliseconds; 0 means to not place on allowlist. */ @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long duration) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 1ff48f6baa6a..0d682d64fb08 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -953,7 +953,7 @@ public class Notification implements Parcelable public ArraySet<PendingIntent> allPendingIntents; /** - * Token identifying the notification that is applying doze/bgcheck whitelisting to the + * Token identifying the notification that is applying doze/bgcheck allowlisting to the * pending intents inside of it, so only those will get the behavior. * * @hide diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 0627bc855934..fe8936654aa4 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -19,6 +19,7 @@ package android.app; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -131,6 +132,73 @@ public class NotificationManager { "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED"; /** + * Activity action: Toggle notification panel of the specified handler. + * + * <p><strong>Important:</strong>You must protect the activity that handles this action with + * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only + * the SystemUI can launch this activity. Activities that are not properly protected will not + * be launched. + * + * <p class="note">This is currently only used on TV to allow a system app to handle the + * notification panel. The package handling the notification panel has to be specified by + * config_notificationHandlerPackage in values/config.xml. + * + * Input: nothing + * Output: nothing + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = + "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; + + /** + * Activity action: Open notification panel of the specified handler. + * + * <p><strong>Important:</strong>You must protect the activity that handles this action with + * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only + * the SystemUI can launch this activity. Activities that are not properly protected will + * not be launched. + * + * <p class="note"> This is currently only used on TV to allow a system app to handle the + * notification panel. The package handling the notification panel has to be specified by + * config_notificationHandlerPackage in values/config.xml. + * + * Input: nothing + * Output: nothing + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = + "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; + + /** + * Intent that is broadcast when the notification panel of the specified handler is to be + * closed. + * + * <p><strong>Important:</strong>You should protect the receiver that handles this action with + * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only + * the SystemUI can send this broadcast to the notification handler. + * + * <p class="note"> This is currently only used on TV to allow a system app to handle the + * notification panel. The package handling the notification panel has to be specified by + * config_notificationHandlerPackage in values/config.xml. This is a protected intent that can + * only be sent by the system. + * + * Input: nothing. + * Output: nothing. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = + "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; + + /** * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} containing the id of the * {@link NotificationChannel} which has a new blocked state. * diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 6c6c04e4e975..3e3a956c9788 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -168,8 +168,8 @@ import java.util.concurrent.atomic.AtomicLong; * this local case, there's no IPC, so use of the cache is (depending on exact * circumstance) unnecessary. * - * For security, there is a whitelist of processes that are allowed to invalidate a cache. - * The whitelist includes normal runtime processes but does not include test processes. + * For security, there is a allowlist of processes that are allowed to invalidate a cache. + * The allowlist includes normal runtime processes but does not include test processes. * Test processes must call {@code PropertyInvalidatedCache.disableForTestMode()} to disable * all cache activity in that process. * diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 95f55ab2fde9..dcb53505227a 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -521,7 +521,7 @@ public class SearchManager /** * This means that context is voice, and therefore the SearchDialog should - * continue showing the microphone until the user indicates that he/she does + * continue showing the microphone until the user indicates that they do * not want to re-speak (e.g. by typing). * * @hide diff --git a/core/java/android/app/SearchableInfo.java b/core/java/android/app/SearchableInfo.java index 83eb2ee1da14..5388282a1b46 100644 --- a/core/java/android/app/SearchableInfo.java +++ b/core/java/android/app/SearchableInfo.java @@ -418,7 +418,7 @@ public final class SearchableInfo implements Parcelable { com.android.internal.R.styleable.SearchableActionKey_suggestActionMsgColumn); a.recycle(); - // sanity check. + // validity check. if (mKeyCode == 0) { throw new IllegalArgumentException("No keycode."); } else if ((mQueryActionMsg == null) && diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 3b11b0d80da5..d50cdeed6d73 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1126,7 +1126,7 @@ public final class SystemServiceRegistry { // Get the services without throwing as this is an optional feature Context outerContext = ctx.getOuterContext(); ContentCaptureOptions options = outerContext.getContentCaptureOptions(); - // Options is null when the service didn't whitelist the activity or package + // Options is null when the service didn't allowlist the activity or package if (options != null && (options.lite || options.isWhitelisted(outerContext))) { IBinder b = ServiceManager .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); @@ -1136,7 +1136,7 @@ public final class SystemServiceRegistry { return new ContentCaptureManager(outerContext, service, options); } } - // When feature is disabled or app / package not whitelisted, we return a null + // When feature is disabled or app / package not allowlisted, we return a null // manager to apps so the performance impact is practically zero return null; }}); diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index e4818b274342..7624f356eb6a 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -74,7 +74,7 @@ public final class WallpaperColors implements Parcelable { */ public static final int HINT_FROM_BITMAP = 1 << 2; - // Maximum size that a bitmap can have to keep our calculations sane + // Maximum size that a bitmap can have to keep our calculations valid private static final int MAX_BITMAP_SIZE = 112; // Even though we have a maximum size, we'll mainly match bitmap sizes diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 7b3d9f113bda..8a85b7942800 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2051,7 +2051,7 @@ public class DevicePolicyManager { * Enable the Home button during LockTask mode. Note that if a custom launcher is used, it has * to be registered as the default launcher with * {@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}, and its - * package needs to be whitelisted for LockTask with + * package needs to be allowlisted for LockTask with * {@link #setLockTaskPackages(ComponentName, String[])}. * * @see #setLockTaskFeatures(ComponentName, int) @@ -2092,7 +2092,7 @@ public class DevicePolicyManager { public static final int LOCK_TASK_FEATURE_KEYGUARD = 1 << 5; /** - * Enable blocking of non-whitelisted activities from being started into a locked task. + * Enable blocking of non-allowlisted activities from being started into a locked task. * * @see #setLockTaskFeatures(ComponentName, int) */ @@ -4713,7 +4713,7 @@ public class DevicePolicyManager { /** * Disable trust agents on secure keyguard screens (e.g. PIN/Pattern/Password). * By setting this flag alone, all trust agents are disabled. If the admin then wants to - * whitelist specific features of some trust agent, {@link #setTrustAgentConfiguration} can be + * allowlist specific features of some trust agent, {@link #setTrustAgentConfiguration} can be * used in conjuction to set trust-agent-specific configurations. */ public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4; @@ -5694,7 +5694,7 @@ public class DevicePolicyManager { * The call will fail if called with the package name of an unsupported VPN app. * <p> Enabling lockdown via {@code lockdownEnabled} argument carries the risk that any failure * of the VPN provider could break networking for all apps. This method clears any lockdown - * whitelist set by {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)}. + * allowlist set by {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)}. * * @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to * remove an existing always-on VPN configuration. @@ -5716,13 +5716,13 @@ public class DevicePolicyManager { * admin to specify a set of apps that should be able to access the network directly when VPN * is not connected. When VPN connects these apps switch over to VPN if allowed to use that VPN. * System apps can always bypass VPN. - * <p> Note that the system doesn't update the whitelist when packages are installed or + * <p> Note that the system doesn't update the allowlist when packages are installed or * uninstalled, the admin app must call this method to keep the list up to date. * <p> When {@code lockdownEnabled} is false {@code lockdownWhitelist} is ignored . When * {@code lockdownEnabled} is {@code true} and {@code lockdownWhitelist} is {@code null} or * empty, only system apps can bypass VPN. * <p> Setting always-on VPN package to {@code null} or using - * {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} clears lockdown whitelist. + * {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} clears lockdown allowlist. * * @param vpnPackage package name for an installed VPN app on the device, or {@code null} * to remove an existing always-on VPN configuration @@ -7955,7 +7955,7 @@ public class DevicePolicyManager { * {@link #setApplicationHidden(ComponentName, String, boolean)}) * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param packageList List of package names to whitelist + * @param packageList List of package names to allowlist * @return true if setting the restriction succeeded. It will fail if called outside a managed * profile * @throws SecurityException if {@code admin} is not a profile owner. @@ -9056,7 +9056,7 @@ public class DevicePolicyManager { } /** - * Called by device owners to set the user's master location setting. + * Called by device owners to set the user's global location setting. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with * @param locationEnabled whether location should be enabled or disabled @@ -9155,11 +9155,11 @@ public class DevicePolicyManager { } /** - * Called by profile or device owners to set the master volume mute on or off. + * Called by profile or device owners to set the global volume mute on or off. * This has no effect when set on a managed profile. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param on {@code true} to mute master volume, {@code false} to turn mute off. + * @param on {@code true} to mute global volume, {@code false} to turn mute off. * @throws SecurityException if {@code admin} is not a device or profile owner. */ public void setMasterVolumeMuted(@NonNull ComponentName admin, boolean on) { @@ -9174,10 +9174,10 @@ public class DevicePolicyManager { } /** - * Called by profile or device owners to check whether the master volume mute is on or off. + * Called by profile or device owners to check whether the global volume mute is on or off. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @return {@code true} if master volume is muted, {@code false} if it's not. + * @return {@code true} if global volume is muted, {@code false} if it's not. * @throws SecurityException if {@code admin} is not a device or profile owner. */ public boolean isMasterVolumeMuted(@NonNull ComponentName admin) { @@ -9248,14 +9248,14 @@ public class DevicePolicyManager { /** * Called by the profile owner of a managed profile to enable widget providers from a given * package to be available in the parent profile. As a result the user will be able to add - * widgets from the white-listed package running under the profile to a widget host which runs + * widgets from the allowlisted package running under the profile to a widget host which runs * under the parent profile, for example the home screen. Note that a package may have zero or * more provider components, where each component provides a different widget type. * <p> - * <strong>Note:</strong> By default no widget provider package is white-listed. + * <strong>Note:</strong> By default no widget provider package is allowlisted. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param packageName The package from which widget providers are white-listed. + * @param packageName The package from which widget providers are allowlisted. * @return Whether the package was added. * @throws SecurityException if {@code admin} is not a profile owner. * @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String) @@ -9279,10 +9279,10 @@ public class DevicePolicyManager { * should have been added via * {@link #addCrossProfileWidgetProvider( android.content.ComponentName, String)}. * <p> - * <strong>Note:</strong> By default no widget provider package is white-listed. + * <strong>Note:</strong> By default no widget provider package is allowlisted. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param packageName The package from which widget providers are no longer white-listed. + * @param packageName The package from which widget providers are no longer allowlisted. * @return Whether the package was removed. * @throws SecurityException if {@code admin} is not a profile owner. * @see #addCrossProfileWidgetProvider(android.content.ComponentName, String) @@ -9306,7 +9306,7 @@ public class DevicePolicyManager { * available in the parent profile. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @return The white-listed package list. + * @return The allowlisted package list. * @see #addCrossProfileWidgetProvider(android.content.ComponentName, String) * @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String) * @throws SecurityException if {@code admin} is not a profile owner. @@ -11627,7 +11627,7 @@ public class DevicePolicyManager { * called, no package is allowed to access cross-profile calendar APIs by default. * * @param admin which {@link DeviceAdminReceiver} this request is associated with - * @param packageNames set of packages to be whitelisted + * @param packageNames set of packages to be allowlisted * @throws SecurityException if {@code admin} is not a profile owner * * @see #getCrossProfileCalendarPackages(ComponentName) @@ -11734,7 +11734,7 @@ public class DevicePolicyManager { } /** - * Sets the set of admin-whitelisted package names that are allowed to request user consent for + * Sets the set of admin-allowlisted package names that are allowed to request user consent for * cross-profile communication. * * <p>Assumes that the caller is a profile owner and is the given {@code admin}. @@ -11742,11 +11742,11 @@ public class DevicePolicyManager { * <p>Previous calls are overridden by each subsequent call to this method. * * <p>Note that other apps may be able to request user consent for cross-profile communication - * if they have been explicitly whitelisted by the OEM. + * if they have been explicitly allowlisted by the OEM. * * <p>When previously-set cross-profile packages are missing from {@code packageNames}, the * app-op for {@code INTERACT_ACROSS_PROFILES} will be reset for those packages. This will not - * occur for packages that are whitelisted by the OEM. + * occur for packages that are allowlisted by the OEM. * * @param admin the {@link DeviceAdminReceiver} this request is associated with * @param packageNames the new cross-profile package names @@ -11771,7 +11771,7 @@ public class DevicePolicyManager { * <p>Assumes that the caller is a profile owner and is the given {@code admin}. * * <p>Note that other apps not included in the returned set may be able to request user consent - * for cross-profile communication if they have been explicitly whitelisted by the OEM. + * for cross-profile communication if they have been explicitly allowlisted by the OEM. * * @param admin the {@link DeviceAdminReceiver} this request is associated with * @return the set of package names the admin has previously set as allowed to request user @@ -11802,7 +11802,7 @@ public class DevicePolicyManager { * #vendor_cross_profile_apps}.</li> * </ul> * - * @return the combined set of whitelisted package names set via + * @return the combined set of allowlisted package names set via * {@link #setCrossProfilePackages(ComponentName, Set)}, {@link com.android.internal.R.array * #cross_profile_apps}, and {@link com.android.internal.R.array#vendor_cross_profile_apps}. * diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index 8f5dbc45bf1b..19242ba9bdb5 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -36,36 +36,36 @@ import java.util.Set; public abstract class DevicePolicyManagerInternal { /** - * Listener for changes in the white-listed packages to show cross-profile + * Listener for changes in the allowlisted packages to show cross-profile * widgets. */ public interface OnCrossProfileWidgetProvidersChangeListener { /** - * Called when the white-listed packages to show cross-profile widgets + * Called when the allowlisted packages to show cross-profile widgets * have changed for a given user. * - * @param profileId The profile for which the white-listed packages changed. - * @param packages The white-listed packages. + * @param profileId The profile for which the allowlisted packages changed. + * @param packages The allowlisted packages. */ public void onCrossProfileWidgetProvidersChanged(int profileId, List<String> packages); } /** - * Gets the packages whose widget providers are white-listed to be + * Gets the packages whose widget providers are allowlisted to be * available in the parent user. * * <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held. * * @param profileId The profile id. * @return The list of packages if such or empty list if there are - * no white-listed packages or the profile id is not a managed + * no allowlisted packages or the profile id is not a managed * profile. */ public abstract List<String> getCrossProfileWidgetProviders(int profileId); /** - * Adds a listener for changes in the white-listed packages to show + * Adds a listener for changes in the allowlisted packages to show * cross-profile app widgets. * * <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held. @@ -181,7 +181,7 @@ public abstract class DevicePolicyManagerInternal { * {@link com.android.internal.R.array#vendor_cross_profile_apps}.</li> * </ul> * - * @return the combined set of whitelisted package names set via + * @return the combined set of allowlisted package names set via * {@link DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)} and * {@link com.android.internal.R.array#cross_profile_apps} and * {@link com.android.internal.R.array#vendor_cross_profile_apps} diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 1345e66a355a..f74d16ee9238 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -1186,13 +1186,13 @@ public final class UsageStatsManager { /** * {@hide} - * Temporarily whitelist the specified app for a short duration. This is to allow an app + * Temporarily allowlist the specified app for a short duration. This is to allow an app * receiving a high priority message to be able to access the network and acquire wakelocks * even if the device is in power-save mode or the app is currently considered inactive. - * @param packageName The package name of the app to whitelist. - * @param duration Duration to whitelist the app for, in milliseconds. It is recommended that + * @param packageName The package name of the app to allowlist. + * @param duration Duration to allowlist the app for, in milliseconds. It is recommended that * this be limited to 10s of seconds. Requested duration will be clamped to a few minutes. - * @param user The user for whom the package should be whitelisted. Passing in a user that is + * @param user The user for whom the package should be allowlisted. Passing in a user that is * not the same as the caller's process will require the INTERACT_ACROSS_USERS permission. * @see #isAppInactive(String) * diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index c7b20b2a1dc3..6a6879626c9c 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2504,7 +2504,7 @@ public final class BluetoothAdapter { * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. * * @param channel RFCOMM channel to listen on - * @param mitm enforce man-in-the-middle protection for authentication. + * @param mitm enforce person-in-the-middle protection for authentication. * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 * connections. * @return a listening RFCOMM BluetoothServerSocket @@ -2562,7 +2562,7 @@ public final class BluetoothAdapter { /** * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. * <p>The link key is not required to be authenticated, i.e the communication may be - * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices, + * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices, * the link will be encrypted, as encryption is mandartory. * For legacy devices (pre Bluetooth 2.1 devices) the link will not * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an @@ -2595,7 +2595,7 @@ public final class BluetoothAdapter { * Create a listening, encrypted, * RFCOMM Bluetooth socket with Service Record. * <p>The link will be encrypted, but the link key is not required to be authenticated - * i.e the communication is vulnerable to Man In the Middle attacks. Use + * i.e the communication is vulnerable to Person In the Middle attacks. Use * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key. * <p> Use this socket if authentication of link key is not possible. * For example, for Bluetooth 2.1 devices, if any of the devices does not have @@ -2680,7 +2680,7 @@ public final class BluetoothAdapter { * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. * * @param port the PSM to listen on - * @param mitm enforce man-in-the-middle protection for authentication. + * @param mitm enforce person-in-the-middle protection for authentication. * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 * connections. * @return An L2CAP BluetoothServerSocket @@ -3381,7 +3381,7 @@ public final class BluetoothAdapter { * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The * supported Bluetooth transport is LE only. * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable - * to man-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and + * to person-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and * authenticated communication channel is desired. * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index fd605604a8f1..6287453f6399 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1845,7 +1845,7 @@ public final class BluetoothDevice implements Parcelable { * socket will be encrypted. * <p> Use this socket only if an authenticated socket link is possible. * Authentication refers to the authentication of the link key to - * prevent man-in-the-middle type of attacks. + * prevent person-in-the-middle type of attacks. * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. @@ -1880,7 +1880,7 @@ public final class BluetoothDevice implements Parcelable { * socket will be encrypted. * <p> Use this socket only if an authenticated socket link is possible. * Authentication refers to the authentication of the link key to - * prevent man-in-the-middle type of attacks. + * prevent person-in-the-middle type of attacks. * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. @@ -1937,7 +1937,7 @@ public final class BluetoothDevice implements Parcelable { * socket will be encrypted. * <p> Use this socket only if an authenticated socket link is possible. * Authentication refers to the authentication of the link key to - * prevent man-in-the-middle type of attacks. + * prevent person-in-the-middle type of attacks. * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. @@ -1969,7 +1969,7 @@ public final class BluetoothDevice implements Parcelable { * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure * outgoing connection to this remote device using SDP lookup of uuid. * <p> The communication channel will not have an authenticated link key - * i.e it will be subject to man-in-the-middle attacks. For Bluetooth 2.1 + * i.e it will be subject to person-in-the-middle attacks. For Bluetooth 2.1 * devices, the link key will be encrypted, as encryption is mandatory. * For legacy devices (pre Bluetooth 2.1 devices) the link key will * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an @@ -2225,7 +2225,7 @@ public final class BluetoothDevice implements Parcelable { * <p>The remote device will be authenticated and communication on this socket will be * encrypted. * <p> Use this socket if an authenticated socket link is possible. Authentication refers - * to the authentication of the link key to prevent man-in-the-middle type of attacks. + * to the authentication of the link key to prevent person-in-the-middle type of attacks. * * @param psm dynamic PSM value from remote device * @return a CoC #BluetoothSocket ready for an outgoing connection @@ -2252,7 +2252,7 @@ public final class BluetoothDevice implements Parcelable { * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection. * <p>Application using this API is responsible for obtaining PSM value from remote device. * <p> The communication channel may not have an authenticated link key, i.e. it may be subject - * to man-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and + * to person-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and * authenticated communication channel is possible. * * @param psm dynamic PSM value from remote device diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index f877f04626da..c58b5d218e74 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -134,14 +134,14 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ static final int AUTHENTICATION_NONE = 0; /** - * Authentication requested; no man-in-the-middle protection required. + * Authentication requested; no person-in-the-middle protection required. * * @hide */ /*package*/ static final int AUTHENTICATION_NO_MITM = 1; /** - * Authentication with man-in-the-middle protection requested. + * Authentication with person-in-the-middle protection requested. * * @hide */ diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java index cf82a3304572..f718c0b57c1b 100644 --- a/core/java/android/bluetooth/BluetoothGattCallback.java +++ b/core/java/android/bluetooth/BluetoothGattCallback.java @@ -183,7 +183,7 @@ public abstract class BluetoothGattCallback { * @param gatt GATT client involved * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from * 6 (7.5ms) to 3200 (4000ms). - * @param latency Slave latency for the connection in number of connection events. Valid range + * @param latency Worker latency for the connection in number of connection events. Valid range * is from 0 to 499 * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10 * (0.1s) to 3200 (32s) diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java index 7066f470aa96..8f1b59cf69e6 100644 --- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -84,7 +84,7 @@ public class BluetoothGattCharacteristic implements Parcelable { public static final int PERMISSION_READ_ENCRYPTED = 0x02; /** - * Characteristic permission: Allow reading with man-in-the-middle protection + * Characteristic permission: Allow reading with person-in-the-middle protection */ public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; @@ -99,7 +99,7 @@ public class BluetoothGattCharacteristic implements Parcelable { public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; /** - * Characteristic permission: Allow encrypted writes with man-in-the-middle + * Characteristic permission: Allow encrypted writes with person-in-the-middle * protection */ public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; @@ -111,7 +111,7 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Characteristic permission: Allow signed write operations with - * man-in-the-middle protection + * person-in-the-middle protection */ public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java index 7cc2d6bc53fb..49ba281e2eb7 100644 --- a/core/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java @@ -58,7 +58,7 @@ public class BluetoothGattDescriptor implements Parcelable { public static final int PERMISSION_READ_ENCRYPTED = 0x02; /** - * Descriptor permission: Allow reading with man-in-the-middle protection + * Descriptor permission: Allow reading with person-in-the-middle protection */ public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; @@ -73,7 +73,7 @@ public class BluetoothGattDescriptor implements Parcelable { public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; /** - * Descriptor permission: Allow encrypted writes with man-in-the-middle + * Descriptor permission: Allow encrypted writes with person-in-the-middle * protection */ public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; @@ -85,7 +85,7 @@ public class BluetoothGattDescriptor implements Parcelable { /** * Descriptor permission: Allow signed write operations with - * man-in-the-middle protection + * person-in-the-middle protection */ public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java index 2c8114be3fe3..0ead5f57e86c 100644 --- a/core/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java @@ -187,7 +187,7 @@ public abstract class BluetoothGattServerCallback { * @param device The remote device involved * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from * 6 (7.5ms) to 3200 (4000ms). - * @param latency Slave latency for the connection in number of connection events. Valid range + * @param latency Worker latency for the connection in number of connection events. Valid range * is from 0 to 499 * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10 * (0.1s) to 3200 (32s) diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java index 13d6d7021ec8..e7809aeb1bb5 100644 --- a/core/java/android/bluetooth/BluetoothGattService.java +++ b/core/java/android/bluetooth/BluetoothGattService.java @@ -44,7 +44,7 @@ public class BluetoothGattService implements Parcelable { /** - * The remote device his service is associated with. + * The remote device this service is associated with. * This applies to client applications only. * * @hide diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 88c186c88aaf..5c1bcaf31319 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -110,7 +110,7 @@ public final class BluetoothServerSocket implements Closeable { * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted * @param port remote port - * @param mitm enforce man-in-the-middle protection for authentication. + * @param mitm enforce person-in-the-middle protection for authentication. * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection * @throws IOException On error, for example Bluetooth not available, or insufficient * privileges diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index f77436965537..d41a6d064d1f 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -128,9 +128,12 @@ public final class BluetoothSocket implements Closeable { private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; private final ParcelUuid mUuid; - private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ - private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ - private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ + /** when true no SPP SDP record will be created */ + private boolean mExcludeSdp = false; + /** when true Person-in-the-middle protection will be enabled */ + private boolean mAuthMitm = false; + /** Minimum 16 digit pin for sec mode 2 connections */ + private boolean mMin16DigitPin = false; @UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.") private ParcelFileDescriptor mPfd; @UnsupportedAppUsage @@ -190,7 +193,7 @@ public final class BluetoothSocket implements Closeable { * @param device remote device that this socket can connect to * @param port remote port * @param uuid SDP uuid - * @param mitm enforce man-in-the-middle protection. + * @param mitm enforce person-in-the-middle protection. * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection * @throws IOException On error, for example Bluetooth not available, or insufficient * privileges diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java index 3fa6a3e76634..06a3f2f08bab 100644 --- a/core/java/android/companion/Association.java +++ b/core/java/android/companion/Association.java @@ -17,6 +17,7 @@ package android.companion; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; @@ -30,17 +31,21 @@ import java.util.Objects; * * @hide */ -@DataClass(genEqualsHashCode = true, genToString = true) -public class Association implements Parcelable { +@DataClass(genEqualsHashCode = true, genToString = true, genHiddenConstructor = true) +public final class Association implements Parcelable { - public final int userId; - public final @NonNull String deviceAddress; - public final @NonNull String companionAppPackage; + private final @UserIdInt int mUserId; + private final @NonNull String mDeviceMacAddress; + private final @NonNull String mPackageName; + /** @hide */ + public int getUserId() { + return mUserId; + } - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -53,22 +58,39 @@ public class Association implements Parcelable { //@formatter:off + /** + * Creates a new Association. + * + * @hide + */ @DataClass.Generated.Member public Association( - int userId, - @NonNull String deviceAddress, - @NonNull String companionAppPackage) { - this.userId = userId; - this.deviceAddress = deviceAddress; + @UserIdInt int userId, + @NonNull String deviceMacAddress, + @NonNull String packageName) { + this.mUserId = userId; + com.android.internal.util.AnnotationValidations.validate( + UserIdInt.class, null, mUserId); + this.mDeviceMacAddress = deviceMacAddress; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, deviceAddress); - this.companionAppPackage = companionAppPackage; + NonNull.class, null, mDeviceMacAddress); + this.mPackageName = packageName; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, companionAppPackage); + NonNull.class, null, mPackageName); // onConstructed(); // You can define this method to get a callback } + @DataClass.Generated.Member + public @NonNull String getDeviceMacAddress() { + return mDeviceMacAddress; + } + + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + @Override @DataClass.Generated.Member public String toString() { @@ -76,9 +98,9 @@ public class Association implements Parcelable { // String fieldNameToString() { ... } return "Association { " + - "userId = " + userId + ", " + - "deviceAddress = " + deviceAddress + ", " + - "companionAppPackage = " + companionAppPackage + + "userId = " + mUserId + ", " + + "deviceMacAddress = " + mDeviceMacAddress + ", " + + "packageName = " + mPackageName + " }"; } @@ -95,9 +117,9 @@ public class Association implements Parcelable { Association that = (Association) o; //noinspection PointlessBooleanExpression return true - && userId == that.userId - && Objects.equals(deviceAddress, that.deviceAddress) - && Objects.equals(companionAppPackage, that.companionAppPackage); + && mUserId == that.mUserId + && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress) + && Objects.equals(mPackageName, that.mPackageName); } @Override @@ -107,9 +129,9 @@ public class Association implements Parcelable { // int fieldNameHashCode() { ... } int _hash = 1; - _hash = 31 * _hash + userId; - _hash = 31 * _hash + Objects.hashCode(deviceAddress); - _hash = 31 * _hash + Objects.hashCode(companionAppPackage); + _hash = 31 * _hash + mUserId; + _hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress); + _hash = 31 * _hash + Objects.hashCode(mPackageName); return _hash; } @@ -119,9 +141,9 @@ public class Association implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - dest.writeInt(userId); - dest.writeString(deviceAddress); - dest.writeString(companionAppPackage); + dest.writeInt(mUserId); + dest.writeString(mDeviceMacAddress); + dest.writeString(mPackageName); } @Override @@ -131,21 +153,23 @@ public class Association implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - protected Association(@NonNull Parcel in) { + /* package-private */ Association(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - int _userId = in.readInt(); - String _deviceAddress = in.readString(); - String _companionAppPackage = in.readString(); + int userId = in.readInt(); + String deviceMacAddress = in.readString(); + String packageName = in.readString(); - this.userId = _userId; - this.deviceAddress = _deviceAddress; + this.mUserId = userId; + com.android.internal.util.AnnotationValidations.validate( + UserIdInt.class, null, mUserId); + this.mDeviceMacAddress = deviceMacAddress; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, deviceAddress); - this.companionAppPackage = _companionAppPackage; + NonNull.class, null, mDeviceMacAddress); + this.mPackageName = packageName; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, companionAppPackage); + NonNull.class, null, mPackageName); // onConstructed(); // You can define this method to get a callback } @@ -165,10 +189,10 @@ public class Association implements Parcelable { }; @DataClass.Generated( - time = 1573767103332L, - codegenVersion = "1.0.13", + time = 1599083149942L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/companion/Association.java", - inputSignatures = "public final int userId\npublic final @android.annotation.NonNull java.lang.String deviceAddress\npublic final @android.annotation.NonNull java.lang.String companionAppPackage\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)") + inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 591a714bfb93..da4980bba3d0 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -305,6 +305,24 @@ public final class CompanionDeviceManager { } } + /** + * Gets all package-device {@link Association}s for the current user. + * + * @return the associations list + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) + public @NonNull List<Association> getAllAssociations() { + if (!checkFeaturePresent()) { + return Collections.emptyList(); + } + try { + return mService.getAssociationsForUser(mContext.getUser().getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private boolean checkFeaturePresent() { boolean featurePresent = mService != null; if (!featurePresent && DEBUG) { diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl index b323f58aa827..bcb9be80e6a4 100644 --- a/core/java/android/companion/ICompanionDeviceManager.aidl +++ b/core/java/android/companion/ICompanionDeviceManager.aidl @@ -18,6 +18,7 @@ package android.companion; import android.app.PendingIntent; import android.companion.IFindDeviceCallback; +import android.companion.Association; import android.companion.AssociationRequest; import android.content.ComponentName; @@ -35,6 +36,8 @@ interface ICompanionDeviceManager { in String callingPackage); List<String> getAssociations(String callingPackage, int userId); + List<Association> getAssociationsForUser(int userId); + void disassociate(String deviceMacAddress, String callingPackage); boolean hasNotificationAccess(in ComponentName component); diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java index 97b33b74314c..80a7b16ee761 100644 --- a/core/java/android/content/AutofillOptions.java +++ b/core/java/android/content/AutofillOptions.java @@ -54,12 +54,12 @@ public final class AutofillOptions implements Parcelable { public final boolean compatModeEnabled; /** - * Whether package is whitelisted for augmented autofill. + * Whether package is allowlisted for augmented autofill. */ public boolean augmentedAutofillEnabled; /** - * List of whitelisted activities. + * List of allowlisted activities. */ @Nullable public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill; @@ -82,7 +82,7 @@ public final class AutofillOptions implements Parcelable { } /** - * Returns whether activity is whitelisted for augmented autofill. + * Returns whether activity is allowlisted for augmented autofill. */ public boolean isAugmentedAutofillEnabled(@NonNull Context context) { if (!augmentedAutofillEnabled) return false; diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java index cb2142c356b2..ef49e029db13 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -69,7 +69,7 @@ public final class ContentCaptureOptions implements Parcelable { public final int logHistorySize; /** - * List of activities explicitly whitelisted for content capture (or {@code null} if whitelisted + * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted * for all acitivites in the package). */ @Nullable @@ -147,7 +147,7 @@ public final class ContentCaptureOptions implements Parcelable { /** @hide */ @VisibleForTesting public boolean isWhitelisted(@NonNull Context context) { - if (whitelistedComponents == null) return true; // whole package is whitelisted + if (whitelistedComponents == null) return true; // whole package is allowlisted final ContentCaptureClient client = context.getContentCaptureClient(); if (client == null) { // Shouldn't happen, but it doesn't hurt to check... diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b5eb043d847c..a7cdc6886f22 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -3384,12 +3384,12 @@ public abstract class ContentResolver implements ContentInterface { } /** - * Gets the master auto-sync setting that applies to all the providers and accounts. + * Gets the global auto-sync setting that applies to all the providers and accounts. * If this is false then the per-provider auto-sync setting is ignored. * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#READ_SYNC_SETTINGS}. * - * @return the master auto-sync setting that applies to all the providers and accounts + * @return the global auto-sync setting that applies to all the providers and accounts */ public static boolean getMasterSyncAutomatically() { try { @@ -3412,12 +3412,12 @@ public abstract class ContentResolver implements ContentInterface { } /** - * Sets the master auto-sync setting that applies to all the providers and accounts. + * Sets the global auto-sync setting that applies to all the providers and accounts. * If this is false then the per-provider auto-sync setting is ignored. * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}. * - * @param sync the master auto-sync setting that applies to all the providers and accounts + * @param sync the global auto-sync setting that applies to all the providers and accounts */ public static void setMasterSyncAutomatically(boolean sync) { setMasterSyncAutomaticallyAsUser(sync, UserHandle.myUserId()); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 98f78878ec15..38bc79750631 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3384,6 +3384,7 @@ public abstract class Context { /** @hide */ @StringDef(suffix = { "_SERVICE" }, value = { POWER_SERVICE, + //@hide: POWER_STATS_SERVICE, WINDOW_SERVICE, LAYOUT_INFLATER_SERVICE, ACCOUNT_SERVICE, @@ -3726,6 +3727,16 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.PowerStatsService} for accessing power stats + * service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String POWER_STATS_SERVICE = "power_stats"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a * {@link android.os.RecoverySystem} for accessing the recovery system * service. * @@ -6128,7 +6139,7 @@ public abstract class Context { } /** - * Gets the Content Capture options for this context, or {@code null} if it's not whitelisted. + * Gets the Content Capture options for this context, or {@code null} if it's not allowlisted. * * @hide */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7eed48535690..35c7b968c892 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2861,7 +2861,7 @@ public class Intent implements Parcelable, Cloneable { * This should <em>only</em> be used to determine when the wallpaper * has changed to show the new wallpaper to the user. You should certainly * never, in response to this, change the wallpaper or other attributes of - * it such as the suggested size. That would be crazy, right? You'd cause + * it such as the suggested size. That would be unexpected, right? You'd cause * all kinds of loops, especially if other apps are doing similar things, * right? Of course. So please don't do this. * @@ -3975,7 +3975,7 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR"; /** - * Broadcast intent sent by the RecoverySystem to inform listeners that a master clear (wipe) + * Broadcast intent sent by the RecoverySystem to inform listeners that a global clear (wipe) * is about to be performed. * @hide */ diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java index 010992c98d48..8f08fdec7806 100644 --- a/core/java/android/content/RestrictionEntry.java +++ b/core/java/android/content/RestrictionEntry.java @@ -68,7 +68,7 @@ public class RestrictionEntry implements Parcelable { /** * Restriction of type "multi-select". Use this for presenting a multi-select list where more - * than one entry can be selected, such as for choosing specific titles to white-list. + * than one entry can be selected, such as for choosing specific titles to allowlist. * Call {@link #setChoiceEntries(String[])} and * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user * and the corresponding values, respectively. diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java index 2869abb53b37..4db4c7316433 100644 --- a/core/java/android/content/integrity/AppIntegrityManager.java +++ b/core/java/android/content/integrity/AppIntegrityManager.java @@ -31,7 +31,7 @@ import java.util.List; * Class for pushing rules used to check the integrity of app installs. * * <p>Note: applications using methods of this class must be a system app and have their package - * name whitelisted as an integrity rule provider. Otherwise a {@link SecurityException} will be + * name allowlisted as an integrity rule provider. Otherwise a {@link SecurityException} will be * thrown. * * @hide @@ -125,7 +125,7 @@ public class AppIntegrityManager { } /** - * Get the package names of all whitelisted rule providers. + * Get the package names of all allowlisted rule providers. * * <p>Warning: this method is only used for tests. * diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java index 1cbbdca76e13..b6bee42e99c8 100644 --- a/core/java/android/content/pm/AndroidTestBaseUpdater.java +++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java @@ -17,7 +17,7 @@ package android.content.pm; /** - * Dummy class to maintain legacy behavior of including a class in core source to toggle + * Placeholder class to maintain legacy behavior of including a class in core source to toggle * whether or not a shared library is stripped at build time. * * @hide diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 0c6810c07394..6673cb933eff 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -2008,7 +2008,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Updates the hidden API enforcement policy for this app from the given values, if appropriate. * * This will have no effect if this app is not subject to hidden API enforcement, i.e. if it - * is on the package whitelist. + * is on the package allowlist. * * @param policy configured policy for this app, or {@link #HIDDEN_API_ENFORCEMENT_DEFAULT} * if nothing configured. diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 3b6740ef7343..8b411d5fe031 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -285,7 +285,7 @@ public class CrossProfileApps { * </ul> * * <p>Note that in order for the user to be able to grant the consent, the requesting package - * must be whitelisted by the admin or the OEM and installed in the other profile. If this is + * must be allowlisted by the admin or the OEM and installed in the other profile. If this is * not the case the user will be shown a message explaining why they can't grant the consent. * * <p>Note that user consent could already be granted if given a return value of {@code true}. @@ -310,8 +310,8 @@ public class CrossProfileApps { * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> * <li>The user has previously consented to cross-profile communication for the calling * package.</li> - * <li>The calling package has either been whitelisted by default by the OEM or has been - * explicitly whitelisted by the admin via + * <li>The calling package has either been allowlisted by default by the OEM or has been + * explicitly allowlisted by the admin via * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. * </li> * </ul> @@ -430,10 +430,10 @@ public class CrossProfileApps { * other profile in the same profile group. * * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will - * not return {@code false} if the app is not whitelisted or not installed in the other profile. + * not return {@code false} if the app is not allowlisted or not installed in the other profile. * * <p>Note that platform-signed apps that are automatically granted the permission and are not - * whitelisted by the OEM will not be included in this list. + * allowlisted by the OEM will not be included in this list. * * @hide */ diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 9eb95a3f6707..75ee9b7943fe 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -700,7 +700,7 @@ public class PackageInstaller { * installer was created. * * <p>This will - * {@link PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set) whitelist + * {@link PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set) allowlist * all restricted permissions}. * * @param packageName The package to install. @@ -1726,25 +1726,25 @@ public class PackageInstaller { } /** - * Sets which restricted permissions to be whitelisted for the app. Whitelisting + * Sets which restricted permissions to be allowlisted for the app. Allowlisting * is not granting the permissions, rather it allows the app to hold permissions - * which are otherwise restricted. Whitelisting a non restricted permission has + * which are otherwise restricted. Allowlisting a non restricted permission has * no effect. * * <p> Permissions can be hard restricted which means that the app cannot hold * them or soft restricted where the app can hold the permission but in a weaker * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} - * depends on the permission declaration. Whitelisting a hard restricted permission - * allows the app to hold that permission and whitelisting a soft restricted + * depends on the permission declaration. Allowlisting a hard restricted permission + * allows the app to hold that permission and allowlisting a soft restricted * permission allows the app to hold the permission in its full, unrestricted form. * - * <p> Permissions can also be immutably restricted which means that the whitelist + * <p> Permissions can also be immutably restricted which means that the allowlist * state of the permission can be determined only at install time and cannot be * changed on updated or at a later point via the package manager APIs. * - * <p>Initially, all restricted permissions are whitelisted but you can change - * which ones are whitelisted by calling this method or the corresponding ones + * <p>Initially, all restricted permissions are allowlisted but you can change + * which ones are allowlisted by calling this method or the corresponding ones * on the {@link PackageManager}. Only soft or hard restricted permissions on the current * Android version are supported and any invalid entries will be removed. * @@ -2469,7 +2469,7 @@ public class PackageInstaller { /** * Get the value set in {@link SessionParams#setWhitelistedRestrictedPermissions(Set)}. - * Note that if all permissions are whitelisted this method returns {@link + * Note that if all permissions are allowlisted this method returns {@link * SessionParams#RESTRICTED_PERMISSIONS_ALL}. * * @hide diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index e990fd783498..04e15c20b2f4 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -371,7 +371,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * * <p>This permission is restricted immutably which means that its * restriction state may be specified only on the first install of - * the app and will stay in this initial whitelist state until + * the app and will stay in this initial allowlist state until * the app is uninstalled. */ public static final int FLAG_IMMUTABLY_RESTRICTED = 1<<4; diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index e57353922115..c5240c2814e8 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -63,6 +63,9 @@ import java.util.Set; /** @hide **/ public class PackageInfoWithoutStateUtils { + public static final String SYSTEM_DATA_PATH = + Environment.getDataDirectoryPath() + File.separator + "system"; + @Nullable public static PackageInfo generate(ParsingPackageRead pkg, int[] gids, @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, @@ -168,7 +171,8 @@ public class PackageInfoWithoutStateUtils { info.instrumentation = new InstrumentationInfo[N]; for (int i = 0; i < N; i++) { info.instrumentation[i] = generateInstrumentationInfo( - pkg.getInstrumentations().get(i), pkg, flags, userId); + pkg.getInstrumentations().get(i), pkg, flags, userId, + true /* assignUserFields */); } } } @@ -332,7 +336,8 @@ public class PackageInfoWithoutStateUtils { return null; } - return generateApplicationInfoUnchecked(pkg, flags, state, userId); + return generateApplicationInfoUnchecked(pkg, flags, state, userId, + true /* assignUserFields */); } /** @@ -340,15 +345,23 @@ public class PackageInfoWithoutStateUtils { * system server. * * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, PackageUserState, int)}. + * + * @param assignUserFields whether to fill the returned {@link ApplicationInfo} with user + * specific fields. This can be skipped when building from a system + * server package, as there are cached strings which can be used rather + * than querying and concatenating the comparatively expensive + * {@link Environment#getDataDirectory(String)}}. */ @NonNull public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg, - @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) { + @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId, + boolean assignUserFields) { // Make shallow copy so we can store the metadata/libraries safely ApplicationInfo ai = pkg.toAppInfoWithoutState(); - // Init handles data directories - // TODO(b/135203078): Consolidate the data directory logic, remove initForUser - ai.initForUser(userId); + + if (assignUserFields) { + assignUserFields(pkg, ai, userId); + } if ((flags & PackageManager.GET_META_DATA) == 0) { ai.metaData = null; @@ -567,9 +580,14 @@ public class PackageInfoWithoutStateUtils { return generateProviderInfo(pkg, p, flags, state, null, userId); } + /** + * @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead, int, + * PackageUserState, int, boolean)} + */ @Nullable public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i, - ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId) { + ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId, + boolean assignUserFields) { if (i == null) return null; InstrumentationInfo ii = new InstrumentationInfo(); @@ -585,10 +603,10 @@ public class PackageInfoWithoutStateUtils { ii.splitSourceDirs = pkg.getSplitCodePaths(); ii.splitPublicSourceDirs = pkg.getSplitCodePaths(); ii.splitDependencies = pkg.getSplitDependencies(); - ii.dataDir = getDataDir(pkg, userId).getAbsolutePath(); - ii.deviceProtectedDataDir = getDeviceProtectedDataDir(pkg, userId).getAbsolutePath(); - ii.credentialProtectedDataDir = getCredentialProtectedDataDir(pkg, - userId).getAbsolutePath(); + + if (assignUserFields) { + assignUserFields(pkg, ii, userId); + } if ((flags & PackageManager.GET_META_DATA) == 0) { return ii; @@ -770,4 +788,55 @@ public class PackageInfoWithoutStateUtils { return Environment.getDataUserCePackageDirectory(pkg.getVolumeUuid(), userId, pkg.getPackageName()); } + + private static void assignUserFields(ParsingPackageRead pkg, ApplicationInfo info, int userId) { + // This behavior is undefined for no-state ApplicationInfos when called by a public API, + // since the uid is never assigned by the system. It will always effectively be appId 0. + info.uid = UserHandle.getUid(userId, UserHandle.getAppId(info.uid)); + + String pkgName = pkg.getPackageName(); + if ("android".equals(pkgName)) { + info.dataDir = SYSTEM_DATA_PATH; + return; + } + + // For performance reasons, all these paths are built as strings + String baseDataDirPrefix = + Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator; + String userIdPkgSuffix = File.separator + userId + File.separator + pkgName; + info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE + + userIdPkgSuffix; + info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix; + + if (pkg.isDefaultToDeviceProtectedStorage() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + info.dataDir = info.deviceProtectedDataDir; + } else { + info.dataDir = info.credentialProtectedDataDir; + } + } + + private static void assignUserFields(ParsingPackageRead pkg, InstrumentationInfo info, + int userId) { + String pkgName = pkg.getPackageName(); + if ("android".equals(pkgName)) { + info.dataDir = SYSTEM_DATA_PATH; + return; + } + + // For performance reasons, all these paths are built as strings + String baseDataDirPrefix = + Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator; + String userIdPkgSuffix = File.separator + userId + File.separator + pkgName; + info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE + + userIdPkgSuffix; + info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix; + + if (pkg.isDefaultToDeviceProtectedStorage() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + info.dataDir = info.deviceProtectedDataDir; + } else { + info.dataDir = info.credentialProtectedDataDir; + } + } } diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 872098c8689e..9cda4d33503b 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -16,6 +16,7 @@ package android.content.pm.parsing; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; @@ -343,6 +344,6 @@ public interface ParsingPackage extends ParsingPackageRead { // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement // for moving to the next step - @Deprecated + @CallSuper Object hideAsParsed(); } diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index ed12a17ad493..2a15e0260c1a 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -20,6 +20,8 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; +import android.annotation.CallSuper; +import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; @@ -73,6 +75,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; /** * The backing data for a package that was parsed from disk. @@ -147,7 +150,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @NonNull protected String mBaseApkPath; - private boolean requiredForAllUsers; @Nullable @DataClass.ParcelWith(ForInternedString.class) private String restrictedAccountType; @@ -165,7 +167,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @DataClass.ParcelWith(ForInternedString.class) private String overlayCategory; private int overlayPriority; - private boolean overlayIsStatic; @NonNull @DataClass.ParcelWith(ForInternedStringValueMap.class) private Map<String, String> overlayables = emptyMap(); @@ -282,11 +283,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @DataClass.ParcelWith(ForInternedString.class) protected String mPath; - private boolean use32BitAbi; - private boolean visibleToInstantApps; - - private boolean forceQueryable; - @NonNull @DataClass.ParcelWith(ForInternedStringList.class) private List<Intent> queriesIntents = emptyList(); @@ -331,12 +327,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { private int compatibleWidthLimitDp; private int descriptionRes; - // Usually there's code to set this to true during parsing, but it's possible to install an APK - // targeting <R that doesn't contain an <application> tag. That code would be skipped and never - // assign this, so initialize this to true for those cases. - private boolean enabled = true; - - private boolean crossProfile; private int fullBackupContent; private int iconRes; private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION; @@ -374,26 +364,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @DataClass.ParcelWith(ForInternedString.class) private String zygotePreloadName; - private boolean externalStorage; - private boolean baseHardwareAccelerated; - private boolean allowBackup; - private boolean killAfterRestore; - private boolean restoreAnyVersion; - private boolean fullBackupOnly; - private boolean persistent; - private boolean debuggable; - private boolean vmSafeMode; - private boolean hasCode; - private boolean allowTaskReparenting; - private boolean allowClearUserData; - private boolean largeHeap; - private boolean usesCleartextTraffic; - private boolean supportsRtl; - private boolean testOnly; - private boolean multiArch; - private boolean extractNativeLibs; - private boolean game; - /** * @see ParsingPackageRead#getResizeableActivity() */ @@ -401,26 +371,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @DataClass.ParcelWith(ForBoolean.class) private Boolean resizeableActivity; - private boolean staticSharedLibrary; - private boolean overlay; - private boolean isolatedSplitLoading; - private boolean hasDomainUrls; - private boolean profileableByShell; - private boolean backupInForeground; - private boolean useEmbeddedDex; - private boolean defaultToDeviceProtectedStorage; - private boolean directBootAware; - private boolean partiallyDirectBootAware; - private boolean resizeableActivityViaSdkVersion; - private boolean allowClearUserDataOnFailedRestore; - private boolean allowAudioPlaybackCapture; - private boolean requestLegacyExternalStorage; - private boolean usesNonSdkApi; - private boolean hasFragileUserData; - private boolean cantSaveState; - private boolean allowNativeHeapPointerTagging; private int autoRevokePermissions; - private boolean preserveLegacyExternalStorage; protected int gwpAsanMode; @@ -428,6 +379,130 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Nullable private ArraySet<String> mimeGroups; + // Usually there's code to set enabled to true during parsing, but it's possible to install + // an APK targeting <R that doesn't contain an <application> tag. That code would be skipped + // and never assign this, so initialize this to true for those cases. + private long mBooleans = Booleans.ENABLED; + + /** + * Flags used for a internal bitset. These flags should never be persisted or exposed outside + * of this class. It is expected that PackageCacher explicitly clears itself whenever the + * Parcelable implementation changes such that all these flags can be re-ordered or invalidated. + */ + protected static class Booleans { + @LongDef({ + EXTERNAL_STORAGE, + BASE_HARDWARE_ACCELERATED, + ALLOW_BACKUP, + KILL_AFTER_RESTORE, + RESTORE_ANY_VERSION, + FULL_BACKUP_ONLY, + PERSISTENT, + DEBUGGABLE, + VM_SAFE_MODE, + HAS_CODE, + ALLOW_TASK_REPARENTING, + ALLOW_CLEAR_USER_DATA, + LARGE_HEAP, + USES_CLEARTEXT_TRAFFIC, + SUPPORTS_RTL, + TEST_ONLY, + MULTI_ARCH, + EXTRACT_NATIVE_LIBS, + GAME, + STATIC_SHARED_LIBRARY, + OVERLAY, + ISOLATED_SPLIT_LOADING, + HAS_DOMAIN_URLS, + PROFILEABLE_BY_SHELL, + BACKUP_IN_FOREGROUND, + USE_EMBEDDED_DEX, + DEFAULT_TO_DEVICE_PROTECTED_STORAGE, + DIRECT_BOOT_AWARE, + PARTIALLY_DIRECT_BOOT_AWARE, + RESIZEABLE_ACTIVITY_VIA_SDK_VERSION, + ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, + ALLOW_AUDIO_PLAYBACK_CAPTURE, + REQUEST_LEGACY_EXTERNAL_STORAGE, + USES_NON_SDK_API, + HAS_FRAGILE_USER_DATA, + CANT_SAVE_STATE, + ALLOW_NATIVE_HEAP_POINTER_TAGGING, + PRESERVE_LEGACY_EXTERNAL_STORAGE, + REQUIRED_FOR_ALL_USERS, + OVERLAY_IS_STATIC, + USE_32_BIT_ABI, + VISIBLE_TO_INSTANT_APPS, + FORCE_QUERYABLE, + CROSS_PROFILE, + ENABLED, + }) + public @interface Values {} + private static final long EXTERNAL_STORAGE = 1L; + private static final long BASE_HARDWARE_ACCELERATED = 1L << 1; + private static final long ALLOW_BACKUP = 1L << 2; + private static final long KILL_AFTER_RESTORE = 1L << 3; + private static final long RESTORE_ANY_VERSION = 1L << 4; + private static final long FULL_BACKUP_ONLY = 1L << 5; + private static final long PERSISTENT = 1L << 6; + private static final long DEBUGGABLE = 1L << 7; + private static final long VM_SAFE_MODE = 1L << 8; + private static final long HAS_CODE = 1L << 9; + private static final long ALLOW_TASK_REPARENTING = 1L << 10; + private static final long ALLOW_CLEAR_USER_DATA = 1L << 11; + private static final long LARGE_HEAP = 1L << 12; + private static final long USES_CLEARTEXT_TRAFFIC = 1L << 13; + private static final long SUPPORTS_RTL = 1L << 14; + private static final long TEST_ONLY = 1L << 15; + private static final long MULTI_ARCH = 1L << 16; + private static final long EXTRACT_NATIVE_LIBS = 1L << 17; + private static final long GAME = 1L << 18; + private static final long STATIC_SHARED_LIBRARY = 1L << 19; + private static final long OVERLAY = 1L << 20; + private static final long ISOLATED_SPLIT_LOADING = 1L << 21; + private static final long HAS_DOMAIN_URLS = 1L << 22; + private static final long PROFILEABLE_BY_SHELL = 1L << 23; + private static final long BACKUP_IN_FOREGROUND = 1L << 24; + private static final long USE_EMBEDDED_DEX = 1L << 25; + private static final long DEFAULT_TO_DEVICE_PROTECTED_STORAGE = 1L << 26; + private static final long DIRECT_BOOT_AWARE = 1L << 27; + private static final long PARTIALLY_DIRECT_BOOT_AWARE = 1L << 28; + private static final long RESIZEABLE_ACTIVITY_VIA_SDK_VERSION = 1L << 29; + private static final long ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE = 1L << 30; + private static final long ALLOW_AUDIO_PLAYBACK_CAPTURE = 1L << 31; + private static final long REQUEST_LEGACY_EXTERNAL_STORAGE = 1L << 32; + private static final long USES_NON_SDK_API = 1L << 33; + private static final long HAS_FRAGILE_USER_DATA = 1L << 34; + private static final long CANT_SAVE_STATE = 1L << 35; + private static final long ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1L << 36; + private static final long PRESERVE_LEGACY_EXTERNAL_STORAGE = 1L << 37; + private static final long REQUIRED_FOR_ALL_USERS = 1L << 38; + private static final long OVERLAY_IS_STATIC = 1L << 39; + private static final long USE_32_BIT_ABI = 1L << 40; + private static final long VISIBLE_TO_INSTANT_APPS = 1L << 41; + private static final long FORCE_QUERYABLE = 1L << 42; + private static final long CROSS_PROFILE = 1L << 43; + private static final long ENABLED = 1L << 44; + } + + private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) { + if (value) { + mBooleans |= flag; + } else { + mBooleans &= ~flag; + } + return this; + } + + private boolean getBoolean(@Booleans.Values long flag) { + return (mBooleans & flag) != 0; + } + + // Derived fields + @NonNull + private UUID mStorageUuid; + private long mLongVersionCode; + @VisibleForTesting public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseApkPath, @NonNull String path, @Nullable TypedArray manifestArray) { @@ -517,10 +592,16 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return this; } + @CallSuper @Override public Object hideAsParsed() { - // There is no equivalent for core-only parsing - throw new UnsupportedOperationException(); + assignDerivedFields(); + return this; + } + + private void assignDerivedFields() { + mStorageUuid = StorageManager.convert(volumeUuid); + mLongVersionCode = PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); } @Override @@ -862,6 +943,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return this; } + @Override + public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) { + nonLocalizedLabel = value == null ? null : value.toString().trim(); + return this; + } + @NonNull @Override public String getProcessName() { @@ -905,7 +992,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { // appInfo.dataDir appInfo.descriptionRes = descriptionRes; // appInfo.deviceProtectedDataDir - appInfo.enabled = enabled; + appInfo.enabled = getBoolean(Booleans.ENABLED); // appInfo.enabledSetting appInfo.fullBackupContent = fullBackupContent; // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy @@ -924,17 +1011,11 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.minAspectRatio = minAspectRatio; appInfo.minSdkVersion = minSdkVersion; appInfo.name = className; - if (appInfo.name != null) { - appInfo.name = appInfo.name.trim(); - } // appInfo.nativeLibraryDir // appInfo.nativeLibraryRootDir // appInfo.nativeLibraryRootRequiresIsa appInfo.networkSecurityConfigRes = networkSecurityConfigRes; appInfo.nonLocalizedLabel = nonLocalizedLabel; - if (appInfo.nonLocalizedLabel != null) { - appInfo.nonLocalizedLabel = appInfo.nonLocalizedLabel.toString().trim(); - } appInfo.packageName = packageName; appInfo.permission = permission; // appInfo.primaryCpuAbi @@ -951,7 +1032,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.splitClassLoaderNames = splitClassLoaderNames; appInfo.splitDependencies = splitDependencies; appInfo.splitNames = splitNames; - appInfo.storageUuid = StorageManager.convert(volumeUuid); + appInfo.storageUuid = mStorageUuid; appInfo.targetSandboxVersion = targetSandboxVersion; appInfo.targetSdkVersion = targetSdkVersion; appInfo.taskAffinity = taskAffinity; @@ -967,7 +1048,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.setResourcePath(mPath); appInfo.setSplitCodePaths(splitCodePaths); appInfo.setSplitResourcePaths(splitCodePaths); - appInfo.setVersionCode(PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode)); + appInfo.setVersionCode(mLongVersionCode); return appInfo; } @@ -994,14 +1075,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { sForInternedString.parcel(this.packageName, dest, flags); dest.writeString(this.realPackage); dest.writeString(this.mBaseApkPath); - dest.writeBoolean(this.requiredForAllUsers); dest.writeString(this.restrictedAccountType); dest.writeString(this.requiredAccountType); sForInternedString.parcel(this.overlayTarget, dest, flags); dest.writeString(this.overlayTargetName); dest.writeString(this.overlayCategory); dest.writeInt(this.overlayPriority); - dest.writeBoolean(this.overlayIsStatic); sForInternedStringValueMap.parcel(this.overlayables, dest, flags); sForInternedString.parcel(this.staticSharedLibName, dest, flags); dest.writeLong(this.staticSharedLibVersion); @@ -1049,9 +1128,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { sForInternedString.parcel(this.volumeUuid, dest, flags); dest.writeParcelable(this.signingDetails, flags); dest.writeString(this.mPath); - dest.writeBoolean(this.use32BitAbi); - dest.writeBoolean(this.visibleToInstantApps); - dest.writeBoolean(this.forceQueryable); dest.writeParcelableList(this.queriesIntents, flags); sForInternedStringList.parcel(this.queriesPackages, dest, flags); dest.writeString(this.appComponentFactory); @@ -1062,8 +1138,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeString(this.className); dest.writeInt(this.compatibleWidthLimitDp); dest.writeInt(this.descriptionRes); - dest.writeBoolean(this.enabled); - dest.writeBoolean(this.crossProfile); dest.writeInt(this.fullBackupContent); dest.writeInt(this.iconRes); dest.writeInt(this.installLocation); @@ -1092,52 +1166,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeIntArray(this.splitFlags); dest.writeStringArray(this.splitNames); dest.writeIntArray(this.splitRevisionCodes); - - dest.writeBoolean(this.externalStorage); - dest.writeBoolean(this.baseHardwareAccelerated); - dest.writeBoolean(this.allowBackup); - dest.writeBoolean(this.killAfterRestore); - dest.writeBoolean(this.restoreAnyVersion); - dest.writeBoolean(this.fullBackupOnly); - dest.writeBoolean(this.persistent); - dest.writeBoolean(this.debuggable); - dest.writeBoolean(this.vmSafeMode); - dest.writeBoolean(this.hasCode); - dest.writeBoolean(this.allowTaskReparenting); - dest.writeBoolean(this.allowClearUserData); - dest.writeBoolean(this.largeHeap); - dest.writeBoolean(this.usesCleartextTraffic); - dest.writeBoolean(this.supportsRtl); - dest.writeBoolean(this.testOnly); - dest.writeBoolean(this.multiArch); - dest.writeBoolean(this.extractNativeLibs); - dest.writeBoolean(this.game); - sForBoolean.parcel(this.resizeableActivity, dest, flags); - - dest.writeBoolean(this.staticSharedLibrary); - dest.writeBoolean(this.overlay); - dest.writeBoolean(this.isolatedSplitLoading); - dest.writeBoolean(this.hasDomainUrls); - dest.writeBoolean(this.profileableByShell); - dest.writeBoolean(this.backupInForeground); - dest.writeBoolean(this.useEmbeddedDex); - dest.writeBoolean(this.defaultToDeviceProtectedStorage); - dest.writeBoolean(this.directBootAware); - dest.writeBoolean(this.partiallyDirectBootAware); - dest.writeBoolean(this.resizeableActivityViaSdkVersion); - dest.writeBoolean(this.allowClearUserDataOnFailedRestore); - dest.writeBoolean(this.allowAudioPlaybackCapture); - dest.writeBoolean(this.requestLegacyExternalStorage); - dest.writeBoolean(this.usesNonSdkApi); - dest.writeBoolean(this.hasFragileUserData); - dest.writeBoolean(this.cantSaveState); - dest.writeBoolean(this.allowNativeHeapPointerTagging); dest.writeInt(this.autoRevokePermissions); - dest.writeBoolean(this.preserveLegacyExternalStorage); dest.writeArraySet(this.mimeGroups); dest.writeInt(this.gwpAsanMode); dest.writeSparseIntArray(this.minExtensionVersions); + dest.writeLong(this.mBooleans); } public ParsingPackageImpl(Parcel in) { @@ -1158,14 +1192,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.packageName = sForInternedString.unparcel(in); this.realPackage = in.readString(); this.mBaseApkPath = in.readString(); - this.requiredForAllUsers = in.readBoolean(); this.restrictedAccountType = in.readString(); this.requiredAccountType = in.readString(); this.overlayTarget = sForInternedString.unparcel(in); this.overlayTargetName = in.readString(); this.overlayCategory = in.readString(); this.overlayPriority = in.readInt(); - this.overlayIsStatic = in.readBoolean(); this.overlayables = sForInternedStringValueMap.unparcel(in); this.staticSharedLibName = sForInternedString.unparcel(in); this.staticSharedLibVersion = in.readLong(); @@ -1213,9 +1245,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.volumeUuid = sForInternedString.unparcel(in); this.signingDetails = in.readParcelable(boot); this.mPath = in.readString(); - this.use32BitAbi = in.readBoolean(); - this.visibleToInstantApps = in.readBoolean(); - this.forceQueryable = in.readBoolean(); this.queriesIntents = in.createTypedArrayList(Intent.CREATOR); this.queriesPackages = sForInternedStringList.unparcel(in); this.appComponentFactory = in.readString(); @@ -1226,8 +1255,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.className = in.readString(); this.compatibleWidthLimitDp = in.readInt(); this.descriptionRes = in.readInt(); - this.enabled = in.readBoolean(); - this.crossProfile = in.readBoolean(); this.fullBackupContent = in.readInt(); this.iconRes = in.readInt(); this.installLocation = in.readInt(); @@ -1256,50 +1283,15 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.splitFlags = in.createIntArray(); this.splitNames = in.createStringArray(); this.splitRevisionCodes = in.createIntArray(); - this.externalStorage = in.readBoolean(); - this.baseHardwareAccelerated = in.readBoolean(); - this.allowBackup = in.readBoolean(); - this.killAfterRestore = in.readBoolean(); - this.restoreAnyVersion = in.readBoolean(); - this.fullBackupOnly = in.readBoolean(); - this.persistent = in.readBoolean(); - this.debuggable = in.readBoolean(); - this.vmSafeMode = in.readBoolean(); - this.hasCode = in.readBoolean(); - this.allowTaskReparenting = in.readBoolean(); - this.allowClearUserData = in.readBoolean(); - this.largeHeap = in.readBoolean(); - this.usesCleartextTraffic = in.readBoolean(); - this.supportsRtl = in.readBoolean(); - this.testOnly = in.readBoolean(); - this.multiArch = in.readBoolean(); - this.extractNativeLibs = in.readBoolean(); - this.game = in.readBoolean(); this.resizeableActivity = sForBoolean.unparcel(in); - this.staticSharedLibrary = in.readBoolean(); - this.overlay = in.readBoolean(); - this.isolatedSplitLoading = in.readBoolean(); - this.hasDomainUrls = in.readBoolean(); - this.profileableByShell = in.readBoolean(); - this.backupInForeground = in.readBoolean(); - this.useEmbeddedDex = in.readBoolean(); - this.defaultToDeviceProtectedStorage = in.readBoolean(); - this.directBootAware = in.readBoolean(); - this.partiallyDirectBootAware = in.readBoolean(); - this.resizeableActivityViaSdkVersion = in.readBoolean(); - this.allowClearUserDataOnFailedRestore = in.readBoolean(); - this.allowAudioPlaybackCapture = in.readBoolean(); - this.requestLegacyExternalStorage = in.readBoolean(); - this.usesNonSdkApi = in.readBoolean(); - this.hasFragileUserData = in.readBoolean(); - this.cantSaveState = in.readBoolean(); - this.allowNativeHeapPointerTagging = in.readBoolean(); this.autoRevokePermissions = in.readInt(); - this.preserveLegacyExternalStorage = in.readBoolean(); this.mimeGroups = (ArraySet<String>) in.readArraySet(boot); this.gwpAsanMode = in.readInt(); this.minExtensionVersions = in.readSparseIntArray(); + this.mBooleans = in.readLong(); + + assignDerivedFields(); } public static final Parcelable.Creator<ParsingPackageImpl> CREATOR = @@ -1367,7 +1359,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isRequiredForAllUsers() { - return requiredForAllUsers; + return getBoolean(Booleans.REQUIRED_FOR_ALL_USERS); } @Nullable @@ -1407,7 +1399,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isOverlayIsStatic() { - return overlayIsStatic; + return getBoolean(Booleans.OVERLAY_IS_STATIC); } @NonNull @@ -1653,17 +1645,17 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isUse32BitAbi() { - return use32BitAbi; + return getBoolean(Booleans.USE_32_BIT_ABI); } @Override public boolean isVisibleToInstantApps() { - return visibleToInstantApps; + return getBoolean(Booleans.VISIBLE_TO_INSTANT_APPS); } @Override public boolean isForceQueryable() { - return forceQueryable; + return getBoolean(Booleans.FORCE_QUERYABLE); } @NonNull @@ -1766,12 +1758,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isEnabled() { - return enabled; + return getBoolean(Booleans.ENABLED); } @Override public boolean isCrossProfile() { - return crossProfile; + return getBoolean(Booleans.CROSS_PROFILE); } @Override @@ -1892,97 +1884,97 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isExternalStorage() { - return externalStorage; + return getBoolean(Booleans.EXTERNAL_STORAGE); } @Override public boolean isBaseHardwareAccelerated() { - return baseHardwareAccelerated; + return getBoolean(Booleans.BASE_HARDWARE_ACCELERATED); } @Override public boolean isAllowBackup() { - return allowBackup; + return getBoolean(Booleans.ALLOW_BACKUP); } @Override public boolean isKillAfterRestore() { - return killAfterRestore; + return getBoolean(Booleans.KILL_AFTER_RESTORE); } @Override public boolean isRestoreAnyVersion() { - return restoreAnyVersion; + return getBoolean(Booleans.RESTORE_ANY_VERSION); } @Override public boolean isFullBackupOnly() { - return fullBackupOnly; + return getBoolean(Booleans.FULL_BACKUP_ONLY); } @Override public boolean isPersistent() { - return persistent; + return getBoolean(Booleans.PERSISTENT); } @Override public boolean isDebuggable() { - return debuggable; + return getBoolean(Booleans.DEBUGGABLE); } @Override public boolean isVmSafeMode() { - return vmSafeMode; + return getBoolean(Booleans.VM_SAFE_MODE); } @Override public boolean isHasCode() { - return hasCode; + return getBoolean(Booleans.HAS_CODE); } @Override public boolean isAllowTaskReparenting() { - return allowTaskReparenting; + return getBoolean(Booleans.ALLOW_TASK_REPARENTING); } @Override public boolean isAllowClearUserData() { - return allowClearUserData; + return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA); } @Override public boolean isLargeHeap() { - return largeHeap; + return getBoolean(Booleans.LARGE_HEAP); } @Override public boolean isUsesCleartextTraffic() { - return usesCleartextTraffic; + return getBoolean(Booleans.USES_CLEARTEXT_TRAFFIC); } @Override public boolean isSupportsRtl() { - return supportsRtl; + return getBoolean(Booleans.SUPPORTS_RTL); } @Override public boolean isTestOnly() { - return testOnly; + return getBoolean(Booleans.TEST_ONLY); } @Override public boolean isMultiArch() { - return multiArch; + return getBoolean(Booleans.MULTI_ARCH); } @Override public boolean isExtractNativeLibs() { - return extractNativeLibs; + return getBoolean(Booleans.EXTRACT_NATIVE_LIBS); } @Override public boolean isGame() { - return game; + return getBoolean(Booleans.GAME); } /** @@ -1996,47 +1988,47 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isStaticSharedLibrary() { - return staticSharedLibrary; + return getBoolean(Booleans.STATIC_SHARED_LIBRARY); } @Override public boolean isOverlay() { - return overlay; + return getBoolean(Booleans.OVERLAY); } @Override public boolean isIsolatedSplitLoading() { - return isolatedSplitLoading; + return getBoolean(Booleans.ISOLATED_SPLIT_LOADING); } @Override public boolean isHasDomainUrls() { - return hasDomainUrls; + return getBoolean(Booleans.HAS_DOMAIN_URLS); } @Override public boolean isProfileableByShell() { - return profileableByShell; + return getBoolean(Booleans.PROFILEABLE_BY_SHELL); } @Override public boolean isBackupInForeground() { - return backupInForeground; + return getBoolean(Booleans.BACKUP_IN_FOREGROUND); } @Override public boolean isUseEmbeddedDex() { - return useEmbeddedDex; + return getBoolean(Booleans.USE_EMBEDDED_DEX); } @Override public boolean isDefaultToDeviceProtectedStorage() { - return defaultToDeviceProtectedStorage; + return getBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE); } @Override public boolean isDirectBootAware() { - return directBootAware; + return getBoolean(Booleans.DIRECT_BOOT_AWARE); } @Override @@ -2046,47 +2038,47 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isPartiallyDirectBootAware() { - return partiallyDirectBootAware; + return getBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE); } @Override public boolean isResizeableActivityViaSdkVersion() { - return resizeableActivityViaSdkVersion; + return getBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION); } @Override public boolean isAllowClearUserDataOnFailedRestore() { - return allowClearUserDataOnFailedRestore; + return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE); } @Override public boolean isAllowAudioPlaybackCapture() { - return allowAudioPlaybackCapture; + return getBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE); } @Override public boolean isRequestLegacyExternalStorage() { - return requestLegacyExternalStorage; + return getBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE); } @Override public boolean isUsesNonSdkApi() { - return usesNonSdkApi; + return getBoolean(Booleans.USES_NON_SDK_API); } @Override public boolean isHasFragileUserData() { - return hasFragileUserData; + return getBoolean(Booleans.HAS_FRAGILE_USER_DATA); } @Override public boolean isCantSaveState() { - return cantSaveState; + return getBoolean(Booleans.CANT_SAVE_STATE); } @Override public boolean isAllowNativeHeapPointerTagging() { - return allowNativeHeapPointerTagging; + return getBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING); } @Override @@ -2096,7 +2088,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean hasPreserveLegacyExternalStorage() { - return preserveLegacyExternalStorage; + return getBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE); } @Override @@ -2113,8 +2105,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setRequiredForAllUsers(boolean value) { - requiredForAllUsers = value; - return this; + return setBoolean(Booleans.REQUIRED_FOR_ALL_USERS, value); } @Override @@ -2125,8 +2116,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setOverlayIsStatic(boolean value) { - overlayIsStatic = value; - return this; + return setBoolean(Booleans.OVERLAY_IS_STATIC, value); } @Override @@ -2173,20 +2163,17 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setUse32BitAbi(boolean value) { - use32BitAbi = value; - return this; + return setBoolean(Booleans.USE_32_BIT_ABI, value); } @Override public ParsingPackageImpl setVisibleToInstantApps(boolean value) { - visibleToInstantApps = value; - return this; + return setBoolean(Booleans.VISIBLE_TO_INSTANT_APPS, value); } @Override public ParsingPackageImpl setForceQueryable(boolean value) { - forceQueryable = value; - return this; + return setBoolean(Booleans.FORCE_QUERYABLE, value); } @Override @@ -2215,14 +2202,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setEnabled(boolean value) { - enabled = value; - return this; + return setBoolean(Booleans.ENABLED, value); } @Override public ParsingPackageImpl setCrossProfile(boolean value) { - crossProfile = value; - return this; + return setBoolean(Booleans.CROSS_PROFILE, value); } @Override @@ -2292,12 +2277,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override - public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) { - nonLocalizedLabel = value; - return this; - } - - @Override public ParsingPackageImpl setRequiresSmallestWidthDp(int value) { requiresSmallestWidthDp = value; return this; @@ -2335,116 +2314,97 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setExternalStorage(boolean value) { - externalStorage = value; - return this; + return setBoolean(Booleans.EXTERNAL_STORAGE, value); } @Override public ParsingPackageImpl setBaseHardwareAccelerated(boolean value) { - baseHardwareAccelerated = value; - return this; + return setBoolean(Booleans.BASE_HARDWARE_ACCELERATED, value); } @Override public ParsingPackageImpl setAllowBackup(boolean value) { - allowBackup = value; - return this; + return setBoolean(Booleans.ALLOW_BACKUP, value); } @Override public ParsingPackageImpl setKillAfterRestore(boolean value) { - killAfterRestore = value; - return this; + return setBoolean(Booleans.KILL_AFTER_RESTORE, value); } @Override public ParsingPackageImpl setRestoreAnyVersion(boolean value) { - restoreAnyVersion = value; - return this; + return setBoolean(Booleans.RESTORE_ANY_VERSION, value); } @Override public ParsingPackageImpl setFullBackupOnly(boolean value) { - fullBackupOnly = value; - return this; + return setBoolean(Booleans.FULL_BACKUP_ONLY, value); } @Override public ParsingPackageImpl setPersistent(boolean value) { - persistent = value; - return this; + return setBoolean(Booleans.PERSISTENT, value); } @Override public ParsingPackageImpl setDebuggable(boolean value) { - debuggable = value; - return this; + return setBoolean(Booleans.DEBUGGABLE, value); } @Override public ParsingPackageImpl setVmSafeMode(boolean value) { - vmSafeMode = value; - return this; + return setBoolean(Booleans.VM_SAFE_MODE, value); } @Override public ParsingPackageImpl setHasCode(boolean value) { - hasCode = value; - return this; + return setBoolean(Booleans.HAS_CODE, value); } @Override public ParsingPackageImpl setAllowTaskReparenting(boolean value) { - allowTaskReparenting = value; - return this; + return setBoolean(Booleans.ALLOW_TASK_REPARENTING, value); } @Override public ParsingPackageImpl setAllowClearUserData(boolean value) { - allowClearUserData = value; - return this; + return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA, value); } @Override public ParsingPackageImpl setLargeHeap(boolean value) { - largeHeap = value; - return this; + return setBoolean(Booleans.LARGE_HEAP, value); } @Override public ParsingPackageImpl setUsesCleartextTraffic(boolean value) { - usesCleartextTraffic = value; - return this; + return setBoolean(Booleans.USES_CLEARTEXT_TRAFFIC, value); } @Override public ParsingPackageImpl setSupportsRtl(boolean value) { - supportsRtl = value; - return this; + return setBoolean(Booleans.SUPPORTS_RTL, value); } @Override public ParsingPackageImpl setTestOnly(boolean value) { - testOnly = value; - return this; + return setBoolean(Booleans.TEST_ONLY, value); } @Override public ParsingPackageImpl setMultiArch(boolean value) { - multiArch = value; - return this; + return setBoolean(Booleans.MULTI_ARCH, value); } @Override public ParsingPackageImpl setExtractNativeLibs(boolean value) { - extractNativeLibs = value; - return this; + return setBoolean(Booleans.EXTRACT_NATIVE_LIBS, value); } @Override public ParsingPackageImpl setGame(boolean value) { - game = value; - return this; + return setBoolean(Booleans.GAME, value); } /** @@ -2458,56 +2418,47 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setStaticSharedLibrary(boolean value) { - staticSharedLibrary = value; - return this; + return setBoolean(Booleans.STATIC_SHARED_LIBRARY, value); } @Override public ParsingPackageImpl setOverlay(boolean value) { - overlay = value; - return this; + return setBoolean(Booleans.OVERLAY, value); } @Override public ParsingPackageImpl setIsolatedSplitLoading(boolean value) { - isolatedSplitLoading = value; - return this; + return setBoolean(Booleans.ISOLATED_SPLIT_LOADING, value); } @Override public ParsingPackageImpl setHasDomainUrls(boolean value) { - hasDomainUrls = value; - return this; + return setBoolean(Booleans.HAS_DOMAIN_URLS, value); } @Override public ParsingPackageImpl setProfileableByShell(boolean value) { - profileableByShell = value; - return this; + return setBoolean(Booleans.PROFILEABLE_BY_SHELL, value); } @Override public ParsingPackageImpl setBackupInForeground(boolean value) { - backupInForeground = value; - return this; + return setBoolean(Booleans.BACKUP_IN_FOREGROUND, value); } @Override public ParsingPackageImpl setUseEmbeddedDex(boolean value) { - useEmbeddedDex = value; - return this; + return setBoolean(Booleans.USE_EMBEDDED_DEX, value); } @Override public ParsingPackageImpl setDefaultToDeviceProtectedStorage(boolean value) { - defaultToDeviceProtectedStorage = value; - return this; + return setBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE, value); } @Override public ParsingPackageImpl setDirectBootAware(boolean value) { - directBootAware = value; - return this; + return setBoolean(Booleans.DIRECT_BOOT_AWARE, value); } @Override @@ -2518,56 +2469,47 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) { - partiallyDirectBootAware = value; - return this; + return setBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE, value); } @Override public ParsingPackageImpl setResizeableActivityViaSdkVersion(boolean value) { - resizeableActivityViaSdkVersion = value; - return this; + return setBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION, value); } @Override public ParsingPackageImpl setAllowClearUserDataOnFailedRestore(boolean value) { - allowClearUserDataOnFailedRestore = value; - return this; + return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, value); } @Override public ParsingPackageImpl setAllowAudioPlaybackCapture(boolean value) { - allowAudioPlaybackCapture = value; - return this; + return setBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE, value); } @Override public ParsingPackageImpl setRequestLegacyExternalStorage(boolean value) { - requestLegacyExternalStorage = value; - return this; + return setBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE, value); } @Override public ParsingPackageImpl setUsesNonSdkApi(boolean value) { - usesNonSdkApi = value; - return this; + return setBoolean(Booleans.USES_NON_SDK_API, value); } @Override public ParsingPackageImpl setHasFragileUserData(boolean value) { - hasFragileUserData = value; - return this; + return setBoolean(Booleans.HAS_FRAGILE_USER_DATA, value); } @Override public ParsingPackageImpl setCantSaveState(boolean value) { - cantSaveState = value; - return this; + return setBoolean(Booleans.CANT_SAVE_STATE, value); } @Override public ParsingPackageImpl setAllowNativeHeapPointerTagging(boolean value) { - allowNativeHeapPointerTagging = value; - return this; + return setBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING, value); } @Override @@ -2578,8 +2520,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setPreserveLegacyExternalStorage(boolean value) { - preserveLegacyExternalStorage = value; - return this; + return setBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE, value); } @Override @@ -2644,7 +2585,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setClassName(@Nullable String className) { - this.className = className; + this.className = className == null ? null : className.trim(); return this; } diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index dbd15f544bcd..acd6305a1442 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -867,7 +867,7 @@ public interface ParsingPackageRead extends Parcelable { * @see ApplicationInfo#gwpAsanMode * @see R.styleable#AndroidManifest_gwpAsanMode */ - public int getGwpAsanMode(); + int getGwpAsanMode(); // TODO(b/135203078): Hide and enforce going through PackageInfoUtils ApplicationInfo toAppInfoWithoutState(); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index bce75cde20c2..9197020e1fce 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -185,6 +185,9 @@ public class ParsingPackageUtils { ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */)); } + // Need to call this to finish the parsing stage + pkg.hideAsParsed(); + return input.success(pkg); } catch (PackageParser.PackageParserException e) { return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index b0437ac7284e..f5e171905096 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -365,7 +365,7 @@ public final class ApkAssets { try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { XmlResourceParser parser = block.newParser(); // If nativeOpenXml doesn't throw, it will always return a valid native pointer, - // which makes newParser always return non-null. But let's be paranoid. + // which makes newParser always return non-null. But let's be careful. if (parser == null) { throw new AssertionError("block.newParser() returned a null parser"); } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 62c7b85fa62d..69dd25fa41d7 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -1058,7 +1058,7 @@ public final class AssetManager implements AutoCloseable { XmlResourceParser parser = block.newParser(); // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with // a valid native pointer, which makes newParser always return non-null. But let's - // be paranoid. + // be careful. if (parser == null) { throw new AssertionError("block.newParser() returned a null parser"); } diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index dd3493063e28..a9b613228070 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -26,6 +26,7 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; import dalvik.system.CloseGuard; @@ -141,8 +142,6 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { /** Usage: The buffer contains a complete mipmap hierarchy */ public static final long USAGE_GPU_MIPMAP_COMPLETE = 1 << 26; - // The approximate size of a native AHardwareBuffer object. - private static final long NATIVE_HARDWARE_BUFFER_SIZE = 232; /** * Creates a new <code>HardwareBuffer</code> instance. * @@ -239,10 +238,10 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private HardwareBuffer(long nativeObject) { mNativeObject = nativeObject; - + long bufferSize = nEstimateSize(nativeObject); ClassLoader loader = HardwareBuffer.class.getClassLoader(); NativeAllocationRegistry registry = new NativeAllocationRegistry( - loader, nGetNativeFinalizer(), NATIVE_HARDWARE_BUFFER_SIZE); + loader, nGetNativeFinalizer(), bufferSize); mCleaner = registry.registerNativeAllocation(this, mNativeObject); mCloseGuard.open("close"); } @@ -429,4 +428,6 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { private static native long nGetUsage(long nativeObject); private static native boolean nIsSupported(int width, int height, int format, int layers, long usage); + @CriticalNative + private static native long nEstimateSize(long nativeObject); } diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java index b35776072dc9..fd98d37bb7f4 100644 --- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java +++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java @@ -33,7 +33,7 @@ public interface BiometricAuthenticator { /** * No biometric methods or nothing has been enrolled. - * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist" + * Move/expose these in BiometricPrompt if we ever want to allow applications to "denylist" * modalities when calling authenticate(). * @hide */ diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index 52251ba90b98..9d32a809fe0d 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -1320,6 +1320,8 @@ public final class StreamConfigurationMap { return ImageFormat.DEPTH16; case HAL_PIXEL_FORMAT_RAW16: return ImageFormat.RAW_DEPTH; + case HAL_PIXEL_FORMAT_RAW10: + return ImageFormat.RAW_DEPTH10; case ImageFormat.JPEG: throw new IllegalArgumentException( "ImageFormat.JPEG is an unknown internal format"); @@ -1393,6 +1395,8 @@ public final class StreamConfigurationMap { return HAL_PIXEL_FORMAT_Y16; case ImageFormat.RAW_DEPTH: return HAL_PIXEL_FORMAT_RAW16; + case ImageFormat.RAW_DEPTH10: + return HAL_PIXEL_FORMAT_RAW10; default: return format; } @@ -1437,6 +1441,7 @@ public final class StreamConfigurationMap { case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.DEPTH16: case ImageFormat.RAW_DEPTH: + case ImageFormat.RAW_DEPTH10: return HAL_DATASPACE_DEPTH; case ImageFormat.DEPTH_JPEG: return HAL_DATASPACE_DYNAMIC_DEPTH; @@ -1878,6 +1883,8 @@ public final class StreamConfigurationMap { return "DEPTH_JPEG"; case ImageFormat.RAW_DEPTH: return "RAW_DEPTH"; + case ImageFormat.RAW_DEPTH10: + return "RAW_DEPTH10"; case ImageFormat.PRIVATE: return "PRIVATE"; case ImageFormat.HEIC: diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index ece5c28884fa..7dc1eaabdc9c 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -138,6 +138,11 @@ public class AmbientDisplayConfiguration { } /** {@hide} */ + public String udfpsLongPressSensorType() { + return mContext.getResources().getString(R.string.config_dozeUdfpsLongPressSensorType); + } + + /** {@hide} */ public boolean pulseOnLongPressEnabled(int user) { return pulseOnLongPressAvailable() && boolSettingDefaultOff( Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 55afefed734e..27bb9e20f968 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -454,20 +454,20 @@ public class InputMethodService extends AbstractInputMethodService { /** * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} * The original app window token is passed from client app window. - * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy - * token to identify this window. - * This dummy token is only valid for a single call to {@link InputMethodImpl#showSoftInput}, - * after which it is set null until next call. + * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique + * placeholder token to identify this window. + * This placeholder token is only valid for a single call to + * {@link InputMethodImpl#showSoftInput}, after which it is set null until next call. */ private IBinder mCurShowInputToken; /** * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#hideSoftInput} * The original app window token is passed from client app window. - * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy - * token to identify this window. - * This dummy token is only valid for a single call to {@link InputMethodImpl#hideSoftInput}, - * after which it is set {@code null} until next call. + * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique + * placeholder token to identify this window. + * This placeholder token is only valid for a single call to + * {@link InputMethodImpl#hideSoftInput}, after which it is set {@code null} until next call. */ private IBinder mCurHideInputToken; diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 1922b6df2e7f..e9e242e2b08a 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -84,7 +84,7 @@ public class NetworkPolicyManager { * The RULE_xxx_ALL rules applies to all networks (metered or non-metered), but on * metered networks, the RULE_xxx_METERED rules should be checked first. For example, * if the device is on Battery Saver Mode and Data Saver Mode simulatenously, and a uid - * is whitelisted for the former but not the latter, its status would be + * is allowlisted for the former but not the latter, its status would be * RULE_REJECT_METERED | RULE_ALLOW_ALL, meaning it could have access to non-metered * networks but not to metered networks. * diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index 8b6ac4271c30..e51145800cc7 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -66,7 +66,7 @@ import javax.net.ssl.X509TrustManager; * must verify the identity of the server you are connected to. * * <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not - * verify the server's identity, allowing man-in-the-middle attacks.</b> + * verify the server's identity, allowing person-in-the-middle attacks.</b> * This implementation does check the server's certificate hostname, but only * for createSocket variants that specify a hostname. When using methods that * use {@link InetAddress} or which return an unconnected socket, you MUST @@ -176,7 +176,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * disabled, using an optional handshake timeout and SSL session cache. * * <p class="caution"><b>Warning:</b> Sockets created using this factory - * are vulnerable to man-in-the-middle attacks!</p> + * are vulnerable to person-in-the-middle attacks!</p> * * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. @@ -508,7 +508,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * * <p class="caution"><b>Warning:</b> Hostname verification is not performed * with this method. You MUST verify the server's identity after connecting - * the socket to avoid man-in-the-middle attacks.</p> + * the socket to avoid person-in-the-middle attacks.</p> */ @Override public Socket createSocket() throws IOException { @@ -527,7 +527,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * * <p class="caution"><b>Warning:</b> Hostname verification is not performed * with this method. You MUST verify the server's identity after connecting - * the socket to avoid man-in-the-middle attacks.</p> + * the socket to avoid person-in-the-middle attacks.</p> */ @Override public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) @@ -548,7 +548,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * * <p class="caution"><b>Warning:</b> Hostname verification is not performed * with this method. You MUST verify the server's identity after connecting - * the socket to avoid man-in-the-middle attacks.</p> + * the socket to avoid person-in-the-middle attacks.</p> */ @Override public Socket createSocket(InetAddress addr, int port) throws IOException { diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java index b1de74e817bc..92c543294ae1 100644 --- a/core/java/android/net/apf/ApfCapabilities.java +++ b/core/java/android/net/apf/ApfCapabilities.java @@ -127,7 +127,7 @@ public final class ApfCapabilities implements Parcelable { } /** - * @return An array of blacklisted EtherType, packets with EtherTypes within it will be dropped. + * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped. */ public static @NonNull int[] getApfEtherTypeBlackList() { return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList); diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index 7bf82e84927f..b138a3f01f49 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -57,7 +57,7 @@ public final class CardEmulation { /** * Activity action: ask the user to change the default * card emulation service for a certain category. This will - * show a dialog that asks the user whether he wants to + * show a dialog that asks the user whether they want to * replace the current default service with the service * identified with the ComponentName specified in * {@link #EXTRA_SERVICE_COMPONENT}, for the category @@ -113,7 +113,7 @@ public final class CardEmulation { * * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService} * or {@link OffHostApduService}, whenever an Application ID (AID) of this category - * is selected, the user is asked which service he wants to use to handle + * is selected, the user is asked which service they want to use to handle * the transaction, even if there is only one matching service. */ public static final int SELECTION_MODE_ALWAYS_ASK = 1; @@ -286,7 +286,7 @@ public final class CardEmulation { * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default * service for this category, which will be preferred. * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked - * every time what service he would like to use in this category. + * every time what service they would like to use in this category. * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked * to pick a service if there is a conflict. * @param category The category, for example {@link #CATEGORY_PAYMENT} diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java index 9208d637a291..55d0e73780a2 100644 --- a/core/java/android/nfc/cardemulation/HostApduService.java +++ b/core/java/android/nfc/cardemulation/HostApduService.java @@ -372,7 +372,7 @@ public abstract class HostApduService extends Service { * from a remote device. A response APDU can be provided directly * by returning a byte-array in this method. Note that in general * response APDUs must be sent as quickly as possible, given the fact - * that the user is likely holding his device over an NFC reader + * that the user is likely holding their device over an NFC reader * when this method is called. * * <p class="note">If there are multiple services that have registered for the same diff --git a/core/java/android/nfc/cardemulation/HostNfcFService.java b/core/java/android/nfc/cardemulation/HostNfcFService.java index fd0d8adf2998..65b5ca77de62 100644 --- a/core/java/android/nfc/cardemulation/HostNfcFService.java +++ b/core/java/android/nfc/cardemulation/HostNfcFService.java @@ -256,7 +256,7 @@ public abstract class HostNfcFService extends Service { * from a remote device. A response packet can be provided directly * by returning a byte-array in this method. Note that in general * response packets must be sent as quickly as possible, given the fact - * that the user is likely holding his device over an NFC reader + * that the user is likely holding their device over an NFC reader * when this method is called. * * <p class="note">This method is running on the main thread of your application. diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 81deba463fc0..1d28489d2ac8 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -1613,7 +1613,7 @@ public class BaseBundle { return; } int lengthPos = parcel.dataPosition(); - parcel.writeInt(-1); // dummy, will hold length + parcel.writeInt(-1); // placeholder, will hold length parcel.writeInt(BUNDLE_MAGIC); int startPos = parcel.dataPosition(); diff --git a/core/java/android/os/BasicShellCommandHandler.java b/core/java/android/os/BasicShellCommandHandler.java index 1ea2229cff1f..366da3db0010 100644 --- a/core/java/android/os/BasicShellCommandHandler.java +++ b/core/java/android/os/BasicShellCommandHandler.java @@ -264,6 +264,18 @@ public abstract class BasicShellCommandHandler { } /** + * @return all the remaining arguments in the command without moving the current position. + */ + public String[] peekRemainingArgs() { + int remaining = getRemainingArgsCount(); + String[] args = new String[remaining]; + for (int pos = mArgPos; pos < mArgs.length; pos++) { + args[pos - mArgPos] = mArgs[pos]; + } + return args; + } + + /** * Returns number of arguments that haven't been processed yet. */ public int getRemainingArgsCount() { diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index fecfd3c2dc7a..fdbf79a9f5d2 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1782,7 +1782,7 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_PACKAGE_INACTIVE = 0x000f; // Event for a package becoming active due to an interaction. public static final int EVENT_PACKAGE_ACTIVE = 0x0010; - // Event for a package being on the temporary whitelist. + // Event for a package being on the temporary allowlist. public static final int EVENT_TEMP_WHITELIST = 0x0011; // Event for the screen waking up. public static final int EVENT_SCREEN_WAKE_UP = 0x0012; diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index eaf6198b67ad..085681d412e9 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -29,6 +29,7 @@ import android.compat.annotation.Disabled; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; @@ -73,12 +74,29 @@ public class Environment { private static final String DIR_FILES = "files"; private static final String DIR_CACHE = "cache"; + /** + * The folder name prefix for the user credential protected data directory. This is exposed for + * use in string path caching for {@link ApplicationInfo} objects, and should not be accessed + * directly otherwise. Prefer {@link #getDataUserCeDirectory(String, int)}. + * {@hide} + */ + public static final String DIR_USER_CE = "user"; + + /** + * The folder name prefix for the user device protected data directory. This is exposed for use + * in string path caching for {@link ApplicationInfo} objects, and should not be accessed + * directly otherwise. Prefer {@link #getDataUserDeDirectory(String, int)}. + * {@hide} + */ + public static final String DIR_USER_DE = "user_de"; + /** {@hide} */ @Deprecated public static final String DIRECTORY_ANDROID = DIR_ANDROID; private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system"); - private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data"); + private static final String DIR_ANDROID_DATA_PATH = getDirectoryPath(ENV_ANDROID_DATA, "/data"); + private static final File DIR_ANDROID_DATA = new File(DIR_ANDROID_DATA_PATH); private static final File DIR_ANDROID_EXPAND = getDirectory(ENV_ANDROID_EXPAND, "/mnt/expand"); private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage"); private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache"); @@ -357,6 +375,14 @@ public class Environment { return DIR_ANDROID_DATA; } + /** + * @see #getDataDirectory() + * @hide + */ + public static String getDataDirectoryPath() { + return DIR_ANDROID_DATA_PATH; + } + /** {@hide} */ public static File getDataDirectory(String volumeUuid) { if (TextUtils.isEmpty(volumeUuid)) { @@ -366,6 +392,15 @@ public class Environment { } } + /** @hide */ + public static String getDataDirectoryPath(String volumeUuid) { + if (TextUtils.isEmpty(volumeUuid)) { + return DIR_ANDROID_DATA_PATH; + } else { + return getExpandDirectory().getAbsolutePath() + File.separator + volumeUuid; + } + } + /** {@hide} */ public static File getExpandDirectory() { return DIR_ANDROID_EXPAND; @@ -489,7 +524,7 @@ public class Environment { /** {@hide} */ public static File getDataUserCeDirectory(String volumeUuid) { - return new File(getDataDirectory(volumeUuid), "user"); + return new File(getDataDirectory(volumeUuid), DIR_USER_CE); } /** {@hide} */ @@ -506,7 +541,7 @@ public class Environment { /** {@hide} */ public static File getDataUserDeDirectory(String volumeUuid) { - return new File(getDataDirectory(volumeUuid), "user_de"); + return new File(getDataDirectory(volumeUuid), DIR_USER_DE); } /** {@hide} */ @@ -1372,6 +1407,12 @@ public class Environment { return path == null ? new File(defaultPath) : new File(path); } + @NonNull + static String getDirectoryPath(@NonNull String variableName, @NonNull String defaultPath) { + String path = System.getenv(variableName); + return path == null ? defaultPath : path; + } + /** {@hide} */ public static void setUserRequired(boolean userRequired) { sUserRequired = userRequired; diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 415e5a60a140..1bddc49f839c 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -33,9 +33,10 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import com.android.internal.annotations.GuardedBy; + import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; -import dalvik.system.VMRuntime; import libcore.util.ArrayUtils; import libcore.util.SneakyThrow; @@ -222,9 +223,31 @@ public final class Parcel { */ private static boolean sParcelExceptionStackTrace; - private static final int POOL_SIZE = 6; - private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE]; - private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE]; + private static final Object sPoolSync = new Object(); + + /** Next item in the linked list pool, if any */ + @GuardedBy("sPoolSync") + private Parcel mPoolNext; + + /** Head of a linked list pool of {@link Parcel} objects */ + @GuardedBy("sPoolSync") + private static Parcel sOwnedPool; + /** Head of a linked list pool of {@link Parcel} objects */ + @GuardedBy("sPoolSync") + private static Parcel sHolderPool; + + /** Total size of pool with head at {@link #sOwnedPool} */ + @GuardedBy("sPoolSync") + private static int sOwnedPoolSize = 0; + /** Total size of pool with head at {@link #sHolderPool} */ + @GuardedBy("sPoolSync") + private static int sHolderPoolSize = 0; + + /** + * We're willing to pool up to 32 objects, which is sized to accommodate + * both a data and reply Parcel for the maximum of 16 Binder threads. + */ + private static final int POOL_SIZE = 32; // Keep in sync with frameworks/native/include/private/binder/ParcelValTypes.h. private static final int VAL_NULL = -1; @@ -285,7 +308,7 @@ public final class Parcel { @CriticalNative private static native int nativeDataCapacity(long nativePtr); @FastNative - private static native long nativeSetDataSize(long nativePtr, int size); + private static native void nativeSetDataSize(long nativePtr, int size); @CriticalNative private static native void nativeSetDataPosition(long nativePtr, int pos); @FastNative @@ -314,7 +337,7 @@ public final class Parcel { @FastNative private static native void nativeWriteStrongBinder(long nativePtr, IBinder val); @FastNative - private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val); + private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val); private static native byte[] nativeCreateByteArray(long nativePtr); private static native boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen); @@ -337,14 +360,14 @@ public final class Parcel { private static native FileDescriptor nativeReadFileDescriptor(long nativePtr); private static native long nativeCreate(); - private static native long nativeFreeBuffer(long nativePtr); + private static native void nativeFreeBuffer(long nativePtr); private static native void nativeDestroy(long nativePtr); private static native byte[] nativeMarshall(long nativePtr); - private static native long nativeUnmarshall( + private static native void nativeUnmarshall( long nativePtr, byte[] data, int offset, int length); private static native int nativeCompareData(long thisNativePtr, long otherNativePtr); - private static native long nativeAppendFrom( + private static native void nativeAppendFrom( long thisNativePtr, long otherNativePtr, int offset, int length); @CriticalNative private static native boolean nativeHasFileDescriptors(long nativePtr); @@ -420,22 +443,27 @@ public final class Parcel { */ @NonNull public static Parcel obtain() { - final Parcel[] pool = sOwnedPool; - synchronized (pool) { - Parcel p; - for (int i=0; i<POOL_SIZE; i++) { - p = pool[i]; - if (p != null) { - pool[i] = null; - if (DEBUG_RECYCLE) { - p.mStack = new RuntimeException(); - } - p.mReadWriteHelper = ReadWriteHelper.DEFAULT; - return p; - } + Parcel res = null; + synchronized (sPoolSync) { + if (sOwnedPool != null) { + res = sOwnedPool; + sOwnedPool = res.mPoolNext; + res.mPoolNext = null; + sOwnedPoolSize--; + } + } + + // When no cache found above, create from scratch; otherwise prepare the + // cached object to be used + if (res == null) { + res = new Parcel(0); + } else { + if (DEBUG_RECYCLE) { + res.mStack = new RuntimeException(); } + res.mReadWriteHelper = ReadWriteHelper.DEFAULT; } - return new Parcel(0); + return res; } /** @@ -446,19 +474,21 @@ public final class Parcel { if (DEBUG_RECYCLE) mStack = null; freeBuffer(); - final Parcel[] pool; if (mOwnsNativeParcelObject) { - pool = sOwnedPool; + synchronized (sPoolSync) { + if (sOwnedPoolSize < POOL_SIZE) { + mPoolNext = sOwnedPool; + sOwnedPool = this; + sOwnedPoolSize++; + } + } } else { mNativePtr = 0; - pool = sHolderPool; - } - - synchronized (pool) { - for (int i=0; i<POOL_SIZE; i++) { - if (pool[i] == null) { - pool[i] = this; - return; + synchronized (sPoolSync) { + if (sHolderPoolSize < POOL_SIZE) { + mPoolNext = sHolderPool; + sHolderPool = this; + sHolderPoolSize++; } } } @@ -532,7 +562,7 @@ public final class Parcel { * @param size The new number of bytes in the Parcel. */ public final void setDataSize(int size) { - updateNativeSize(nativeSetDataSize(mNativePtr, size)); + nativeSetDataSize(mNativePtr, size); } /** @@ -584,11 +614,11 @@ public final class Parcel { * Set the bytes in data to be the raw bytes of this Parcel. */ public final void unmarshall(@NonNull byte[] data, int offset, int length) { - updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length)); + nativeUnmarshall(mNativePtr, data, offset, length); } public final void appendFrom(Parcel parcel, int offset, int length) { - updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length)); + nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length); } /** @hide */ @@ -871,24 +901,7 @@ public final class Parcel { * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p> */ public final void writeFileDescriptor(@NonNull FileDescriptor val) { - updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val)); - } - - private void updateNativeSize(long newNativeSize) { - if (mOwnsNativeParcelObject) { - if (newNativeSize > Integer.MAX_VALUE) { - newNativeSize = Integer.MAX_VALUE; - } - if (newNativeSize != mNativeSize) { - int delta = (int) (newNativeSize - mNativeSize); - if (delta > 0) { - VMRuntime.getRuntime().registerNativeAllocation(delta); - } else { - VMRuntime.getRuntime().registerNativeFree(-delta); - } - mNativeSize = newNativeSize; - } - } + nativeWriteFileDescriptor(mNativePtr, val); } /** @@ -3496,22 +3509,27 @@ public final class Parcel { /** @hide */ static protected final Parcel obtain(long obj) { - final Parcel[] pool = sHolderPool; - synchronized (pool) { - Parcel p; - for (int i=0; i<POOL_SIZE; i++) { - p = pool[i]; - if (p != null) { - pool[i] = null; - if (DEBUG_RECYCLE) { - p.mStack = new RuntimeException(); - } - p.init(obj); - return p; - } + Parcel res = null; + synchronized (sPoolSync) { + if (sHolderPool != null) { + res = sHolderPool; + sHolderPool = res.mPoolNext; + res.mPoolNext = null; + sHolderPoolSize--; + } + } + + // When no cache found above, create from scratch; otherwise prepare the + // cached object to be used + if (res == null) { + res = new Parcel(obj); + } else { + if (DEBUG_RECYCLE) { + res.mStack = new RuntimeException(); } + res.init(obj); } - return new Parcel(obj); + return res; } private Parcel(long nativePtr) { @@ -3535,7 +3553,7 @@ public final class Parcel { private void freeBuffer() { resetSqaushingState(); if (mOwnsNativeParcelObject) { - updateNativeSize(nativeFreeBuffer(mNativePtr)); + nativeFreeBuffer(mNativePtr); } mReadWriteHelper = ReadWriteHelper.DEFAULT; } @@ -3545,7 +3563,6 @@ public final class Parcel { if (mNativePtr != 0) { if (mOwnsNativeParcelObject) { nativeDestroy(mNativePtr); - updateNativeSize(0); } mNativePtr = 0; } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index ed38b3ff78e5..3265829e5061 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -274,7 +274,6 @@ public final class PowerManager { /** * Brightness value for fully off in float. - * TODO(brightnessfloat): rename this to BRIGHTNES_OFF and remove the integer-based constant. * @hide */ public static final float BRIGHTNESS_OFF_FLOAT = -1.0f; @@ -1860,8 +1859,8 @@ public final class PowerManager { } /** - * Return whether the given application package name is on the device's power whitelist. - * Apps can be placed on the whitelist through the settings UI invoked by + * Return whether the given application package name is on the device's power allowlist. + * Apps can be placed on the allowlist through the settings UI invoked by * {@link android.provider.Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}. */ public boolean isIgnoringBatteryOptimizations(String packageName) { @@ -2296,7 +2295,7 @@ public final class PowerManager { = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED"; /** - * @hide Intent that is broadcast when the set of power save whitelist apps has changed. + * @hide Intent that is broadcast when the set of power save allowlist apps has changed. * This broadcast is only sent to registered receivers. */ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) @@ -2304,7 +2303,7 @@ public final class PowerManager { = "android.os.action.POWER_SAVE_WHITELIST_CHANGED"; /** - * @hide Intent that is broadcast when the set of temporarily whitelisted apps has changed. + * @hide Intent that is broadcast when the set of temporarily allowlisted apps has changed. * This broadcast is only sent to registered receivers. */ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index efea9537c4cf..ca0981b46182 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -32,6 +32,7 @@ import dalvik.system.VMRuntime; import libcore.io.IoUtils; import java.io.FileDescriptor; +import java.io.IOException; import java.util.Map; import java.util.concurrent.TimeoutException; @@ -614,7 +615,7 @@ public class Process { * started. * @param pkgDataInfoMap Map from related package names to private data directory * volume UUID and inode number. - * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory * volume UUID and inode number. * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. @@ -1317,33 +1318,16 @@ public class Process { */ public static void waitForProcessDeath(int pid, int timeout) throws InterruptedException, TimeoutException { - FileDescriptor pidfd = null; - if (sPidFdSupported == PIDFD_UNKNOWN) { - int fd = -1; + boolean fallback = supportsPidFd(); + if (!fallback) { + FileDescriptor pidfd = null; try { - fd = nativePidFdOpen(pid, 0); - sPidFdSupported = PIDFD_SUPPORTED; - } catch (ErrnoException e) { - sPidFdSupported = e.errno != OsConstants.ENOSYS - ? PIDFD_SUPPORTED : PIDFD_UNSUPPORTED; - } finally { + final int fd = nativePidFdOpen(pid, 0); if (fd >= 0) { pidfd = new FileDescriptor(); pidfd.setInt$(fd); - } - } - } - boolean fallback = sPidFdSupported == PIDFD_UNSUPPORTED; - if (!fallback) { - try { - if (pidfd == null) { - int fd = nativePidFdOpen(pid, 0); - if (fd >= 0) { - pidfd = new FileDescriptor(); - pidfd.setInt$(fd); - } else { - fallback = true; - } + } else { + fallback = true; } if (pidfd != null) { StructPollfd[] fds = new StructPollfd[] { @@ -1392,5 +1376,59 @@ public class Process { throw new TimeoutException(); } + /** + * Determine whether the system supports pidfd APIs + * + * @return Returns true if the system supports pidfd APIs + * @hide + */ + public static boolean supportsPidFd() { + if (sPidFdSupported == PIDFD_UNKNOWN) { + int fd = -1; + try { + fd = nativePidFdOpen(myPid(), 0); + sPidFdSupported = PIDFD_SUPPORTED; + } catch (ErrnoException e) { + sPidFdSupported = e.errno != OsConstants.ENOSYS + ? PIDFD_SUPPORTED : PIDFD_UNSUPPORTED; + } finally { + if (fd >= 0) { + final FileDescriptor f = new FileDescriptor(); + f.setInt$(fd); + IoUtils.closeQuietly(f); + } + } + } + return sPidFdSupported == PIDFD_SUPPORTED; + } + + /** + * Open process file descriptor for given pid. + * + * @param pid The process ID to open for + * @param flags Reserved, unused now, must be 0 + * @return The process file descriptor for given pid + * @throws IOException if it can't be opened + * + * @hide + */ + public static @Nullable FileDescriptor openPidFd(int pid, int flags) throws IOException { + if (!supportsPidFd()) { + return null; + } + if (flags != 0) { + throw new IllegalArgumentException(); + } + try { + FileDescriptor pidfd = new FileDescriptor(); + pidfd.setInt$(nativePidFdOpen(pid, flags)); + return pidfd; + } catch (ErrnoException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } + } + private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException; } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 257bc5b64285..6c5b04a649e2 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -2500,7 +2500,7 @@ public final class StrictMode { * that happens while this span is active. You must call finish() on the span when done. * * <p>This will never return null, but on devices without debugging enabled, this may return a - * dummy object on which the finish() method is a no-op. + * placeholder object on which the finish() method is a no-op. * * <p>TODO: add CloseGuard to this, verifying callers call finish. * diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING index 39c196d1a06c..1bdc82a82c6c 100644 --- a/core/java/android/os/TEST_MAPPING +++ b/core/java/android/os/TEST_MAPPING @@ -29,6 +29,15 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] + }, + { + "file_patterns": ["Environment\\.java"], + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.pm.parsing.PackageInfoUserFieldsTest" + } + ] } ], "postsubmit": [ diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index e907e2204b7b..5a48242f26ea 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -109,7 +109,7 @@ public class UpdateEngine { * Error code: an update failed to apply due to a mismatch in payload * hash. * - * <p>Update engine does sanity checks for the given payload and its + * <p>Update engine does validity checks for the given payload and its * metadata. */ public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 81ffefd05b19..ddc21ab2c8c0 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -830,7 +830,7 @@ public class UserManager { public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone"; /** - * Specifies if a user is disallowed from adjusting the master volume. If set, the master volume + * Specifies if a user is disallowed from adjusting the global volume. If set, the global volume * will be muted. This can be set by device owners from API 21 and profile owners from API 24. * The default value is <code>false</code>. * @@ -1056,7 +1056,7 @@ public class UserManager { public static final String DISALLOW_CAMERA = "no_camera"; /** - * Specifies if a user is not allowed to unmute the device's master volume. + * Specifies if a user is not allowed to unmute the device's global volume. * * @see DevicePolicyManager#setMasterVolumeMuted(ComponentName, boolean) * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java index 6adba63f42ce..a1b4dc3ffded 100644 --- a/core/java/android/os/WorkSource.java +++ b/core/java/android/os/WorkSource.java @@ -1133,7 +1133,7 @@ public class WorkSource implements Parcelable { ArrayList<WorkChain> newChains = null; ArrayList<WorkChain> goneChains = null; - // TODO(narayan): This is a dumb O(M*N) algorithm that determines what has changed across + // TODO(narayan): This is a naive O(M*N) algorithm that determines what has changed across // WorkSource objects. We can replace this with something smarter, for e.g by defining // a Comparator between WorkChains. It's unclear whether that will be more efficient if // the number of chains associated with a WorkSource is expected to be small diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index ffede09f99d6..9e332e9b0456 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -333,7 +333,7 @@ public class ZygoteProcess { * started. * @param pkgDataInfoMap Map from related package names to private data directory * volume UUID and inode number. - * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory * volume UUID and inode number. * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. @@ -615,7 +615,7 @@ public class ZygoteProcess { * @param disabledCompatChanges a list of disabled compat changes for the process being started. * @param pkgDataInfoMap Map from related package names to private data directory volume UUID * and inode number. - * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory * volume UUID and inode number. * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 31ccf95ba16f..dcbbd712914a 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -69,7 +69,7 @@ public final class IncrementalFileStorages { @Nullable StorageHealthCheckParams healthCheckParams, @Nullable IStorageHealthListener healthListener, List<InstallationFileParcel> addedFiles) throws IOException { - // TODO(b/136132412): sanity check if session should not be incremental + // TODO(b/136132412): validity check if session should not be incremental IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( Context.INCREMENTAL_SERVICE); if (incrementalManager == null) { diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java index 32c80e7069da..77d8664a6459 100644 --- a/core/java/android/os/incremental/V4Signature.java +++ b/core/java/android/os/incremental/V4Signature.java @@ -31,7 +31,7 @@ import java.nio.ByteOrder; /** * V4 signature fields. - * Keep in sync with APKSig master copy. + * Keep in sync with APKSig authoritative copy. * @hide */ public class V4Signature { diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index b9d27e923615..d80a7e794220 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -154,7 +154,7 @@ public final class PermissionManager { * Get set of permissions that have been split into more granular or dependent permissions. * * <p>E.g. before {@link android.os.Build.VERSION_CODES#Q} an app that was granted - * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in + * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access the location while it was in * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#Q} * the location permission only grants location access while the app is in foreground. This * would break apps that target before {@link android.os.Build.VERSION_CODES#Q}. Hence whenever diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java index e10c507723e7..b98846163702 100644 --- a/core/java/android/print/PrintDocumentInfo.java +++ b/core/java/android/print/PrintDocumentInfo.java @@ -32,7 +32,7 @@ import java.lang.annotation.RetentionPolicy; * purposes. This meta-data is used by the platform and print services, * components that interact with printers. For example, this class * contains the number of pages contained in the document it describes and - * this number of pages is shown to the user allowing him/her to select + * this number of pages is shown to the user allowing them to select * the range to print. Also a print service may optimize the printing * process based on the content type, such as document or photo. * <p> diff --git a/core/java/android/printservice/recommendation/RecommendationService.java b/core/java/android/printservice/recommendation/RecommendationService.java index 968a62585d8e..c6d4f2523b7e 100644 --- a/core/java/android/printservice/recommendation/RecommendationService.java +++ b/core/java/android/printservice/recommendation/RecommendationService.java @@ -74,10 +74,10 @@ public abstract class RecommendationService extends Service { @Override public void registerCallbacks(IRecommendationServiceCallbacks callbacks) { // The callbacks come in order of the caller on oneway calls. Hence while the caller - // cannot know at what time the connection is made, he can know the ordering of + // cannot know at what time the connection is made, they can know the ordering of // connection and disconnection. // - // Similar he cannot know when the disconnection is processed, hence he has to + // Similar they cannot know when the disconnection is processed, hence they have to // handle callbacks after calling disconnect. if (callbacks != null) { mHandler.obtainMessage(MyHandler.MSG_CONNECT, callbacks).sendToTarget(); diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 17fae1cafe62..e1aa21e8ea49 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -188,7 +188,7 @@ public final class CalendarContract { * notified when there is a change in the managed profile calendar provider. * * <p>Throw UnsupportedOperationException if another profile doesn't exist or is disabled, or - * if the calling package is not whitelisted to access cross-profile calendar, or if the + * if the calling package is not allowlisted to access cross-profile calendar, or if the * feature has been disabled by the user in Settings. * * @see Events#ENTERPRISE_CONTENT_URI diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 4e1f81919c7d..b704d66d8e41 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -165,7 +165,7 @@ public abstract class DocumentsProvider extends ContentProvider { public void attachInfo(Context context, ProviderInfo info) { registerAuthority(info.authority); - // Sanity check our setup + // Validity check our setup if (!info.exported) { throw new SecurityException("Provider must be exported"); } diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java index f4d0cb4d43d3..85c0ba1c4b1f 100644 --- a/core/java/android/provider/SearchIndexablesProvider.java +++ b/core/java/android/provider/SearchIndexablesProvider.java @@ -100,7 +100,7 @@ public abstract class SearchIndexablesProvider extends ContentProvider { mMatcher.addURI(mAuthority, SearchIndexablesContract.DYNAMIC_INDEXABLES_RAW_PATH, MATCH_DYNAMIC_RAW_CODE); - // Sanity check our setup + // Validity check our setup if (!info.exported) { throw new SecurityException("Provider must be exported"); } diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java index 5db3c1a9a707..90ce11ae0313 100644 --- a/core/java/android/se/omapi/Channel.java +++ b/core/java/android/se/omapi/Channel.java @@ -234,7 +234,7 @@ public final class Channel implements java.nio.channels.Channel { * response of the SELECT command. * @return <code>true</code> if new Applet was selected on this channel. - <code>false</code> he already selected Applet stays selected on this channel. + <code>false</code> the already selected Applet stays selected on this channel. * * @throws IOException if there is a communication problem to the reader or the Secure Element. * @throws IllegalStateException if the channel is used after being closed. diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index bc08b84d31fd..b1107a8c2efb 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -501,7 +501,7 @@ public final class FillResponse implements Parcelable { * Disables autofill for the app or activity. * * <p>This method is useful to optimize performance in cases where the service knows it - * can not autofill an app—for example, when the service has a list of "blacklisted" + * can not autofill an app—for example, when the service has a list of "denylisted" * apps such as office suites. * * <p>By default, it disables autofill for all activities in the app, unless the response is diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 95cc64ae8aab..620c457024b8 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -204,7 +204,7 @@ public abstract class AugmentedAutofillService extends Service { * <ul> * <li>Service does not recognize what should be autofilled. * <li>Service does not have data to fill the request. - * <li>Service blacklisted that app (or activity) for autofill. + * <li>Service denylisted that app (or activity) for autofill. * <li>App disabled itself for autofill. * </ul> * diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index b1f147be6735..84f602820f4a 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -231,7 +231,7 @@ public abstract class ContentCaptureService extends Service { /** * Explicitly limits content capture to the given packages and activities. * - * <p>To reset the whitelist, call it passing {@code null} to both arguments. + * <p>To reset the allowlist, call it passing {@code null} to both arguments. * * <p>Useful when the service wants to restrict content capture to a category of apps, like * chat apps. For example, if the service wants to support view captures on all activities of @@ -367,7 +367,7 @@ public abstract class ContentCaptureService extends Service { * Notifies the service of an activity-level event that is not associated with a session. * * <p>This method can be used to track some high-level events for all activities, even those - * that are not whitelisted for Content Capture. + * that are not allowlisted for Content Capture. * * @param event high-level activity event */ diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java index f2cedbc7aa92..ced2a01cb1d0 100644 --- a/core/java/android/service/dreams/Sandman.java +++ b/core/java/android/service/dreams/Sandman.java @@ -37,7 +37,7 @@ public final class Sandman { private static final String TAG = "Sandman"; - // The sandman is eternal. No one instantiates him. + // The sandman is eternal. No one instantiates them. private Sandman() { } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 3ed7f6c517d4..7a185382a631 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -834,7 +834,7 @@ public class TextToSpeech { // NOTE: The API currently does not allow the caller to query whether // they are actually connected to any engine. This might fail for various - // reasons like if the user disables all her TTS engines. + // reasons like if the user disables all their TTS engines. mCurrentEngine = null; dispatchOnInit(ERROR); diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index 5b5c8548f281..f19e7d2724d9 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -345,9 +345,9 @@ public class Time { } /** - * Print the current value given the format string provided. See man - * strftime for what means what. The final string must be less than 256 - * characters. + * Print the current value given the format string provided. See + * strftime(3) manual page for what means what. The final string must be + * less than 256 characters. * @param format a string containing the desired format. * @return a String containing the current time expressed in the current locale. */ diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 09736085954c..9cc6b9f83ede 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -66,6 +66,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_tether_all_in_one", "false"); DEFAULT_FLAGS.put("settings_silky_home", "false"); + DEFAULT_FLAGS.put("settings_contextual_home", "false"); } /** diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index 55542d898784..eefb15604bbc 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -495,7 +495,7 @@ public class LongSparseArray<E> implements Cloneable { array.mKeys = source.createLongArray(); array.mValues = source.createStringArray(); - // Make sure array is sane + // Make sure array is valid Preconditions.checkArgument(array.mKeys.length >= size); Preconditions.checkArgument(array.mValues.length >= size); diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index c05dd9de2f17..f23ec91f0b79 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -316,7 +316,7 @@ public class LongSparseLongArray implements Cloneable { array.mKeys = source.createLongArray(); array.mValues = source.createLongArray(); - // Make sure array is sane + // Make sure array is valid Preconditions.checkArgument(array.mKeys.length >= size); Preconditions.checkArgument(array.mValues.length >= size); diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 5f963b019335..4d1402a0d6d6 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -25,6 +25,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyA import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; +import static android.util.apk.ApkSigningBlockUtils.verifyProofOfRotationStruct; import android.os.Build; import android.util.ArrayMap; @@ -53,7 +54,6 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Map; @@ -90,9 +90,10 @@ public class ApkSignatureSchemeV3Verifier { * associated with each signer. * * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. - * @throws SecurityException if the APK Signature Scheme v3 signature of this APK does not - * verify. - * @throws IOException if an I/O error occurs while reading the APK file. + * @throws SecurityException if the APK Signature Scheme v3 signature of this APK does + * not + * verify. + * @throws IOException if an I/O error occurs while reading the APK file. */ public static VerifiedSigner verify(String apkFile) throws SignatureNotFoundException, SecurityException, IOException { @@ -106,7 +107,7 @@ public class ApkSignatureSchemeV3Verifier { * Block while gathering signer information. The APK contents are not verified. * * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. - * @throws IOException if an I/O error occurs while reading the APK file. + * @throws IOException if an I/O error occurs while reading the APK file. */ public static VerifiedSigner unsafeGetCertsWithoutVerification(String apkFile) throws SignatureNotFoundException, SecurityException, IOException { @@ -125,9 +126,10 @@ public class ApkSignatureSchemeV3Verifier { * associated with each signer. * * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. - * @throws SecurityException if an APK Signature Scheme v3 signature of this APK does not - * verify. - * @throws IOException if an I/O error occurs while reading the APK file. + * @throws SecurityException if an APK Signature Scheme v3 signature of this APK does + * not + * verify. + * @throws IOException if an I/O error occurs while reading the APK file. */ private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { @@ -140,7 +142,7 @@ public class ApkSignatureSchemeV3Verifier { * additional information relevant for verifying the block against the file. * * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. - * @throws IOException if an I/O error occurs while reading the APK file. + * @throws IOException if an I/O error occurs while reading the APK file. */ public static SignatureInfo findSignature(RandomAccessFile apk) throws IOException, SignatureNotFoundException { @@ -152,7 +154,7 @@ public class ApkSignatureSchemeV3Verifier { * Block. * * @param signatureInfo APK Signature Scheme v3 Block and information relevant for verifying it - * against the APK file. + * against the APK file. */ private static VerifiedSigner verify( RandomAccessFile apk, @@ -160,7 +162,7 @@ public class ApkSignatureSchemeV3Verifier { boolean doVerifyIntegrity) throws SecurityException, IOException { int signerCount = 0; Map<Integer, byte[]> contentDigests = new ArrayMap<>(); - Pair<X509Certificate[], VerifiedProofOfRotation> result = null; + Pair<X509Certificate[], ApkSigningBlockUtils.VerifiedProofOfRotation> result = null; CertificateFactory certFactory; try { certFactory = CertificateFactory.getInstance("X.509"); @@ -215,10 +217,11 @@ public class ApkSignatureSchemeV3Verifier { return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests); } - private static Pair<X509Certificate[], VerifiedProofOfRotation> verifySigner( - ByteBuffer signerBlock, - Map<Integer, byte[]> contentDigests, - CertificateFactory certFactory) + private static Pair<X509Certificate[], ApkSigningBlockUtils.VerifiedProofOfRotation> + verifySigner( + ByteBuffer signerBlock, + Map<Integer, byte[]> contentDigests, + CertificateFactory certFactory) throws SecurityException, IOException, PlatformNotSupportedException { ByteBuffer signedData = getLengthPrefixedSlice(signerBlock); int minSdkVersion = signerBlock.getInt(); @@ -228,9 +231,9 @@ public class ApkSignatureSchemeV3Verifier { // this signature isn't meant to be used with this platform, skip it. throw new PlatformNotSupportedException( "Signer not supported by this platform " - + "version. This platform: " + Build.VERSION.SDK_INT - + ", signer minSdkVersion: " + minSdkVersion - + ", maxSdkVersion: " + maxSdkVersion); + + "version. This platform: " + Build.VERSION.SDK_INT + + ", signer minSdkVersion: " + minSdkVersion + + ", maxSdkVersion: " + maxSdkVersion); } ByteBuffer signatures = getLengthPrefixedSlice(signerBlock); @@ -331,7 +334,8 @@ public class ApkSignatureSchemeV3Verifier { && (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) { throw new SecurityException( getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm) - + " contents digest does not match the digest specified by a preceding signer"); + + " contents digest does not match the digest specified by a " + + "preceding signer"); } ByteBuffer certificates = getLengthPrefixedSlice(signedData); @@ -379,11 +383,11 @@ public class ApkSignatureSchemeV3Verifier { private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c; - private static Pair<X509Certificate[], VerifiedProofOfRotation> verifyAdditionalAttributes( - ByteBuffer attrs, List<X509Certificate> certs, CertificateFactory certFactory) - throws IOException { + private static Pair<X509Certificate[], ApkSigningBlockUtils.VerifiedProofOfRotation> + verifyAdditionalAttributes(ByteBuffer attrs, List<X509Certificate> certs, + CertificateFactory certFactory) throws IOException { X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]); - VerifiedProofOfRotation por = null; + ApkSigningBlockUtils.VerifiedProofOfRotation por = null; while (attrs.hasRemaining()) { ByteBuffer attr = getLengthPrefixedSlice(attrs); @@ -392,7 +396,7 @@ public class ApkSignatureSchemeV3Verifier { + "ID. Remaining: " + attr.remaining()); } int id = attr.getInt(); - switch(id) { + switch (id) { case PROOF_OF_ROTATION_ATTR_ID: if (por != null) { throw new SecurityException("Encountered multiple Proof-of-rotation records" @@ -404,7 +408,7 @@ public class ApkSignatureSchemeV3Verifier { try { if (por.certs.size() > 0 && !Arrays.equals(por.certs.get(por.certs.size() - 1).getEncoded(), - certChain[0].getEncoded())) { + certChain[0].getEncoded())) { throw new SecurityException("Terminal certificate in Proof-of-rotation" + " record does not match APK signing certificate"); } @@ -422,96 +426,6 @@ public class ApkSignatureSchemeV3Verifier { return Pair.create(certChain, por); } - private static VerifiedProofOfRotation verifyProofOfRotationStruct( - ByteBuffer porBuf, - CertificateFactory certFactory) - throws SecurityException, IOException { - int levelCount = 0; - int lastSigAlgorithm = -1; - X509Certificate lastCert = null; - List<X509Certificate> certs = new ArrayList<>(); - List<Integer> flagsList = new ArrayList<>(); - - // Proof-of-rotation struct: - // A uint32 version code followed by basically a singly linked list of nodes, called levels - // here, each of which have the following structure: - // * length-prefix for the entire level - // - length-prefixed signed data (if previous level exists) - // * length-prefixed X509 Certificate - // * uint32 signature algorithm ID describing how this signed data was signed - // - uint32 flags describing how to treat the cert contained in this level - // - uint32 signature algorithm ID to use to verify the signature of the next level. The - // algorithm here must match the one in the signed data section of the next level. - // - length-prefixed signature over the signed data in this level. The signature here - // is verified using the certificate from the previous level. - // The linking is provided by the certificate of each level signing the one of the next. - - try { - - // get the version code, but don't do anything with it: creator knew about all our flags - porBuf.getInt(); - HashSet<X509Certificate> certHistorySet = new HashSet<>(); - while (porBuf.hasRemaining()) { - levelCount++; - ByteBuffer level = getLengthPrefixedSlice(porBuf); - ByteBuffer signedData = getLengthPrefixedSlice(level); - int flags = level.getInt(); - int sigAlgorithm = level.getInt(); - byte[] signature = readLengthPrefixedByteArray(level); - - if (lastCert != null) { - // Use previous level cert to verify current level - Pair<String, ? extends AlgorithmParameterSpec> sigAlgParams = - getSignatureAlgorithmJcaSignatureAlgorithm(lastSigAlgorithm); - PublicKey publicKey = lastCert.getPublicKey(); - Signature sig = Signature.getInstance(sigAlgParams.first); - sig.initVerify(publicKey); - if (sigAlgParams.second != null) { - sig.setParameter(sigAlgParams.second); - } - sig.update(signedData); - if (!sig.verify(signature)) { - throw new SecurityException("Unable to verify signature of certificate #" - + levelCount + " using " + sigAlgParams.first + " when verifying" - + " Proof-of-rotation record"); - } - } - - signedData.rewind(); - byte[] encodedCert = readLengthPrefixedByteArray(signedData); - int signedSigAlgorithm = signedData.getInt(); - if (lastCert != null && lastSigAlgorithm != signedSigAlgorithm) { - throw new SecurityException("Signing algorithm ID mismatch for certificate #" - + levelCount + " when verifying Proof-of-rotation record"); - } - lastCert = (X509Certificate) - certFactory.generateCertificate(new ByteArrayInputStream(encodedCert)); - lastCert = new VerbatimX509Certificate(lastCert, encodedCert); - - lastSigAlgorithm = sigAlgorithm; - if (certHistorySet.contains(lastCert)) { - throw new SecurityException("Encountered duplicate entries in " - + "Proof-of-rotation record at certificate #" + levelCount + ". All " - + "signing certificates should be unique"); - } - certHistorySet.add(lastCert); - certs.add(lastCert); - flagsList.add(flags); - } - } catch (IOException | BufferUnderflowException e) { - throw new IOException("Failed to parse Proof-of-rotation record", e); - } catch (NoSuchAlgorithmException | InvalidKeyException - | InvalidAlgorithmParameterException | SignatureException e) { - throw new SecurityException( - "Failed to verify signature over signed data for certificate #" - + levelCount + " when verifying Proof-of-rotation record", e); - } catch (CertificateException e) { - throw new SecurityException("Failed to decode certificate #" + levelCount - + " when verifying Proof-of-rotation record", e); - } - return new VerifiedProofOfRotation(certs, flagsList); - } - static byte[] getVerityRootHash(String apkPath) throws IOException, SignatureNotFoundException, SecurityException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { @@ -523,7 +437,7 @@ public class ApkSignatureSchemeV3Verifier { static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) throws IOException, SignatureNotFoundException, SecurityException, DigestException, - NoSuchAlgorithmException { + NoSuchAlgorithmException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { SignatureInfo signatureInfo = findSignature(apk); return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo); @@ -532,7 +446,7 @@ public class ApkSignatureSchemeV3Verifier { static byte[] generateApkVerityRootHash(String apkPath) throws NoSuchAlgorithmException, DigestException, IOException, - SignatureNotFoundException { + SignatureNotFoundException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { SignatureInfo signatureInfo = findSignature(apk); VerifiedSigner vSigner = verify(apk, false); @@ -545,35 +459,21 @@ public class ApkSignatureSchemeV3Verifier { } /** - * Verified processed proof of rotation. - * - * @hide for internal use only. - */ - public static class VerifiedProofOfRotation { - public final List<X509Certificate> certs; - public final List<Integer> flagsList; - - public VerifiedProofOfRotation(List<X509Certificate> certs, List<Integer> flagsList) { - this.certs = certs; - this.flagsList = flagsList; - } - } - - /** * Verified APK Signature Scheme v3 signer, including the proof of rotation structure. * * @hide for internal use only. */ public static class VerifiedSigner { public final X509Certificate[] certs; - public final VerifiedProofOfRotation por; + public final ApkSigningBlockUtils.VerifiedProofOfRotation por; public final byte[] verityRootHash; // Algorithm -> digest map of signed digests in the signature. // All these are verified if requested. public final Map<Integer, byte[]> contentDigests; - public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por, + public VerifiedSigner(X509Certificate[] certs, + ApkSigningBlockUtils.VerifiedProofOfRotation por, byte[] verityRootHash, Map<Integer, byte[]> contentDigests) { this.certs = certs; this.por = por; diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 021f232979ef..c97c995641d1 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -19,6 +19,7 @@ package android.util.apk; import android.util.ArrayMap; import android.util.Pair; +import java.io.ByteArrayInputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; @@ -26,12 +27,23 @@ import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.DigestException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PSSParameterSpec; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.List; import java.util.Map; /** @@ -51,9 +63,8 @@ public final class ApkSigningBlockUtils { * @param blockId the ID value in the APK Signing Block's sequence of ID-value pairs * identifying the appropriate block to find, e.g. the APK Signature Scheme v2 * block ID. - * * @throws SignatureNotFoundException if the APK is not signed using this scheme. - * @throws IOException if an I/O error occurs while reading the APK file. + * @throws IOException if an I/O error occurs while reading the APK file. */ static SignatureInfo findSignature(RandomAccessFile apk, int blockId) throws IOException, SignatureNotFoundException { @@ -377,7 +388,7 @@ public final class ApkSigningBlockUtils { /** * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. * - * @throws IOException if an I/O error occurs while reading the file. + * @throws IOException if an I/O error occurs while reading the file. * @throws SignatureNotFoundException if the EoCD could not be found. */ static Pair<ByteBuffer, Long> getEocd(RandomAccessFile apk) @@ -398,13 +409,13 @@ public final class ApkSigningBlockUtils { if (centralDirOffset > eocdOffset) { throw new SignatureNotFoundException( "ZIP Central Directory offset out of range: " + centralDirOffset - + ". ZIP End of Central Directory offset: " + eocdOffset); + + ". ZIP End of Central Directory offset: " + eocdOffset); } long centralDirSize = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd); if (centralDirOffset + centralDirSize != eocdOffset) { throw new SignatureNotFoundException( "ZIP Central Directory is not immediately followed by End of Central" - + " Directory"); + + " Directory"); } return centralDirOffset; } @@ -687,7 +698,7 @@ public final class ApkSigningBlockUtils { static Pair<ByteBuffer, Long> findApkSigningBlock( RandomAccessFile apk, long centralDirOffset) - throws IOException, SignatureNotFoundException { + throws IOException, SignatureNotFoundException { // FORMAT: // OFFSET DATA TYPE DESCRIPTION // * @+0 bytes uint64: size in bytes (excluding this field) @@ -806,4 +817,108 @@ public final class ApkSigningBlockUtils { } } + static VerifiedProofOfRotation verifyProofOfRotationStruct( + ByteBuffer porBuf, + CertificateFactory certFactory) + throws SecurityException, IOException { + int levelCount = 0; + int lastSigAlgorithm = -1; + X509Certificate lastCert = null; + List<X509Certificate> certs = new ArrayList<>(); + List<Integer> flagsList = new ArrayList<>(); + + // Proof-of-rotation struct: + // A uint32 version code followed by basically a singly linked list of nodes, called levels + // here, each of which have the following structure: + // * length-prefix for the entire level + // - length-prefixed signed data (if previous level exists) + // * length-prefixed X509 Certificate + // * uint32 signature algorithm ID describing how this signed data was signed + // - uint32 flags describing how to treat the cert contained in this level + // - uint32 signature algorithm ID to use to verify the signature of the next level. The + // algorithm here must match the one in the signed data section of the next level. + // - length-prefixed signature over the signed data in this level. The signature here + // is verified using the certificate from the previous level. + // The linking is provided by the certificate of each level signing the one of the next. + + try { + + // get the version code, but don't do anything with it: creator knew about all our flags + porBuf.getInt(); + HashSet<X509Certificate> certHistorySet = new HashSet<>(); + while (porBuf.hasRemaining()) { + levelCount++; + ByteBuffer level = getLengthPrefixedSlice(porBuf); + ByteBuffer signedData = getLengthPrefixedSlice(level); + int flags = level.getInt(); + int sigAlgorithm = level.getInt(); + byte[] signature = readLengthPrefixedByteArray(level); + + if (lastCert != null) { + // Use previous level cert to verify current level + Pair<String, ? extends AlgorithmParameterSpec> sigAlgParams = + getSignatureAlgorithmJcaSignatureAlgorithm(lastSigAlgorithm); + PublicKey publicKey = lastCert.getPublicKey(); + Signature sig = Signature.getInstance(sigAlgParams.first); + sig.initVerify(publicKey); + if (sigAlgParams.second != null) { + sig.setParameter(sigAlgParams.second); + } + sig.update(signedData); + if (!sig.verify(signature)) { + throw new SecurityException("Unable to verify signature of certificate #" + + levelCount + " using " + sigAlgParams.first + " when verifying" + + " Proof-of-rotation record"); + } + } + + signedData.rewind(); + byte[] encodedCert = readLengthPrefixedByteArray(signedData); + int signedSigAlgorithm = signedData.getInt(); + if (lastCert != null && lastSigAlgorithm != signedSigAlgorithm) { + throw new SecurityException("Signing algorithm ID mismatch for certificate #" + + levelCount + " when verifying Proof-of-rotation record"); + } + lastCert = (X509Certificate) + certFactory.generateCertificate(new ByteArrayInputStream(encodedCert)); + lastCert = new VerbatimX509Certificate(lastCert, encodedCert); + + lastSigAlgorithm = sigAlgorithm; + if (certHistorySet.contains(lastCert)) { + throw new SecurityException("Encountered duplicate entries in " + + "Proof-of-rotation record at certificate #" + levelCount + ". All " + + "signing certificates should be unique"); + } + certHistorySet.add(lastCert); + certs.add(lastCert); + flagsList.add(flags); + } + } catch (IOException | BufferUnderflowException e) { + throw new IOException("Failed to parse Proof-of-rotation record", e); + } catch (NoSuchAlgorithmException | InvalidKeyException + | InvalidAlgorithmParameterException | SignatureException e) { + throw new SecurityException( + "Failed to verify signature over signed data for certificate #" + + levelCount + " when verifying Proof-of-rotation record", e); + } catch (CertificateException e) { + throw new SecurityException("Failed to decode certificate #" + levelCount + + " when verifying Proof-of-rotation record", e); + } + return new VerifiedProofOfRotation(certs, flagsList); + } + + /** + * Verified processed proof of rotation. + * + * @hide for internal use only. + */ + public static class VerifiedProofOfRotation { + public final List<X509Certificate> certs; + public final List<Integer> flagsList; + + public VerifiedProofOfRotation(List<X509Certificate> certs, List<Integer> flagsList) { + this.certs = certs; + this.flagsList = flagsList; + } + } } diff --git a/core/java/android/util/apk/SourceStampVerificationResult.java b/core/java/android/util/apk/SourceStampVerificationResult.java index 2edaf623fb94..8b9eee2f796e 100644 --- a/core/java/android/util/apk/SourceStampVerificationResult.java +++ b/core/java/android/util/apk/SourceStampVerificationResult.java @@ -19,6 +19,8 @@ package android.util.apk; import android.annotation.Nullable; import java.security.cert.Certificate; +import java.util.Collections; +import java.util.List; /** * A class encapsulating the result from the source stamp verifier @@ -32,12 +34,15 @@ public final class SourceStampVerificationResult { private final boolean mPresent; private final boolean mVerified; private final Certificate mCertificate; + private final List<? extends Certificate> mCertificateLineage; private SourceStampVerificationResult( - boolean present, boolean verified, @Nullable Certificate certificate) { + boolean present, boolean verified, @Nullable Certificate certificate, + List<? extends Certificate> certificateLineage) { this.mPresent = present; this.mVerified = verified; this.mCertificate = certificate; + this.mCertificateLineage = certificateLineage; } public boolean isPresent() { @@ -52,6 +57,10 @@ public final class SourceStampVerificationResult { return mCertificate; } + public List<? extends Certificate> getCertificateLineage() { + return mCertificateLineage; + } + /** * Create a non-present source stamp outcome. * @@ -59,18 +68,21 @@ public final class SourceStampVerificationResult { */ public static SourceStampVerificationResult notPresent() { return new SourceStampVerificationResult( - /* present= */ false, /* verified= */ false, /* certificate= */ null); + /* present= */ false, /* verified= */ false, /* certificate= */ + null, /* certificateLineage= */ Collections.emptyList()); } /** * Create a verified source stamp outcome. * - * @param certificate The source stamp certificate. + * @param certificate The source stamp certificate. + * @param certificateLineage The proof-of-rotation lineage for the source stamp. * @return A verified source stamp result, and the source stamp certificate. */ - public static SourceStampVerificationResult verified(Certificate certificate) { + public static SourceStampVerificationResult verified(Certificate certificate, + List<? extends Certificate> certificateLineage) { return new SourceStampVerificationResult( - /* present= */ true, /* verified= */ true, certificate); + /* present= */ true, /* verified= */ true, certificate, certificateLineage); } /** @@ -80,6 +92,7 @@ public final class SourceStampVerificationResult { */ public static SourceStampVerificationResult notVerified() { return new SourceStampVerificationResult( - /* present= */ true, /* verified= */ false, /* certificate= */ null); + /* present= */ true, /* verified= */ false, /* certificate= */ + null, /* certificateLineage= */ Collections.emptyList()); } } diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java index 5fc242353d51..f9e312146ccf 100644 --- a/core/java/android/util/apk/SourceStampVerifier.java +++ b/core/java/android/util/apk/SourceStampVerifier.java @@ -23,6 +23,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; +import static android.util.apk.ApkSigningBlockUtils.verifyProofOfRotationStruct; import android.util.Pair; import android.util.Slog; @@ -44,12 +45,14 @@ import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -76,6 +79,7 @@ public abstract class SourceStampVerifier { private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; private static final int SOURCE_STAMP_BLOCK_ID = 0x6dff800d; + private static final int PROOF_OF_ROTATION_ATTR_ID = 0x9d6303f7; private static final int VERSION_JAR_SIGNATURE_SCHEME = 1; private static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; @@ -85,11 +89,13 @@ public abstract class SourceStampVerifier { private static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256"; /** Hidden constructor to prevent instantiation. */ - private SourceStampVerifier() {} + private SourceStampVerifier() { + } - /** Verifies SourceStamp present in a list of APKs. */ + /** Verifies SourceStamp present in a list of (split) APKs for the same app. */ public static SourceStampVerificationResult verify(List<String> apkFiles) { Certificate stampCertificate = null; + List<? extends Certificate> stampCertificateLineage = Collections.emptyList(); for (String apkFile : apkFiles) { SourceStampVerificationResult sourceStampVerificationResult = verify(apkFile); if (!sourceStampVerificationResult.isPresent() @@ -97,12 +103,15 @@ public abstract class SourceStampVerifier { return sourceStampVerificationResult; } if (stampCertificate != null - && !stampCertificate.equals(sourceStampVerificationResult.getCertificate())) { + && (!stampCertificate.equals(sourceStampVerificationResult.getCertificate()) + || !stampCertificateLineage.equals( + sourceStampVerificationResult.getCertificateLineage()))) { return SourceStampVerificationResult.notVerified(); } stampCertificate = sourceStampVerificationResult.getCertificate(); + stampCertificateLineage = sourceStampVerificationResult.getCertificateLineage(); } - return SourceStampVerificationResult.verified(stampCertificate); + return SourceStampVerificationResult.verified(stampCertificate, stampCertificateLineage); } /** Verifies SourceStamp present in the provided APK. */ @@ -177,21 +186,44 @@ public abstract class SourceStampVerifier { "No signatures found for signature scheme %d", signatureSchemeDigest.getKey())); } + ByteBuffer signatures = ApkSigningBlockUtils.getLengthPrefixedSlice( + signedSignatureSchemeData.get(signatureSchemeDigest.getKey())); verifySourceStampSignature( - signedSignatureSchemeData.get(signatureSchemeDigest.getKey()), + signatureSchemeDigest.getValue(), sourceStampCertificate, - signatureSchemeDigest.getValue()); + signatures); } - return SourceStampVerificationResult.verified(sourceStampCertificate); + List<? extends Certificate> sourceStampCertificateLineage = Collections.emptyList(); + if (sourceStampBlockData.hasRemaining()) { + // The stamp block contains some additional attributes. + ByteBuffer stampAttributeData = getLengthPrefixedSlice(sourceStampBlockData); + ByteBuffer stampAttributeDataSignatures = getLengthPrefixedSlice(sourceStampBlockData); + + byte[] stampAttributeBytes = new byte[stampAttributeData.remaining()]; + stampAttributeData.get(stampAttributeBytes); + stampAttributeData.flip(); + + verifySourceStampSignature(stampAttributeBytes, sourceStampCertificate, + stampAttributeDataSignatures); + ApkSigningBlockUtils.VerifiedProofOfRotation verifiedProofOfRotation = + verifySourceStampAttributes(stampAttributeData, sourceStampCertificate); + if (verifiedProofOfRotation != null) { + sourceStampCertificateLineage = verifiedProofOfRotation.certs; + } + } + + return SourceStampVerificationResult.verified(sourceStampCertificate, + sourceStampCertificateLineage); } /** * Verify the SourceStamp certificate found in the signing block is the same as the SourceStamp * certificate found in the APK. It returns the verified certificate. * - * @param sourceStampBlockData the source stamp block in the APK signing block which contains - * the certificate used to sign the stamp digests. + * @param sourceStampBlockData the source stamp block in the APK signing block which + * contains + * the certificate used to sign the stamp digests. * @param sourceStampCertificateDigest the source stamp certificate digest found in the APK. */ private static X509Certificate verifySourceStampCertificate( @@ -230,16 +262,16 @@ public abstract class SourceStampVerifier { * Verify the SourceStamp signature found in the signing block is signed by the SourceStamp * certificate found in the APK. * - * @param signedBlockData the source stamp block in the APK signing block which contains the - * stamp signed digests. + * @param data the digest to be verified being signed by the source stamp + * certificate. * @param sourceStampCertificate the source stamp certificate used to sign the stamp digests. - * @param digest the digest to be verified being signed by the source stamp certificate. + * @param signatures the source stamp block in the APK signing block which contains + * the stamp signed digests. */ - private static void verifySourceStampSignature( - ByteBuffer signedBlockData, X509Certificate sourceStampCertificate, byte[] digest) + private static void verifySourceStampSignature(byte[] data, + X509Certificate sourceStampCertificate, ByteBuffer signatures) throws IOException { // Parse the signatures block and identify supported signatures - ByteBuffer signatures = ApkSigningBlockUtils.getLengthPrefixedSlice(signedBlockData); int signatureCount = 0; int bestSigAlgorithm = -1; byte[] bestSigAlgorithmSignatureBytes = null; @@ -285,7 +317,7 @@ public abstract class SourceStampVerifier { if (jcaSignatureAlgorithmParams != null) { sig.setParameter(jcaSignatureAlgorithmParams); } - sig.update(digest); + sig.update(data); sigVerified = sig.verify(bestSigAlgorithmSignatureBytes); } catch (InvalidKeyException | InvalidAlgorithmParameterException @@ -414,6 +446,46 @@ public abstract class SourceStampVerifier { return result.array(); } + private static ApkSigningBlockUtils.VerifiedProofOfRotation verifySourceStampAttributes( + ByteBuffer stampAttributeData, + X509Certificate sourceStampCertificate) + throws IOException { + CertificateFactory certFactory; + try { + certFactory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); + } + ByteBuffer stampAttributes = getLengthPrefixedSlice(stampAttributeData); + ApkSigningBlockUtils.VerifiedProofOfRotation verifiedProofOfRotation = null; + while (stampAttributes.hasRemaining()) { + ByteBuffer attribute = getLengthPrefixedSlice(stampAttributes); + int id = attribute.getInt(); + if (id == PROOF_OF_ROTATION_ATTR_ID) { + if (verifiedProofOfRotation != null) { + throw new SecurityException("Encountered multiple Proof-of-rotation records" + + " when verifying source stamp signature"); + } + verifiedProofOfRotation = verifyProofOfRotationStruct(attribute, certFactory); + // Make sure that the last certificate in the Proof-of-rotation record matches + // the one used to sign this APK. + try { + if (verifiedProofOfRotation.certs.size() > 0 + && !Arrays.equals(verifiedProofOfRotation.certs.get( + verifiedProofOfRotation.certs.size() - 1).getEncoded(), + sourceStampCertificate.getEncoded())) { + throw new SecurityException("Terminal certificate in Proof-of-rotation" + + " record does not match source stamp certificate"); + } + } catch (CertificateEncodingException e) { + throw new SecurityException("Failed to encode certificate when comparing" + + " Proof-of-rotation record and source stamp certificate", e); + } + } + } + return verifiedProofOfRotation; + } + private static byte[] computeSha256Digest(byte[] input) { try { MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 2bd3c06c318d..d55c25fc1a4f 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -65,6 +65,8 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is @@ -87,10 +89,10 @@ public final class SurfaceControl implements Parcelable { private static native void nativeWriteToParcel(long nativeObject, Parcel out); private static native void nativeRelease(long nativeObject); private static native void nativeDisconnect(long nativeObject); - private static native ScreenshotHardwareBuffer nativeCaptureDisplay( - DisplayCaptureArgs captureArgs); - private static native ScreenshotHardwareBuffer nativeCaptureLayers( - LayerCaptureArgs captureArgs); + private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs, + ScreenCaptureListener captureListener); + private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs, + ScreenCaptureListener captureListener); private static native long nativeMirrorSurface(long mirrorOfObject); private static native long nativeCreateTransaction(); private static native long nativeGetNativeTransactionFinalizer(); @@ -613,6 +615,39 @@ public final class SurfaceControl implements Parcelable { } /** + * @hide + */ + public interface ScreenCaptureListener { + /** + * The callback invoked when the screen capture is complete. + * @param hardwareBuffer Data containing info about the screen capture. + */ + void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer); + } + + private static class SyncScreenCaptureListener implements ScreenCaptureListener { + private static final int SCREENSHOT_WAIT_TIME_S = 1; + private ScreenshotHardwareBuffer mScreenshotHardwareBuffer; + private final CountDownLatch mCountDownLatch = new CountDownLatch(1); + + @Override + public void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) { + mScreenshotHardwareBuffer = hardwareBuffer; + mCountDownLatch.countDown(); + } + + private ScreenshotHardwareBuffer waitForScreenshot() { + try { + mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS); + } catch (Exception e) { + Log.e(TAG, "Failed to wait for screen capture result", e); + } + + return mScreenshotHardwareBuffer; + } + } + + /** * A common arguments class used for various screenshot requests. This contains arguments that * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs} * @hide @@ -687,7 +722,7 @@ public final class SurfaceControl implements Parcelable { /** * The arguments class used to make display capture requests. * - * @see #nativeCaptureDisplay(DisplayCaptureArgs) + * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener) * @hide */ public static class DisplayCaptureArgs extends CaptureArgs { @@ -2228,13 +2263,30 @@ public final class SurfaceControl implements Parcelable { } /** + * @param captureArgs Arguments about how to take the screenshot + * @param captureListener A listener to receive the screenshot callback + * @hide + */ + public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs, + @NonNull ScreenCaptureListener captureListener) { + return nativeCaptureDisplay(captureArgs, captureListener); + } + + /** * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with * the content. * * @hide */ public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) { - return nativeCaptureDisplay(captureArgs); + SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener(); + + int status = captureDisplay(captureArgs, screenCaptureListener); + if (status != 0) { + return null; + } + + return screenCaptureListener.waitForScreenshot(); } /** @@ -2279,14 +2331,21 @@ public final class SurfaceControl implements Parcelable { .setPixelFormat(format) .build(); - return nativeCaptureLayers(captureArgs); + return captureLayers(captureArgs); } /** * @hide */ public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) { - return nativeCaptureLayers(captureArgs); + SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener(); + + int status = captureLayers(captureArgs, screenCaptureListener); + if (status != 0) { + return null; + } + + return screenCaptureListener.waitForScreenshot(); } /** @@ -2303,7 +2362,17 @@ public final class SurfaceControl implements Parcelable { .setExcludeLayers(exclude) .build(); - return nativeCaptureLayers(captureArgs); + return captureLayers(captureArgs); + } + + /** + * @param captureArgs Arguments about how to take the screenshot + * @param captureListener A listener to receive the screenshot callback + * @hide + */ + public static int captureLayers(@NonNull LayerCaptureArgs captureArgs, + @NonNull ScreenCaptureListener captureListener) { + return nativeCaptureLayers(captureArgs, captureListener); } /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index f937bc9e84a9..795d13fdc509 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -506,7 +506,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall // It's possible to create a SurfaceView using the default constructor and never // attach it to a view hierarchy, this is a common use case when dealing with // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage - // the lifecycle. Instead of attaching it to a view, he/she can just pass + // the lifecycle. Instead of attaching it to a view, they can just pass // the SurfaceHolder forward, most live wallpapers do it. if (viewRoot != null) { viewRoot.removeSurfaceChangedCallback(this); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f648292d3118..fd7c2d896b09 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -7033,7 +7033,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { - // Child wants a specific size... let him have it + // Child wants a specific size... let them have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index b398cf6c9cb3..44c754c21261 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -509,7 +509,7 @@ public final class AutofillManager { /** * Views that were otherwised not important for autofill but triggered a session because the - * context is whitelisted for augmented autofill. + * context is allowlisted for augmented autofill. */ @GuardedBy("mLock") @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds; @@ -2060,7 +2060,7 @@ public final class AutofillManager { /** * Explicitly limits augmented autofill to the given packages and activities. * - * <p>To reset the whitelist, call it passing {@code null} to both arguments. + * <p>To reset the allowlist, call it passing {@code null} to both arguments. * * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like * apps that uses addresses. For example, if the service wants to support augmented autofill on @@ -2107,7 +2107,7 @@ public final class AutofillManager { } /** - * Notifies that a non-autofillable view was entered because the activity is whitelisted for + * Notifies that a non-autofillable view was entered because the activity is allowlisted for * augmented autofill. * * <p>This method is necessary to set the right flag on start, so the server-side session diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 484b1c10423c..029552d4902e 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -81,7 +81,7 @@ import java.util.function.Consumer; * <a href="https://source.android.com/compatibility/cdd">CDD requirements</a>. * <li><b>Performance:</b> content capture is highly optimized to minimize its impact in the app * jankiness and overall device system health. For example, its only enabled on apps (or even - * specific activities from an app) that were explicitly whitelisted by the intelligence service, + * specific activities from an app) that were explicitly allowlisted by the intelligence service, * and it buffers the events so they are sent in a batch to the service (see * {@link #isContentCaptureEnabled()} for other cases when its disabled). * </ul> @@ -495,7 +495,7 @@ public final class ContentCaptureManager { /** * Gets the (optional) intent used to launch the service-specific settings. * - * <p>This method is static because it's called by Settings, which might not be whitelisted + * <p>This method is static because it's called by Settings, which might not be allowlisted * for content capture (in which case the ContentCaptureManager on its context would be null). * * @hide @@ -530,8 +530,8 @@ public final class ContentCaptureManager { * <p>There are many reasons it could be disabled, such as: * <ul> * <li>App itself disabled content capture through {@link #setContentCaptureEnabled(boolean)}. - * <li>Intelligence service did not whitelist content capture for this activity's package. - * <li>Intelligence service did not whitelist content capture for this specific activity. + * <li>Intelligence service did not allowlist content capture for this activity's package. + * <li>Intelligence service did not allowlist content capture for this specific activity. * <li>Intelligence service disabled content capture globally. * <li>User disabled content capture globally through the Android Settings app. * <li>Device manufacturer (OEM) disabled content capture globally. @@ -566,7 +566,7 @@ public final class ContentCaptureManager { public Set<ContentCaptureCondition> getContentCaptureConditions() { // NOTE: we could cache the conditions on ContentCaptureOptions, but then it would be stick // to the lifetime of the app. OTOH, by dynamically calling the server every time, we allow - // the service to fine tune how long-lived apps (like browsers) are whitelisted. + // the service to fine tune how long-lived apps (like browsers) are allowlisted. if (!isContentCaptureEnabled() && !mOptions.lite) return null; final SyncResultReceiver resultReceiver = syncRun( diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 64c59e54e85b..c6332686bf7f 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -126,7 +126,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { public static final int STATE_INTERNAL_ERROR = 0x100; /** - * Session is disabled because service didn't whitelist package or activity. + * Session is disabled because service didn't allowlist package or activity. * * @hide */ diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index c43beea98c7e..05353651258e 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -397,7 +397,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { + " after " + numberEvents + " delayed events"); } resetSession(STATE_DISABLED | STATE_NO_RESPONSE); - // TODO(b/111276913): blacklist activity / use special flag to indicate that + // TODO(b/111276913): denylist activity / use special flag to indicate that // when it's launched again return; } diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index 869a9295e16b..f44ab3ac2ed1 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -382,7 +382,7 @@ public interface InputMethod { /** * Update token of the client window requesting {@link #showSoftInput(int, ResultReceiver)} - * @param showInputToken dummy app window token for window requesting + * @param showInputToken placeholder app window token for window requesting * {@link InputMethodManager#showSoftInput(View, int)} * @hide */ @@ -390,7 +390,7 @@ public interface InputMethod { /** * Update token of the client window requesting {@link #hideSoftInput(int, ResultReceiver)} - * @param hideInputToken dummy app window token for window requesting + * @param hideInputToken placeholder app window token for window requesting * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} * @hide */ diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 11a6acf5b934..67ed30f2ec73 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -1026,7 +1026,7 @@ public abstract class AbsSeekBar extends ProgressBar { } /** - * This is called when the user either releases his touch or the touch is + * This is called when the user either releases their touch or the touch is * canceled. */ void onStopTrackingTouch() { diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index c1c1a6e88b50..6aa4e466ab6f 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -483,7 +483,7 @@ public class DatePicker extends FrameLayout { /** * Returns whether the spinners are shown. * <p> - * <strong>Note:</strong> his method returns {@code false} when the + * <strong>Note:</strong> this method returns {@code false} when the * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set * to {@code calendar}. * diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java index bdfb5503946d..51869d4e04d5 100644 --- a/core/java/android/widget/ExpandableListView.java +++ b/core/java/android/widget/ExpandableListView.java @@ -1007,7 +1007,7 @@ public class ExpandableListView extends ListView { flatChildPos = mConnector.getFlattenedPos(elChildPos); - // Sanity check + // Validity check if (flatChildPos == null) { throw new IllegalStateException("Could not find child"); } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index b33660a200c2..3bb39c135034 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -123,7 +123,7 @@ public class HorizontalScrollView extends FrameLayout { /** * True if the user is currently dragging this ScrollView around. This is * not the same as 'is being flinged', which can be checked by - * mScroller.isFinished() (flinging begins when the user lifts his finger). + * mScroller.isFinished() (flinging begins when the user lifts their finger). */ @UnsupportedAppUsage private boolean mIsBeingDragged = false; @@ -560,7 +560,7 @@ public class HorizontalScrollView extends FrameLayout { /* * Shortcut the most recurring case: the user is in the dragging - * state and he is moving his finger. We want to intercept this + * state and they are moving their finger. We want to intercept this * motion. */ final int action = ev.getAction(); @@ -576,7 +576,7 @@ public class HorizontalScrollView extends FrameLayout { case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. + * whether the user has moved far enough from their original down touch. */ /* diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 3b482a8b2d54..8dfb936ef7ee 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -554,7 +554,7 @@ public class NumberPicker extends LinearLayout { public static int SCROLL_STATE_IDLE = 0; /** - * The user is scrolling using touch, and his finger is still on the screen. + * The user is scrolling using touch, and their finger is still on the screen. */ public static int SCROLL_STATE_TOUCH_SCROLL = 1; @@ -1675,7 +1675,7 @@ public class NumberPicker extends LinearLayout { // Do not draw the middle item if input is visible since the input // is shown only if the wheel is static and it covers the middle // item. Otherwise, if the user starts editing the text via the - // IME he may see a dimmed version of the old value intermixed + // IME they may see a dimmed version of the old value intermixed // with the new one. if ((showSelectorWheel && i != SELECTOR_MIDDLE_ITEM_INDEX) || (i == SELECTOR_MIDDLE_ITEM_INDEX && mInputText.getVisibility() != VISIBLE)) { diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index 849488d42bcf..4722fdc7818f 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -389,7 +389,7 @@ public class RadioGroup extends LinearLayout { /** * <p>A pass-through listener acts upon the events and dispatches them * to another listener. This allows the table layout to set its own internal - * hierarchy change listener without preventing the user to setup his.</p> + * hierarchy change listener without preventing the user to setup this.</p> */ private class PassThroughHierarchyChangeListener implements ViewGroup.OnHierarchyChangeListener { @@ -529,4 +529,4 @@ public class RadioGroup extends LinearLayout { private boolean isVisibleWithText(RadioButton button) { return button.getVisibility() == VISIBLE && !TextUtils.isEmpty(button.getText()); } -}
\ No newline at end of file +} diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 3847d6b2a613..b44b8c222fac 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -133,7 +133,7 @@ public class ScrollView extends FrameLayout { /** * True if the user is currently dragging this ScrollView around. This is * not the same as 'is being flinged', which can be checked by - * mScroller.isFinished() (flinging begins when the user lifts his finger). + * mScroller.isFinished() (flinging begins when the user lifts their finger). */ @UnsupportedAppUsage private boolean mIsBeingDragged = false; @@ -593,7 +593,7 @@ public class ScrollView extends FrameLayout { /* * Shortcut the most recurring case: the user is in the dragging - * state and he is moving his finger. We want to intercept this + * state and they is moving their finger. We want to intercept this * motion. */ final int action = ev.getAction(); @@ -616,7 +616,7 @@ public class ScrollView extends FrameLayout { case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. + * whether the user has moved far enough from their original down touch. */ /* diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java index 8bb4d16fe7df..8e635f4be68e 100644 --- a/core/java/android/widget/TableLayout.java +++ b/core/java/android/widget/TableLayout.java @@ -751,7 +751,7 @@ public class TableLayout extends LinearLayout { /** * <p>A pass-through listener acts upon the events and dispatches them * to another listener. This allows the table layout to set its own internal - * hierarchy change listener without preventing the user to setup his.</p> + * hierarchy change listener without preventing the user to setup this.</p> */ private class PassThroughHierarchyChangeListener implements OnHierarchyChangeListener { diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index a3c29a8d4a7b..491ddba8959d 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -70,7 +70,7 @@ import java.util.Map; public class AccessibilityShortcutController { private static final String TAG = "AccessibilityShortcutController"; - // Dummy component names for framework features + // Placeholder component names for framework features public static final ComponentName COLOR_INVERSION_COMPONENT_NAME = new ComponentName("com.android.server.accessibility", "ColorInversion"); public static final ComponentName DALTONIZER_COMPONENT_NAME = @@ -109,8 +109,8 @@ public class AccessibilityShortcutController { public FrameworkObjectProvider mFrameworkObjectProvider = new FrameworkObjectProvider(); /** - * @return An immutable map from dummy component names to feature info for toggling a framework - * feature + * @return An immutable map from placeholder component names to feature + * info for toggling a framework feature */ public static Map<ComponentName, ToggleableFrameworkFeatureInfo> getFrameworkShortcutFeaturesMap() { diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java index 508deacb49d7..0b92b93f2c88 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java @@ -42,7 +42,7 @@ import java.util.List; /** * Activity used to display various targets related to accessibility service, accessibility - * activity or white listing feature for volume key shortcut. + * activity or allowlisting feature for volume key shortcut. */ public class AccessibilityShortcutChooserActivity extends Activity { @ShortcutType diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java index d75659372a07..d84175ed1e7c 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java @@ -36,7 +36,7 @@ import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder; /** * Abstract base class for creating various target related to accessibility service, - * accessibility activity, and white listing feature. + * accessibility activity, and allowlisting feature. */ public abstract class AccessibilityTarget implements TargetOperations, OnTargetSelectedListener, OnTargetCheckedChangeListener { diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java index 60a102adcf7a..a7c5f6d5e03f 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java @@ -101,7 +101,7 @@ public final class AccessibilityTargetHelper { /** * Returns list of {@link AccessibilityTarget} of the installed accessibility service, - * accessibility activity, and white listing feature including accessibility feature's package + * accessibility activity, and allowlisting feature including accessibility feature's package * name, component id, etc. * * @param context The context of the application. diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index fd90b56426aa..5910b334dc2b 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -3099,7 +3099,7 @@ public class ChooserActivity extends ResolverActivity implements final TargetInfo ti = mChooserMultiProfilePagerAdapter.getActiveListAdapter() .targetInfoForPosition(mListPosition, /* filtered */ true); - // This should always be the case for ItemViewHolder, check for sanity + // This should always be the case for ItemViewHolder, check for validity if (ti instanceof DisplayResolveInfo) { showTargetDetails((DisplayResolveInfo) ti); } @@ -3480,7 +3480,7 @@ public class ChooserActivity extends ResolverActivity implements v.setOnLongClickListener(v1 -> { final TargetInfo ti = mChooserListAdapter.targetInfoForPosition( holder.getItemIndex(column), true); - // This should always be the case for non-DS targets, check for sanity + // This should always be the case for non-DS targets, check for validity if (ti instanceof DisplayResolveInfo) { showTargetDetails((DisplayResolveInfo) ti); } diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java index 92e9fe492442..56ec87cc8188 100644 --- a/core/java/com/android/internal/app/NetInitiatedActivity.java +++ b/core/java/com/android/internal/app/NetInitiatedActivity.java @@ -35,7 +35,7 @@ import com.android.internal.location.GpsNetInitiatedHandler; import com.android.server.LocalServices; /** - * This activity is shown to the user for him/her to accept or deny network-initiated + * This activity is shown to the user for them to accept or deny network-initiated * requests. It uses the alert dialog style. It will be launched from a notification. */ public class NetInitiatedActivity extends AlertActivity implements DialogInterface.OnClickListener { diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java index b3904f457708..e2342bb654a1 100644 --- a/core/java/com/android/internal/app/ToolbarActionBar.java +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -527,7 +527,7 @@ public class ToolbarActionBar extends ActionBar { public View onCreatePanelView(int featureId) { if (featureId == Window.FEATURE_OPTIONS_PANEL) { // This gets called by PhoneWindow.preparePanel. Since this already manages - // its own panel, we return a dummy view here to prevent PhoneWindow from + // its own panel, we return a placeholder view here to prevent PhoneWindow from // preparing a default one. return new View(mDecorToolbar.getContext()); } diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java index d2e71c85e7f9..88bdb66a2949 100644 --- a/core/java/com/android/internal/colorextraction/types/Tonal.java +++ b/core/java/com/android/internal/colorextraction/types/Tonal.java @@ -478,7 +478,7 @@ public class Tonal implements ExtractionType { public ConfigParser(Context context) { mTonalPalettes = new ArrayList<>(); - // Load all palettes and the blacklist from an XML. + // Load all palettes and the denylist from an XML. try { XmlPullParser parser = context.getResources().getXml(R.xml.color_extraction); int eventType = parser.getEventType(); diff --git a/core/java/com/android/internal/infra/GlobalWhitelistState.java b/core/java/com/android/internal/infra/GlobalWhitelistState.java index a0b2f94aea32..3c081e27305c 100644 --- a/core/java/com/android/internal/infra/GlobalWhitelistState.java +++ b/core/java/com/android/internal/infra/GlobalWhitelistState.java @@ -31,7 +31,7 @@ import java.util.List; /** * Helper class used to manage a {@link WhitelistHelper} per user instance when the main service * cannot hold a lock when external entities (typically {@code ActivityManagerService}) needs to - * get whitelist info. + * get allowlist info. * * <p>This class is thread safe. */ @@ -47,7 +47,7 @@ public class GlobalWhitelistState { protected SparseArray<WhitelistHelper> mWhitelisterHelpers; /** - * Sets the whitelist for the given user. + * Sets the allowlist for the given user. */ public void setWhitelist(@UserIdInt int userId, @Nullable List<String> packageNames, @Nullable List<ComponentName> components) { @@ -65,7 +65,7 @@ public class GlobalWhitelistState { } /** - * Checks if the given package is whitelisted for the given user. + * Checks if the given package is allowlisted for the given user. */ public boolean isWhitelisted(@UserIdInt int userId, @NonNull String packageName) { synchronized (mGlobalWhitelistStateLock) { @@ -76,7 +76,7 @@ public class GlobalWhitelistState { } /** - * Checks if the given component is whitelisted for the given user. + * Checks if the given component is allowlisted for the given user. */ public boolean isWhitelisted(@UserIdInt int userId, @NonNull ComponentName componentName) { synchronized (mGlobalWhitelistStateLock) { @@ -87,7 +87,7 @@ public class GlobalWhitelistState { } /** - * Gets the whitelisted components for the given package and user. + * Gets the allowlisted components for the given package and user. */ public ArraySet<ComponentName> getWhitelistedComponents(@UserIdInt int userId, @NonNull String packageName) { @@ -99,7 +99,7 @@ public class GlobalWhitelistState { } /** - * Resets the whitelist for the given user. + * Resets the allowlist for the given user. */ public void resetWhitelist(@NonNull int userId) { synchronized (mGlobalWhitelistStateLock) { diff --git a/core/java/com/android/internal/infra/WhitelistHelper.java b/core/java/com/android/internal/infra/WhitelistHelper.java index b1d85f78984c..1d76090f59f3 100644 --- a/core/java/com/android/internal/infra/WhitelistHelper.java +++ b/core/java/com/android/internal/infra/WhitelistHelper.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.Objects; /** - * Helper class for keeping track of whitelisted packages/activities. + * Helper class for keeping track of allowlisted packages/activities. * * <p><b>NOTE: </b>this class is not thread safe. * @hide @@ -38,18 +38,18 @@ public final class WhitelistHelper { private static final String TAG = "WhitelistHelper"; /** - * Map of whitelisted packages/activities. The whole package is whitelisted if its + * Map of allowlisted packages/activities. The whole package is allowlisted if its * corresponding value is {@code null}. */ @Nullable private ArrayMap<String, ArraySet<ComponentName>> mWhitelistedPackages; /** - * Sets the whitelist with the given packages and activities. The list is cleared if both + * Sets the allowlist with the given packages and activities. The list is cleared if both * packageNames and components are {@code null}. * - * @param packageNames packages to be whitelisted. - * @param components activities to be whitelisted. + * @param packageNames packages to be allowlisted. + * @param components activities to be allowlisted. * * @throws IllegalArgumentException if packages or components are empty. */ @@ -103,7 +103,7 @@ public final class WhitelistHelper { } /** - * Returns {@code true} if the entire package is whitelisted. + * Returns {@code true} if the entire package is allowlisted. */ public boolean isWhitelisted(@NonNull String packageName) { Objects.requireNonNull(packageName); @@ -115,7 +115,7 @@ public final class WhitelistHelper { } /** - * Returns {@code true} if the specified activity is whitelisted. + * Returns {@code true} if the specified activity is allowlisted. */ public boolean isWhitelisted(@NonNull ComponentName componentName) { Objects.requireNonNull(componentName); @@ -130,8 +130,8 @@ public final class WhitelistHelper { } /** - * Returns a set of whitelisted components with the given package, or null if nothing is - * whitelisted. + * Returns a set of allowlisted components with the given package, or null if nothing is + * allowlisted. */ @Nullable public ArraySet<ComponentName> getWhitelistedComponents(@NonNull String packageName) { diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index e5475f8bea9d..9b1299ebe54c 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -371,7 +371,7 @@ public final class InputMethodPrivilegedOperations { /** * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean)}. * - * @param showOrHideInputToken dummy token that maps to window requesting + * @param showOrHideInputToken placeholder token that maps to window requesting * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow * (IBinder, int)} diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index dd1e4f46a5de..095882ebe669 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -305,7 +305,7 @@ public class RuntimeInit { /** * Returns an HTTP user agent of the form - * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)". + * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MAIN)". */ private static String getDefaultUserAgent() { StringBuilder result = new StringBuilder(64); @@ -324,7 +324,7 @@ public class RuntimeInit { result.append(model); } } - String id = Build.ID; // "MASTER" or "M4-rc20" + String id = Build.ID; // "MAIN" or "M4-rc20" if (id.length() > 0) { result.append(" Build/"); result.append(id); diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 1ca9250f2d27..a985965afe17 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -206,7 +206,7 @@ public final class Zygote { /** List of packages with the same uid, and its app data info: volume uuid and inode. */ public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map"; - /** List of whitelisted packages and its app data info: volume uuid and inode. */ + /** List of allowlisted packages and its app data info: volume uuid and inode. */ public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map"; /** Bind mount app storage dirs to lower fs not via fuse */ @@ -327,7 +327,7 @@ public final class Zygote { * @param isTopApp true if the process is for top (high priority) application. * @param pkgDataInfoList A list that stores related packages and its app data * info: volume uuid and inode. - * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps. + * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps. * @param bindMountAppDataDirs True if the zygote needs to mount data dirs. * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs. * @@ -395,7 +395,7 @@ public final class Zygote { * volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name, * app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid, * app_b_ce_inode, ...]; - * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps. + * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps. * @param bindMountAppDataDirs True if the zygote needs to mount data dirs. * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs. */ diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index 22082d02d9f6..ed074327c3c5 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -227,7 +227,7 @@ class ZygoteArguments { String[] mPkgDataInfoList; /** - * A list that stores all whitelisted app data info: volume uuid and inode. + * A list that stores all allowlisted app data info: volume uuid and inode. * Null if it does need to do app data isolation. */ String[] mWhitelistedDataInfoList; diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index bb1733738643..a4ce027501b8 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -297,7 +297,7 @@ class ZygoteServer { mUsapPoolRefillDelayMs = Integer.parseInt(usapPoolRefillDelayMsPropString); } - // Sanity check + // Validity check if (mUsapPoolSizeMin >= mUsapPoolSizeMax) { Log.w(TAG, "The max size of the USAP pool must be greater than the minimum size." + " Restoring default values."); diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java index 8a4eb4a9ca71..bed85aed3625 100644 --- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java @@ -277,7 +277,6 @@ public class BaseProtoLogImpl { String group = groups[i]; IProtoLogGroup g = LOG_GROUPS.get(group); if (g != null) { - System.out.println("G: "+ g); if (setTextLogging) { g.setLogToLogcat(value); } else { diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index ef2dfd599fb6..cc2934fd8dc5 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.service.notification.StatusBarNotification; import com.android.internal.statusbar.StatusBarIcon; @@ -224,6 +225,11 @@ oneway interface IStatusBar void stopTracing(); /** + * Handles a logging command from the WM shell command. + */ + void handleWindowManagerLoggingCommand(in String[] args, in ParcelFileDescriptor outFd); + + /** * If true, suppresses the ambient display from showing. If false, re-enables the ambient * display. */ diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java index 10bd66f17870..fb871a428e1e 100644 --- a/core/java/com/android/internal/view/menu/MenuPopup.java +++ b/core/java/com/android/internal/view/menu/MenuPopup.java @@ -136,7 +136,7 @@ public abstract class MenuPopup implements ShowableListMenu, MenuPresenter, */ protected static int measureIndividualMenuWidth(ListAdapter adapter, ViewGroup parent, Context context, int maxAllowedWidth) { - // Menus don't tend to be long, so this is more sane than it looks. + // Menus don't tend to be long, so this is more valid than it looks. int maxWidth = 0; View itemView = null; int itemType = 0; diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index baf3188fb235..eef33684e883 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -361,7 +361,7 @@ public class ActionBarContainer extends FrameLayout { } /** - * Dummy drawable so that we don't break background display lists and + * Placeholder drawable so that we don't break background display lists and * projection surfaces. */ private class ActionBarBackgroundDrawable extends Drawable { diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 08a9f48ec0b4..c0c7f100ef21 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1211,7 +1211,7 @@ public class LockPatternUtils { } /** - * Set and store the lockout deadline, meaning the user can't attempt his/her unlock + * Set and store the lockout deadline, meaning the user can't attempt their unlock * pattern until the deadline has passed. * @return the chosen deadline. */ @@ -1229,7 +1229,7 @@ public class LockPatternUtils { /** * @return The elapsed time in millis in the future when the user is allowed to - * attempt to enter his/her lock pattern, or 0 if the user is welcome to + * attempt to enter their lock pattern, or 0 if the user is welcome to * enter a pattern. */ public long getLockoutAttemptDeadline(int userId) { @@ -1515,7 +1515,7 @@ public class LockPatternUtils { * Create an escrow token for the current user, which can later be used to unlock FBE * or change user password. * - * After adding, if the user currently has lockscreen password, he will need to perform a + * After adding, if the user currently has lockscreen password, they will need to perform a * confirm credential operation in order to activate the token for future use. If the user * has no secure lockscreen, then the token is activated immediately. * @@ -1592,7 +1592,7 @@ public class LockPatternUtils { /** * Unlock the specified user by an pre-activated escrow token. This should have the same effect - * on device encryption as the user entering his lockscreen credentials for the first time after + * on device encryption as the user entering their lockscreen credentials for the first time after * boot, this includes unlocking the user's credential-encrypted storage as well as the keystore * * <p>This method is only available to code running in the system server process itself. diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java index 38588ea9239d..f5df3abad658 100644 --- a/core/java/com/android/internal/widget/LockSettingsInternal.java +++ b/core/java/com/android/internal/widget/LockSettingsInternal.java @@ -30,7 +30,7 @@ public abstract class LockSettingsInternal { * Create an escrow token for the current user, which can later be used to unlock FBE * or change user password. * - * After adding, if the user currently has lockscreen password, he will need to perform a + * After adding, if the user currently has lockscreen password, they will need to perform a * confirm credential operation in order to activate the token for future use. * Once the token is activated, the callback that is passed here is called. If the user * has no secure lockscreen, then the token is activated immediately. diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java index c8a86d108134..6f377b9b228a 100644 --- a/core/java/com/android/internal/widget/ViewPager.java +++ b/core/java/com/android/internal/widget/ViewPager.java @@ -1843,7 +1843,7 @@ public class ViewPager extends ViewGroup { case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. + * whether the user has moved far enough from their original down touch. */ /* diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java index b4610bda2cd9..ce9ab82614d5 100644 --- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java +++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java @@ -262,14 +262,14 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper { boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomaticallyAsUser( mUserId); if (currentMasterSyncEnabled) { - // Disable master sync to prevent any syncs from running. + // Disable global sync to prevent any syncs from running. ContentResolver.setMasterSyncAutomaticallyAsUser(false, mUserId); } try { restoreFromJsonArray(accountJSONArray, mUserId); } finally { - // Set the master sync preference to the value from the backup set. + // Set the global sync preference to the value from the backup set. ContentResolver.setMasterSyncAutomaticallyAsUser(masterSyncEnabled, mUserId); } Log.i(TAG, "Restore successful."); diff --git a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java index ffae7570ea79..e1fe1bdbdd5f 100644 --- a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java +++ b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java @@ -113,7 +113,7 @@ import javax.net.ssl.TrustManagerFactory; * <li> * <p> * Send the certificate request to the trusted Certificate Authority for signature. - * One may choose to act as her own CA and sign the certificate request using a PKI + * One may choose to act as their own CA and sign the certificate request using a PKI * tool, such as OpenSSL. * </p> * </li> diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp index e78e08e4f170..2944f72f7f4d 100644 --- a/core/jni/android_hardware_HardwareBuffer.cpp +++ b/core/jni/android_hardware_HardwareBuffer.cpp @@ -30,8 +30,9 @@ #include <binder/Parcel.h> -#include <ui/GraphicBuffer.h> #include <private/gui/ComposerService.h> +#include <ui/GraphicBuffer.h> +#include <ui/PixelFormat.h> #include <hardware/gralloc1.h> #include <grallocusage/GrallocUsageConversion.h> @@ -166,6 +167,20 @@ static jlong android_hardware_HardwareBuffer_getUsage(JNIEnv* env, return AHardwareBuffer_convertFromGrallocUsageBits(buffer->getUsage()); } +static jlong android_hardware_HardwareBuffer_estimateSize(jlong nativeObject) { + GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject); + + uint32_t bpp = bytesPerPixel(buffer->getPixelFormat()); + if (bpp == 0) { + // If the pixel format is not recognized, use 1 as default. + bpp = 1; + } + + const uint32_t bufferStride = + buffer->getStride() > 0 ? buffer->getStride() : buffer->getWidth(); + return static_cast<jlong>(buffer->getHeight() * bufferStride * bpp); +} + // ---------------------------------------------------------------------------- // Serialization // ---------------------------------------------------------------------------- @@ -247,6 +262,7 @@ uint64_t android_hardware_HardwareBuffer_convertToGrallocUsageBits(uint64_t usag const char* const kClassPathName = "android/hardware/HardwareBuffer"; +// clang-format off static const JNINativeMethod gMethods[] = { { "nCreateHardwareBuffer", "(IIIIJ)J", (void*) android_hardware_HardwareBuffer_create }, @@ -267,7 +283,11 @@ static const JNINativeMethod gMethods[] = { { "nGetFormat", "(J)I", (void*) android_hardware_HardwareBuffer_getFormat }, { "nGetLayers", "(J)I", (void*) android_hardware_HardwareBuffer_getLayers }, { "nGetUsage", "(J)J", (void*) android_hardware_HardwareBuffer_getUsage }, + + // --------------- @CriticalNative ---------------------- + { "nEstimateSize", "(J)J", (void*) android_hardware_HardwareBuffer_estimateSize }, }; +// clang-format on int register_android_hardware_HardwareBuffer(JNIEnv* env) { int err = RegisterMethodsOrDie(env, kClassPathName, gMethods, diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index 7cfe3bc1020a..0892b70b9651 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -114,7 +114,7 @@ static jint android_os_Parcel_dataCapacity(jlong nativePtr) return parcel ? parcel->dataCapacity() : 0; } -static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size) +static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); if (parcel != NULL) { @@ -122,9 +122,7 @@ static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nati if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } - return parcel->getOpenAshmemSize(); } - return 0; } static void android_os_Parcel_setDataPosition(jlong nativePtr, jint pos) @@ -308,7 +306,7 @@ static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong } } -static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object) +static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); if (parcel != NULL) { @@ -317,9 +315,7 @@ static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jl if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } - return parcel->getOpenAshmemSize(); } - return 0; } static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr) @@ -506,14 +502,12 @@ static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz) return reinterpret_cast<jlong>(parcel); } -static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr) +static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); if (parcel != NULL) { parcel->freeData(); - return parcel->getOpenAshmemSize(); } - return 0; } static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr) @@ -551,12 +545,12 @@ static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong na return ret; } -static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr, +static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr, jbyteArray data, jint offset, jint length) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); if (parcel == NULL || length < 0) { - return 0; + return; } jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0); @@ -570,7 +564,6 @@ static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativ env->ReleasePrimitiveArrayCritical(data, array, 0); } - return parcel->getOpenAshmemSize(); } static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisNativePtr, @@ -588,23 +581,23 @@ static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisN return thisParcel->compareData(*otherParcel); } -static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr, +static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr, jlong otherNativePtr, jint offset, jint length) { Parcel* thisParcel = reinterpret_cast<Parcel*>(thisNativePtr); if (thisParcel == NULL) { - return 0; + return; } Parcel* otherParcel = reinterpret_cast<Parcel*>(otherNativePtr); if (otherParcel == NULL) { - return thisParcel->getOpenAshmemSize(); + return; } status_t err = thisParcel->appendFrom(otherParcel, offset, length); if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } - return thisParcel->getOpenAshmemSize(); + return; } static jboolean android_os_Parcel_hasFileDescriptors(jlong nativePtr) @@ -720,7 +713,7 @@ static const JNINativeMethod gParcelMethods[] = { // @CriticalNative {"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity}, // @FastNative - {"nativeSetDataSize", "(JI)J", (void*)android_os_Parcel_setDataSize}, + {"nativeSetDataSize", "(JI)V", (void*)android_os_Parcel_setDataSize}, // @CriticalNative {"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition}, // @FastNative @@ -749,7 +742,7 @@ static const JNINativeMethod gParcelMethods[] = { // @FastNative {"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder}, // @FastNative - {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor}, + {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor}, {"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray}, {"nativeReadByteArray", "(J[BI)Z", (void*)android_os_Parcel_readByteArray}, @@ -772,13 +765,13 @@ static const JNINativeMethod gParcelMethods[] = { {"nativeReadFileDescriptor", "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor}, {"nativeCreate", "()J", (void*)android_os_Parcel_create}, - {"nativeFreeBuffer", "(J)J", (void*)android_os_Parcel_freeBuffer}, + {"nativeFreeBuffer", "(J)V", (void*)android_os_Parcel_freeBuffer}, {"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy}, {"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall}, - {"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall}, + {"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall}, {"nativeCompareData", "(JJ)I", (void*)android_os_Parcel_compareData}, - {"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom}, + {"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom}, // @CriticalNative {"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors}, {"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken}, diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 4ef15d7098ff..9efe4b15ce7a 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -30,6 +30,7 @@ #include <android_runtime/android_hardware_HardwareBuffer.h> #include <android_runtime/android_view_Surface.h> #include <android_runtime/android_view_SurfaceSession.h> +#include <gui/IScreenCaptureListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -188,6 +189,11 @@ static struct { static struct { jclass clazz; + jmethodID onScreenCaptureComplete; +} gScreenCaptureListenerClassInfo; + +static struct { + jclass clazz; jmethodID ctor; jfieldID defaultConfig; jfieldID primaryRefreshRateMin; @@ -226,6 +232,56 @@ constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode } } +class ScreenCaptureListenerWrapper : public BnScreenCaptureListener { +public: + explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) { + env->GetJavaVM(&mVm); + screenCaptureListenerObject = env->NewGlobalRef(jobject); + LOG_ALWAYS_FATAL_IF(!screenCaptureListenerObject, "Failed to make global ref"); + } + + ~ScreenCaptureListenerWrapper() { + if (screenCaptureListenerObject) { + getenv()->DeleteGlobalRef(screenCaptureListenerObject); + screenCaptureListenerObject = nullptr; + } + } + + status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override { + JNIEnv* env = getenv(); + if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) { + env->CallVoidMethod(screenCaptureListenerObject, + gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr); + return NO_ERROR; + } + jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( + env, captureResults.buffer->toAHardwareBuffer()); + const jint namedColorSpace = + fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace); + jobject screenshotHardwareBuffer = + env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz, + gScreenshotHardwareBufferClassInfo.builder, + jhardwareBuffer, namedColorSpace, + captureResults.capturedSecureLayers); + env->CallVoidMethod(screenCaptureListenerObject, + gScreenCaptureListenerClassInfo.onScreenCaptureComplete, + screenshotHardwareBuffer); + env->DeleteLocalRef(jhardwareBuffer); + env->DeleteLocalRef(screenshotHardwareBuffer); + return NO_ERROR; + } + +private: + jobject screenCaptureListenerObject; + JavaVM* mVm; + + JNIEnv* getenv() { + JNIEnv* env; + mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); + return env; + } +}; + // ---------------------------------------------------------------------------- static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) { @@ -327,36 +383,28 @@ static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env, return captureArgs; } -static jobject nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject) { +static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject, + jobject screenCaptureListenerObject) { const DisplayCaptureArgs captureArgs = displayCaptureArgsFromObject(env, displayCaptureArgsObject); if (captureArgs.displayToken == NULL) { - return NULL; - } - - ScreenCaptureResults captureResults; - status_t res = ScreenshotClient::captureDisplay(captureArgs, captureResults); - if (res != NO_ERROR) { - return NULL; + return BAD_VALUE; } - jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( - env, captureResults.buffer->toAHardwareBuffer()); - const jint namedColorSpace = - fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace); - return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz, - gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer, - namedColorSpace, captureResults.capturedSecureLayers); + sp<IScreenCaptureListener> captureListener = + new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject); + return ScreenshotClient::captureDisplay(captureArgs, captureListener); } -static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject) { +static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject, + jobject screenCaptureListenerObject) { LayerCaptureArgs captureArgs; getCaptureArgs(env, layerCaptureArgsObject, captureArgs); SurfaceControl* layer = reinterpret_cast<SurfaceControl*>( env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer)); if (layer == nullptr) { - return nullptr; + return BAD_VALUE; } captureArgs.layerHandle = layer->getHandle(); @@ -380,19 +428,9 @@ static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptu env->ReleaseLongArrayElements(excludeObjectArray, const_cast<jlong*>(objects), JNI_ABORT); } - ScreenCaptureResults captureResults; - status_t res = ScreenshotClient::captureLayers(captureArgs, captureResults); - if (res != NO_ERROR) { - return NULL; - } - - jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( - env, captureResults.buffer->toAHardwareBuffer()); - const jint namedColorSpace = - fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace); - return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz, - gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer, - namedColorSpace, captureResults.capturedSecureLayers); + sp<IScreenCaptureListener> captureListener = + new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject); + return ScreenshotClient::captureLayers(captureArgs, captureListener); } static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) { @@ -1664,12 +1702,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { {"nativeSetOverrideScalingMode", "(JJI)V", (void*)nativeSetOverrideScalingMode }, {"nativeCaptureDisplay", - "(Landroid/view/SurfaceControl$DisplayCaptureArgs;)" - "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;", + "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I", (void*)nativeCaptureDisplay }, {"nativeCaptureLayers", - "(Landroid/view/SurfaceControl$LayerCaptureArgs;)" - "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;", + "(Landroid/view/SurfaceControl$LayerCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I", (void*)nativeCaptureLayers }, {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V", (void*)nativeSetInputWindowInfo }, @@ -1875,6 +1911,12 @@ int register_android_view_SurfaceControl(JNIEnv* env) gLayerCaptureArgsClassInfo.childrenOnly = GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z"); + jclass screenCaptureListenerClazz = + FindClassOrDie(env, "android/view/SurfaceControl$ScreenCaptureListener"); + gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz); + gScreenCaptureListenerClassInfo.onScreenCaptureComplete = + GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete", + "(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V"); return err; } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index f96ed36fcedd..e6bfeccfa8ae 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -826,7 +826,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, } if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) { - // Sane default of no storage visible + // Valid default of no storage visible return; } @@ -1108,7 +1108,7 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server, // Temporarily block SIGCHLD during forks. The SIGCHLD handler might // log, which would result in the logging FDs we close being reopened. - // This would cause failures because the FDs are not whitelisted. + // This would cause failures because the FDs are not allowlisted. // // Note that the zygote process is single threaded at this point. BlockSignal(SIGCHLD, fail_fn); @@ -1314,7 +1314,7 @@ static void relabelAllDirs(const char* path, security_context_t context, fail_fn * in data directories. * * Steps: - * 1). Collect a list of all related apps (apps with same uid and whitelisted apps) data info + * 1). Collect a list of all related apps (apps with same uid and allowlisted apps) data info * (package name, data stored volume uuid, and inode number of its CE data directory) * 2). Mount tmpfs on /data/data, /data/user(_de) and /mnt/expand, so apps no longer * able to access apps data directly. @@ -2048,7 +2048,7 @@ static void UnmountStorageOnInit(JNIEnv* env) { return; } - // Mark rootfs as being a slave so that changes from default + // Mark rootfs as being MS_SLAVE so that changes from default // namespace only flow into our children. if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) { RuntimeAbort(env, __LINE__, "Failed to mount() rootfs as MS_SLAVE"); diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 5609b36703ae..9fed1b95f6c3 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -40,6 +40,7 @@ import "frameworks/base/core/proto/android/server/fingerprint.proto"; import "frameworks/base/core/proto/android/server/jobscheduler.proto"; import "frameworks/base/core/proto/android/server/location/context_hub.proto"; import "frameworks/base/core/proto/android/server/powermanagerservice.proto"; +import "frameworks/base/core/proto/android/server/powerstatsservice.proto"; import "frameworks/base/core/proto/android/server/rolemanagerservice.proto"; import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; import "frameworks/base/core/proto/android/service/appwidget.proto"; @@ -510,6 +511,11 @@ message IncidentProto { (section).args = "sensorservice --proto" ]; + optional com.android.server.powerstats.PowerStatsServiceProto powerstats = 3054 [ + (section).type = SECTION_DUMPSYS, + (section).args = "power_stats --proto" + ]; + // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999 optional android.util.TextDumpProto textdump_wifi = 4000 [ (section).type = SECTION_TEXT_DUMPSYS, diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto new file mode 100644 index 000000000000..c80524402335 --- /dev/null +++ b/core/proto/android/server/powerstatsservice.proto @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package com.android.server.powerstats; + +option java_multiple_files = true; + +message IncidentReportProto { + /** Section number matches that in incident.proto */ + optional PowerStatsServiceProto incident_report = 3054; +} + +message PowerStatsServiceProto { + repeated RailInfoProto rail_info = 1; + repeated EnergyDataProto energy_data = 2; +} + +/** + * Rail information: + * Reports information related to the rails being monitored. + */ +message RailInfoProto { + /** Index corresponding to the rail */ + optional int32 index = 1; + + /** Name of the rail (opaque to the framework) */ + optional string rail_name = 2; + + /** Name of the subsystem to which this rail belongs (opaque to the framework) */ + optional string subsys_name = 3; + + /** Hardware sampling rate */ + optional int32 sampling_rate = 4; +} + +/** + * Rail level energy measurements: + * Reports accumulated energy since boot on each rail. + */ +message EnergyDataProto { + /** + * Index corresponding to the rail. This index matches + * the index returned in RailInfo + */ + optional int32 index = 1; + + /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */ + optional int64 timestamp_ms = 2; + + /** Accumulated energy since device boot in microwatt-seconds (uWs) */ + optional int64 energy_uws = 3; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 560e3c11d2f4..68e2891657ac 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -107,6 +107,8 @@ <!-- @deprecated This is rarely used and will be phased out soon. --> <protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" /> + <protected-broadcast android:name="android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL" /> + <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" /> <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" /> <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE_PRIORITIZED" /> @@ -892,11 +894,11 @@ <p> This is a soft restricted permission which cannot be held by an app it its full form until the installer on record whitelists the permission. - Specifically, if the permission is whitelisted the holder app can access + Specifically, if the permission is allowlisted the holder app can access external storage and the visual and aural media collections while if the - permission is not whitelisted the holder app can only access to the visual + permission is not allowlisted the holder app can only access to the visual and aural medial collections. Also the permission is immutably restricted - meaning that the whitelist state can be specified only at install time and + meaning that the allowlist state can be specified only at install time and cannot change until the app is installed. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. <p>Protection level: dangerous --> @@ -920,7 +922,7 @@ read/write files in your application-specific directories returned by {@link android.content.Context#getExternalFilesDir} and {@link android.content.Context#getExternalCacheDir}. - <p>If this permission is not whitelisted for an app that targets an API level before + <p>If this permission is not allowlisted for an app that targets an API level before {@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p> <p>Protection level: dangerous</p> --> @@ -3781,7 +3783,7 @@ <permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" android:protectionLevel="signature" /> - <!-- @SystemApi Allows an application to whitelist restricted permissions + <!-- @SystemApi Allows an application to allowlist restricted permissions on any of the whitelists. @hide --> <permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" @@ -4155,7 +4157,7 @@ <permission android:name="android.permission.CHANGE_APP_IDLE_STATE" android:protectionLevel="signature|privileged" /> - <!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to + <!-- @hide @SystemApi Allows an application to temporarily allowlist an inactive app to access the network and acquire wakelocks. <p>Not for use by third-party applications. --> <permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" @@ -4779,7 +4781,7 @@ <permission android:name="android.permission.ACCESS_VR_STATE" android:protectionLevel="signature|preinstalled" /> - <!-- Allows an application to whitelist tasks during lock task mode + <!-- Allows an application to allowlist tasks during lock task mode @hide <p>Not for use by third-party applications.</p> --> <permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES" android:protectionLevel="signature|setup" /> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 050c1c4b4df5..fbbdaa3d7358 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -239,9 +239,7 @@ <!-- Old synonym for "privileged". Deprecated in API level 23. --> <flag name="system" value="0x10" /> <!-- Additional flag from base permission type: this permission can also - (optionally) be granted to development applications. Although undocumented, the - permission state used to be shared by all users (including future users), but it is - managed per-user since API level 31. --> + (optionally) be granted to development applications. --> <flag name="development" value="0x20" /> <!-- Additional flag from base permission type: this permission is closely associated with an app op for controlling access. --> @@ -273,7 +271,7 @@ <flag name="runtime" value="0x2000" /> <!-- Additional flag from base permission type: this permission can be granted only if its protection level is signature, the requesting app resides on the OEM partition, - and the OEM has white-listed the app to receive this permission by the OEM. + and the OEM has allowlisted the app to receive this permission by the OEM. --> <flag name="oem" value="0x4000" /> <!-- Additional flag from base permission type: this permission can be granted to @@ -340,7 +338,7 @@ <flag name="softRestricted" value="0x8" /> <!-- This permission is restricted immutably which means that its restriction state may be specified only on the first install of - the app and will stay in this initial whitelist state until + the app and will stay in this initial allowlist state until the app is uninstalled. --> <flag name="immutablyRestricted" value="0x10" /> @@ -1409,13 +1407,13 @@ <attr name="lockTaskMode"> <!-- This is the default value. Tasks will not launch into lockTask mode but can be placed there by calling {@link android.app.Activity#startLockTask}. If a task with - this mode has been whitelisted using {@link + this mode has been allowlisted using {@link android.app.admin.DevicePolicyManager#setLockTaskPackages} then calling {@link android.app.Activity#startLockTask} will enter lockTask mode immediately, otherwise the user will be presented with a dialog to approve entering pinned mode. <p>If the system is already in lockTask mode when a new task rooted at this activity is launched that task will or will not start depending on whether the package of this - activity has been whitelisted. + activity has been allowlisted. <p>Tasks rooted at this activity can only exit lockTask mode using {@link android.app.Activity#stopLockTask}. --> <enum name="normal" value="0"/> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 550162a242dc..a161b9080660 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -364,7 +364,7 @@ less than 0x600 --> <bool translatable="false" name="config_apfDrop802_3Frames">true</bool> - <!-- An array of Black listed EtherType, packets with EtherTypes within this array + <!-- An array of Denylisted EtherType, packets with EtherTypes within this array will be dropped TODO: need to put proper values, these are for testing purposes only --> <integer-array translatable="false" name="config_apfEthTypeBlackList"> @@ -1166,6 +1166,7 @@ 0 - Nothing 1 - Launch all apps intent 2 - Launch assist intent + 3 - Launch notification panel This needs to match the constants in policy/src/com/android/internal/policy/impl/PhoneWindowManager.java --> @@ -1539,7 +1540,7 @@ <!-- Class name of WallpaperManagerService. --> <string name="config_wallpaperManagerServiceName" translatable="false">com.android.server.wallpaper.WallpaperManagerService</string> - <!-- Enables the TimeZoneRuleManager service. This is the master switch for the updateable time + <!-- Enables the TimeZoneRuleManager service. This is the global switch for the updateable time zone update mechanism. --> <bool name="config_enableUpdateableTimeZoneRules">false</bool> @@ -2127,6 +2128,9 @@ <!-- Type of the long press sensor. Empty if long press is not supported. --> <string name="config_dozeLongPressSensorType" translatable="false"></string> + <!-- Type of the udfps long press sensor. Empty if long press is not supported. --> + <string name="config_dozeUdfpsLongPressSensorType" translatable="false"></string> + <!-- If the sensor that wakes up the lock screen is available or not. --> <bool name="config_dozeWakeLockScreenSensorAvailable">false</bool> <integer name="config_dozeWakeLockScreenDebounce">300</integer> @@ -2309,18 +2313,18 @@ will be locked. --> <bool name="config_multiuserDelayUserDataLocking">false</bool> - <!-- Whether to only install system packages on a user if they're whitelisted for that user + <!-- Whether to only install system packages on a user if they're allowlisted for that user type. These are flags and can be freely combined. - 0 - disable whitelist (install all system packages; no logging) - 1 - enforce (only install system packages if they are whitelisted) - 2 - log (log non-whitelisted packages) - 4 - any package not mentioned in the whitelist file is implicitly whitelisted on all users + 0 - disable allowlist (install all system packages; no logging) + 1 - enforce (only install system packages if they are allowlisted) + 2 - log (log non-allowlisted packages) + 4 - any package not mentioned in the allowlist file is implicitly allowlisted on all users 8 - same as 4, but just for the SYSTEM user 16 - ignore OTAs (don't install system packages during OTAs) Common scenarios: - - to enable feature (fully enforced) for a complete whitelist: 1 - - to enable feature for an incomplete whitelist (so use implicit whitelist mode): 5 - - to enable feature but implicitly whitelist for SYSTEM user to ease local development: 9 + - to enable feature (fully enforced) for a complete allowlist: 1 + - to enable feature for an incomplete allowlist (so use implicit allowlist mode): 5 + - to enable feature but implicitly allowlist for SYSTEM user to ease local development: 9 - to disable feature completely if it had never been enabled: 16 - to henceforth disable feature and try to undo its previous effects: 0 Note: This list must be kept current with PACKAGE_WHITELIST_MODE_PROP in @@ -2498,25 +2502,25 @@ Example: com.google.android.myapp/.resolver.MyResolverActivity --> <string name="config_customResolverActivity" translatable="false"></string> - <!-- Name of the activity or service that prompts the user to reject, accept, or whitelist + <!-- Name of the activity or service that prompts the user to reject, accept, or allowlist an adb host's public key, when an unwhitelisted host connects to the local adbd. Can be customized for other product types --> <string name="config_customAdbPublicKeyConfirmationComponent" >com.android.systemui/com.android.systemui.usb.UsbDebuggingActivity</string> - <!-- Name of the activity that prompts the secondary user to acknowledge she/he needs to + <!-- Name of the activity that prompts the secondary user to acknowledge they need to switch to the primary user to enable USB debugging. Can be customized for other product types --> <string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" >com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string> - <!-- Name of the activity or service that prompts the user to reject, accept, or whitelist + <!-- Name of the activity or service that prompts the user to reject, accept, or allowlist a wireless network for wireless debugging. Can be customized for other product types --> <string name="config_customAdbWifiNetworkConfirmationComponent" >com.android.systemui/com.android.systemui.wifi.WifiDebuggingActivity</string> - <!-- Name of the activity that prompts the secondary user to acknowledge she/he needs to + <!-- Name of the activity that prompts the secondary user to acknowledge they need to switch to the primary user to enable wireless debugging. Can be customized for other product types --> <string name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent" @@ -2564,7 +2568,7 @@ <string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string> <!-- Flag indicating that the media framework should not allow changes or mute on any - stream or master volumes. --> + stream or global volumes. --> <bool name="config_useFixedVolume">false</bool> <!-- The list of IMEs which should be disabled until used. @@ -3390,6 +3394,9 @@ service. --> <string name="config_tvRemoteServicePackage" translatable="false"></string> + <!-- The package name of the package implementing the custom notification panel --> + <string name="config_notificationHandlerPackage" translatable="false"></string> + <!-- True if the device supports persisting security logs across reboots. This requires the device's kernel to have pstore and pmsg enabled, and DRAM to be powered and refreshed through all stages of reboot. --> @@ -4082,7 +4089,7 @@ <!-- All of the paths defined for the batterymeter are defined on a 12x20 canvas, and must be parsable by android.utill.PathParser --> <string name="config_batterymeterPerimeterPath" translatable="false"> - M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z + M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z </string> <string name="config_batterymeterErrorPerimeterPath" translatable="false">@string/config_batterymeterPerimeterPath</string> <string name="config_batterymeterFillMask" translatable="false"> @@ -4278,10 +4285,10 @@ we only enabled it while the device has ability of mixed color spaces composition --> <bool name="config_enableWcgMode">false</bool> - <!-- When true, enables the whitelisted app to handle bug reports from power menu short press. --> + <!-- When true, enables the allowlisted app to handle bug reports from power menu short press. --> <bool name="config_bugReportHandlerEnabled">false</bool> - <!-- The package name for the default bug report handler app from power menu short press. This app must be whitelisted. --> + <!-- The package name for the default bug report handler app from power menu short press. This app must be allowlisted. --> <string name="config_defaultBugReportHandlerApp" translatable="false"></string> <!-- The default value used for RawContacts.ACCOUNT_NAME when contacts are inserted without this diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4d74cf7d79bc..a5e5fba9a14e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -354,11 +354,11 @@ <!-- A notification is shown when the AccountManager is unable to supply an auth token without prompting the user to re-enter the password. This is the text that will scroll through the - notification bar (will be seen by the user as he uses another application). --> + notification bar (will be seen by the user as they use another application). --> <string name="notification_title">Signin error for <xliff:g id="account" example="foo@gmail.com">%1$s</xliff:g></string> <!-- Sync notifications --> <skip /> - <!-- A notification is shown when there is a sync error. This is the text that will scroll through the notification bar (will be seen by the user as he uses another application). --> + <!-- A notification is shown when there is a sync error. This is the text that will scroll through the notification bar (will be seen by the user as they use another application). --> <string name="contentServiceSync">Sync</string> <!-- A notification is shown when there is a sync error. This is the title of the notification. It will be seen in the pull-down notification tray. [CHAR LIMIT=NOTIF_TITLE] --> <string name="contentServiceSyncNotificationTitle">Can\'t sync</string> @@ -3126,7 +3126,7 @@ <string name="use_a_different_app">Use a different app</string> <!-- Text displayed when the user selects the check box for setting default application. See the "Use by default for this action" check box. --> <string name="clearDefaultHintMsg">Clear default in System settings > Apps > Downloaded.</string> - <!-- Default title for the activity chooser, when one is not given. Android allows multiple activities to perform an action. for example, there may be many ringtone pickers installed. A dialog is shown to the user allowing him to pick which activity should be used. This is the title. --> + <!-- Default title for the activity chooser, when one is not given. Android allows multiple activities to perform an action. for example, there may be many ringtone pickers installed. A dialog is shown to the user allowing them to pick which activity should be used. This is the title. --> <string name="chooseActivity">Choose an action</string> <!-- title for the USB activity chooser. --> <string name="chooseUsbActivity">Choose an app for the USB device</string> @@ -3346,7 +3346,7 @@ <string name="ringtone_picker_title_alarm">Alarm sounds</string> <!-- The title of the notification sound picker dialog [CHAR LIMIT=100] --> <string name="ringtone_picker_title_notification">Notification sounds</string> - <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead. For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. --> + <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, this is shown instead. For example, if the ringtone was on a SD card and it had been removed, this would be shown for ringtones on that SD card. --> <string name="ringtone_unknown">Unknown</string> <!-- A notification is shown when a wifi captive portal network is detected. This is the notification's title. --> @@ -3905,7 +3905,7 @@ <string name="gpsNotifTitle">Location request</string> <!-- Network positioning notification message. The name of the user (e.g. John Doe) and service (SUPL-service) who sent the request is shown as dynamic strings. - Translation should not be longer than master text. --> + Translation should not be longer than example text. --> <string name="gpsNotifMessage">Requested by <xliff:g id="name">%1$s</xliff:g> (<xliff:g id="service" example="SUPL-service">%2$s</xliff:g>)</string> <!-- Network positioning verification Yes. Button to push to share location information. --> <string name="gpsVerifYes">Yes</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 35ce780d3408..fdcd39a7f4cb 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3047,6 +3047,9 @@ <java-symbol type="string" name="config_tvRemoteServicePackage" /> <java-symbol type="string" name="notification_messaging_title_template" /> + <!-- Notification handler / dashboard package --> + <java-symbol type="string" name="config_notificationHandlerPackage" /> + <java-symbol type="bool" name="config_supportPreRebootSecurityLogs" /> <java-symbol type="dimen" name="notification_media_image_margin_end" /> @@ -3484,6 +3487,7 @@ <java-symbol type="array" name="config_hideWhenDisabled_packageNames" /> <java-symbol type="string" name="config_dozeLongPressSensorType" /> + <java-symbol type="string" name="config_dozeUdfpsLongPressSensorType" /> <java-symbol type="bool" name="config_dozeWakeLockScreenSensorAvailable" /> <java-symbol type="integer" name="config_dozeWakeLockScreenDebounce" /> diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-lineage-invalid.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-lineage-invalid.apk Binary files differnew file mode 100644 index 000000000000..f9777c3f5ca5 --- /dev/null +++ b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-lineage-invalid.apk diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-lineage-valid.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-lineage-valid.apk Binary files differnew file mode 100644 index 000000000000..955652e387b8 --- /dev/null +++ b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-lineage-valid.apk diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java index 6328101b7349..0c351d149d8b 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java @@ -269,7 +269,7 @@ public class ActivityManagerTest extends AndroidTestCase { return unmarshalled; } - // If any entries in appear in the list, sanity check them against all running applications + // If any entries in appear in the list, validity check them against all running applications private void checkErrorListSanity(List<ActivityManager.ProcessErrorStateInfo> errList) { if (errList == null) return; @@ -277,7 +277,7 @@ public class ActivityManagerTest extends AndroidTestCase { while (iter.hasNext()) { ActivityManager.ProcessErrorStateInfo info = iter.next(); assertNotNull(info); - // sanity checks + // validity checks assertTrue((info.condition == ActivityManager.ProcessErrorStateInfo.CRASHED) || (info.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING)); // TODO look at each of these and consider a stronger test diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java index 1f4e5dffc84e..da386a6bac90 100644 --- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java +++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java @@ -277,7 +277,7 @@ public class AssistStructureTest { try { // Write to parcel - parcel.setDataPosition(0); // Sanity / paranoid check + parcel.setDataPosition(0); // Validity Check structure.writeToParcel(parcel, NO_FLAGS); // Read from parcel diff --git a/core/tests/coretests/src/android/content/FakeProviderLocal.java b/core/tests/coretests/src/android/content/FakeProviderLocal.java index a8c2f4059d50..29f054bd482d 100644 --- a/core/tests/coretests/src/android/content/FakeProviderLocal.java +++ b/core/tests/coretests/src/android/content/FakeProviderLocal.java @@ -20,7 +20,7 @@ import android.database.Cursor; import android.net.Uri; /** - * A dummy content provider for tests. This provider runs in the same process as the test. + * A placeholder content provider for tests. This provider runs in the same process as the test. */ public class FakeProviderLocal extends ContentProvider { diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java index 8bc56607f555..2c92da34d9a4 100644 --- a/core/tests/coretests/src/android/content/FakeProviderRemote.java +++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java @@ -20,7 +20,7 @@ import android.database.Cursor; import android.net.Uri; /** - * A dummy content provider for tests. This provider runs in a different process from the test. + * A placeholder content provider for tests. This provider runs in a different process from the test. */ public class FakeProviderRemote extends ContentProvider { diff --git a/core/tests/coretests/src/android/content/SlowProvider.java b/core/tests/coretests/src/android/content/SlowProvider.java index aba32e836e80..2ba0effa130e 100644 --- a/core/tests/coretests/src/android/content/SlowProvider.java +++ b/core/tests/coretests/src/android/content/SlowProvider.java @@ -20,8 +20,8 @@ import android.database.Cursor; import android.net.Uri; /** - * A dummy content provider for tests. This provider runs in a different process from the test and - * is intentionally slow. + * A placeholder content provider for tests. This provider runs in a + * different process from the test and is intentionally slow. */ public class SlowProvider extends ContentProvider { diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java index ea6917682965..7af96c3f9c6b 100644 --- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java @@ -486,7 +486,7 @@ public class AtomicFormulaTest { assertThat(boolFormula.isInstallerFormula()).isFalse(); } - /** Returns a builder with all fields filled with some dummy data. */ + /** Returns a builder with all fields filled with some placeholder data. */ private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { return new AppInstallMetadata.Builder() .setPackageName("abc") diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java index abc5fed181de..ba060fa630c5 100644 --- a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java @@ -282,7 +282,7 @@ public class CompoundFormulaTest { assertThat(compoundFormula.isInstallerFormula()).isTrue(); } - /** Returns a builder with all fields filled with some dummy data. */ + /** Returns a builder with all fields filled with some placeholder data. */ private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { return new AppInstallMetadata.Builder() .setPackageName("abc") diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java index 693d4cae1f1c..70712e40fd5d 100644 --- a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java @@ -110,7 +110,7 @@ public class InstallerAllowedByManifestFormulaTest { assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); } - /** Returns a builder with all fields filled with some dummy data. */ + /** Returns a builder with all fields filled with some placeholder data. */ private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { return new AppInstallMetadata.Builder() .setPackageName("abc") diff --git a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java index f84355f6755e..ced1f4d28f74 100644 --- a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java +++ b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java @@ -316,7 +316,7 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP private void checkResultCounts(String queryString, int minRows, int maxRows, String matchDisplay1, String matchDisplay2) { - // get the cursor and apply sanity checks to result + // get the cursor and apply validity checks to result Cursor c = getQueryCursor(queryString); assertNotNull(c); assertTrue("Insufficient rows in filtered cursor", c.getCount() >= minRows); diff --git a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java index 81d54b57486c..bc0bddba2f20 100644 --- a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java +++ b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java @@ -17,6 +17,7 @@ package android.util.apk; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -198,6 +199,38 @@ public class SourceStampVerifierTest { assertNull(result.getCertificate()); } + @Test + public void testSourceStamp_validStampLineage() throws Exception { + mPrimaryApk = getApk("SourceStampVerifierTest/stamp-lineage-valid.apk"); + byte[] expectedStampCertHash = getSourceStampCertificateHashFromApk(mPrimaryApk); + + SourceStampVerificationResult result = + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); + + assertTrue(result.isPresent()); + assertTrue(result.isVerified()); + assertNotNull(result.getCertificate()); + byte[] actualStampCertHash = + MessageDigest.getInstance("SHA-256").digest(result.getCertificate().getEncoded()); + assertArrayEquals(expectedStampCertHash, actualStampCertHash); + assertEquals(2, result.getCertificateLineage().size()); + assertEquals(result.getCertificate(), + result.getCertificateLineage().get(result.getCertificateLineage().size() - 1)); + } + + @Test + public void testSourceStamp_invalidStampLineage() throws Exception { + mPrimaryApk = getApk("SourceStampVerifierTest/stamp-lineage-invalid.apk"); + + SourceStampVerificationResult result = + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); + + assertTrue(result.isPresent()); + assertFalse(result.isVerified()); + assertNull(result.getCertificate()); + assertTrue(result.getCertificateLineage().isEmpty()); + } + private File getApk(String apkPath) throws IOException { File apk = File.createTempFile("SourceStampApk", ".apk"); try (InputStream inputStream = mContext.getAssets().open(apkPath)) { diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java index b329e55b569f..88f2313cc727 100644 --- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java +++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java @@ -318,7 +318,7 @@ public class AutofillIdTest { try { // Write to parcel - parcel.setDataPosition(0); // Sanity / paranoid check + parcel.setDataPosition(0); // Validity Check id.writeToParcel(parcel, 0); // Read from parcel diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java index 2008537c040a..67614bb22e4e 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java @@ -317,7 +317,7 @@ public class ContentCaptureEventTest { try { // Write to parcel - parcel.setDataPosition(0); // Sanity / paranoid check + parcel.setDataPosition(0); // Validity Check event.writeToParcel(parcel, 0); // Read from parcel diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index 5ea071835de2..eb58c6300fca 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -66,7 +66,7 @@ public class ContentCaptureSessionTest { @Test public void testNewAutofillId_differentSessions() { - assertThat(mSession1.getId()).isNotEqualTo(mSession2.getId()); //sanity check + assertThat(mSession1.getId()).isNotEqualTo(mSession2.getId()); //validity check final AutofillId parentId = new AutofillId(42); final AutofillId childId1 = mSession1.newAutofillId(parentId, 108L); final AutofillId childId2 = mSession2.newAutofillId(parentId, 108L); @@ -84,7 +84,7 @@ public class ContentCaptureSessionTest { @Test public void testNewViewStructure() { - assertThat(mMockView.getAutofillId()).isNotNull(); // sanity check + assertThat(mMockView.getAutofillId()).isNotNull(); // validity check final ViewStructure structure = mSession1.newViewStructure(mMockView); assertThat(structure).isNotNull(); assertThat(structure.getAutofillId()).isEqualTo(mMockView.getAutofillId()); diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index bd2d4af54c83..6ac73b1972d4 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -223,7 +223,7 @@ key 200 MEDIA_PLAY key 201 MEDIA_PAUSE # key 202 "KEY_PROG3" # key 203 "KEY_PROG4" -# key 204 (undefined) +key 204 NOTIFICATION # key 205 "KEY_SUSPEND" # key 206 "KEY_CLOSE" key 207 MEDIA_PLAY diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index ca37917f437f..4cac7fbb023b 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -301,7 +301,7 @@ public final class ImageDecoder implements AutoCloseable { ImageDecoder decoder = null; try { - decoder = nCreate(fd, preferAnimation, source); + decoder = nCreate(fd, AssetFileDescriptor.UNKNOWN_LENGTH, preferAnimation, source); } finally { if (decoder == null) { IoUtils.closeQuietly(stream); @@ -349,7 +349,7 @@ public final class ImageDecoder implements AutoCloseable { try { try { Os.lseek(fd, offset, SEEK_SET); - decoder = nCreate(fd, preferAnimation, source); + decoder = nCreate(fd, assetFd.getDeclaredLength(), preferAnimation, source); } catch (ErrnoException e) { decoder = createFromStream(new FileInputStream(fd), true, preferAnimation, source); } @@ -2008,7 +2008,7 @@ public final class ImageDecoder implements AutoCloseable { private static native ImageDecoder nCreate(InputStream is, byte[] storage, boolean preferAnimation, Source src) throws IOException; // The fd must be seekable. - private static native ImageDecoder nCreate(FileDescriptor fd, + private static native ImageDecoder nCreate(FileDescriptor fd, long length, boolean preferAnimation, Source src) throws IOException; @NonNull private static native Bitmap nDecodeBitmap(long nativePtr, diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index 15d855e9560c..a7d3f7980d37 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -47,6 +47,7 @@ public class ImageFormat { DEPTH16, DEPTH_POINT_CLOUD, RAW_DEPTH, + RAW_DEPTH10, PRIVATE, HEIC }) @@ -725,6 +726,15 @@ public class ImageFormat { public static final int RAW_DEPTH = 0x1002; /** + * Unprocessed implementation-dependent raw + * depth measurements, opaque with 10 bit + * samples and device specific bit layout. + * + * @hide + */ + public static final int RAW_DEPTH10 = 0x1003; + + /** * Android private opaque image format. * <p> * The choices of the actual format and pixel data layout are entirely up to @@ -797,6 +807,7 @@ public class ImageFormat { case RAW_DEPTH: case RAW_SENSOR: return 16; + case RAW_DEPTH10: case RAW10: return 10; case RAW12: @@ -838,6 +849,7 @@ public class ImageFormat { case DEPTH_POINT_CLOUD: case PRIVATE: case RAW_DEPTH: + case RAW_DEPTH10: case Y8: case DEPTH_JPEG: case HEIC: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index ae0975467e3f..e3029e55a214 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -24,6 +24,8 @@ import com.android.internal.protolog.common.IProtoLogGroup; * This file is used by the ProtoLogTool to generate optimized logging code. */ public enum ShellProtoLogGroup implements IProtoLogGroup { + // NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict + // with those in the framework ProtoLogGroup WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java index 6a925e74e847..66ecf453c362 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java @@ -44,8 +44,6 @@ public class ShellProtoLogImpl extends BaseProtoLogImpl { private static ShellProtoLogImpl sServiceInstance = null; - private final PrintWriter mSystemOutWriter; - static { addLogGroupEnum(ShellProtoLogGroup.values()); } @@ -111,11 +109,11 @@ public class ShellProtoLogImpl extends BaseProtoLogImpl { return sServiceInstance; } - public void startTextLogging(Context context, String... groups) { + public int startTextLogging(Context context, String[] groups, PrintWriter pw) { try { mViewerConfig.loadViewerConfig( context.getResources().openRawResource(R.raw.wm_shell_protolog)); - setLogging(true /* setTextLogging */, true, mSystemOutWriter, groups); + return setLogging(true /* setTextLogging */, true, pw, groups); } catch (IOException e) { Log.i(TAG, "Unable to load log definitions: IOException while reading " + "wm_shell_protolog. " + e); @@ -123,16 +121,15 @@ public class ShellProtoLogImpl extends BaseProtoLogImpl { Log.i(TAG, "Unable to load log definitions: JSON parsing exception while reading " + "wm_shell_protolog. " + e); } + return -1; } - public void stopTextLogging(String... groups) { - setLogging(true /* setTextLogging */, false, mSystemOutWriter, groups); + public int stopTextLogging(String[] groups, PrintWriter pw) { + return setLogging(true /* setTextLogging */, false, pw, groups); } private ShellProtoLogImpl() { - super(new File(LOG_FILENAME), null, BUFFER_CAPACITY, - new ProtoLogViewerConfigReader()); - mSystemOutWriter = new PrintWriter(System.out, true); + super(new File(LOG_FILENAME), null, BUFFER_CAPACITY, new ProtoLogViewerConfigReader()); } } diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index 1f4fd230e55e..da91d46b0738 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -152,7 +152,7 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, } static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, - jobject fileDescriptor, jboolean preferAnimation, jobject source) { + jobject fileDescriptor, jlong length, jboolean preferAnimation, jobject source) { #ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC return throw_exception(env, kSourceException, "Only supported on Android", nullptr, source); #else @@ -172,7 +172,14 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, nullptr, source); } - std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); + std::unique_ptr<SkFILEStream> fileStream; + if (length == -1) { + // -1 corresponds to AssetFileDescriptor.UNKNOWN_LENGTH. Pass no length + // so SkFILEStream will figure out the size of the file on its own. + fileStream.reset(new SkFILEStream(file)); + } else { + fileStream.reset(new SkFILEStream(file, length)); + } return native_create(env, std::move(fileStream), source, preferAnimation); #endif } @@ -493,7 +500,7 @@ static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, { "nCreate", "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, - { "nCreate", "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, + { "nCreate", "(Ljava/io/FileDescriptor;JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 4dbce92ed01c..1333b92037c3 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -516,8 +516,9 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) flushInfo.fFinishedContext = destroyInfo; GrSemaphoresSubmitted submitted = bufferInfo->skSurface->flush( SkSurface::BackendSurfaceAccess::kPresent, flushInfo); - ALOGE_IF(!bufferInfo->skSurface->getContext(), "Surface is not backed by gpu"); - bufferInfo->skSurface->getContext()->submit(); + GrDirectContext* context = GrAsDirectContext(bufferInfo->skSurface->recordingContext()); + ALOGE_IF(!context, "Surface is not backed by gpu"); + context->submit(); if (submitted == GrSemaphoresSubmitted::kYes) { VkSemaphoreGetFdInfoKHR getFdInfo; getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 0e7eaa21888e..c1a98eabb2b7 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -46,13 +46,13 @@ import com.android.internal.location.ProviderProperties; */ interface ILocationManager { - Location getLastLocation(in LocationRequest request, String packageName, String attributionTag); - void getCurrentLocation(in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationCallback callback, String packageName, String attributionTag, String listenerId); + Location getLastLocation(String provider, String packageName, String attributionTag); + void getCurrentLocation(String provider, in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationCallback callback, String packageName, String attributionTag, String listenerId); - void registerLocationListener(in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId); + void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId); void unregisterLocationListener(in ILocationListener listener); - void registerLocationPendingIntent(in LocationRequest request, in PendingIntent intent, String packageName, String attributionTag); + void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent intent, String packageName, String attributionTag); void unregisterLocationPendingIntent(in PendingIntent intent); void requestGeofence(in Geofence geofence, in PendingIntent intent, String packageName, String attributionTag); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 04bcbfc5f6b8..d7ef24170c24 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -670,11 +670,8 @@ public class LocationManager { public Location getLastKnownLocation(@NonNull String provider) { Preconditions.checkArgument(provider != null, "invalid null provider"); - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - provider, 0, 0, true); - try { - return mService.getLastLocation(request, mContext.getPackageName(), + return mService.getLastLocation(provider, mContext.getPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -682,23 +679,11 @@ public class LocationManager { } /** - * Asynchronously returns a single current location fix. This may activate sensors in order to - * compute a new location, unlike {@link #getLastKnownLocation(String)}, which will only return - * a cached fix if available. The given callback will be invoked once and only once, either with - * a valid location fix or with a null location fix if the provider was unable to generate a - * valid location. - * - * <p>A client may supply an optional {@link CancellationSignal}. If this is used to cancel the - * operation, no callback should be expected after the cancellation. - * - * <p>This method may return locations from the very recent past (on the order of several - * seconds), but will never return older locations (for example, several minutes old or older). - * Clients may rely upon the guarantee that if this method returns a location, it will represent - * the best estimation of the location of the device in the present moment. + * Asynchronously returns a single current location fix from the given provider. * - * <p>Clients calling this method from the background may notice that the method fails to - * determine a valid location fix more often than while in the foreground. Background - * applications may be throttled in their location accesses to some degree. + * <p>See + * {@link #getCurrentLocation(String, LocationRequest, CancellationSignal, Executor, Consumer)} + * for more information. * * @param provider a provider listed by {@link #getAllProviders()} * @param cancellationSignal an optional signal that allows for cancelling this call @@ -714,16 +699,19 @@ public class LocationManager { public void getCurrentLocation(@NonNull String provider, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) { - getCurrentLocation(LocationRequest.createFromDeprecatedProvider(provider, 0, 0, true), + getCurrentLocation( + provider, + new LocationRequest.Builder(0).build(), cancellationSignal, executor, consumer); } /** - * Asynchronously returns a single current location fix based on the given - * {@link LocationRequest}. + * Asynchronously returns a single current location fix from the given provider based on the + * given {@link LocationRequest}. * - * <p>See {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} for more - * information. + * <p>See + * {@link #getCurrentLocation(String, LocationRequest, CancellationSignal, Executor, Consumer)} + * for more information. * * @param locationRequest the location request containing location parameters * @param cancellationSignal an optional signal that allows for cancelling this call @@ -735,13 +723,66 @@ public class LocationManager { * @throws IllegalArgumentException if consumer is null * @throws SecurityException if no suitable permission is present * @hide + * @deprecated Use + * {@link #getCurrentLocation(String, LocationRequest, CancellationSignal, Executor, Consumer)} + * instead. */ + @Deprecated @SystemApi @TestApi @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull LocationRequest locationRequest, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) { + Preconditions.checkArgument(locationRequest.getProvider() != null); + getCurrentLocation(locationRequest.getProvider(), locationRequest, cancellationSignal, + executor, consumer); + } + + /** + * Asynchronously returns a single current location fix from the given provider based on the + * given {@link LocationRequest}. This may activate sensors in order to compute a new location, + * unlike {@link #getLastKnownLocation(String)}, which will only return a cached fix if + * available. The given callback will be invoked once and only once, either with a valid + * location or with a null location if the provider was unable to generate a valid location. + * + * <p>A client may supply an optional {@link CancellationSignal}. If this is used to cancel the + * operation, no callback should be expected after the cancellation. + * + * <p>This method may return locations from the very recent past (on the order of several + * seconds), but will never return older locations (for example, several minutes old or older). + * Clients may rely upon the guarantee that if this method returns a location, it will represent + * the best estimation of the location of the device in the present moment. + * + * <p>Clients calling this method from the background may notice that the method fails to + * determine a valid location fix more often than while in the foreground. Background + * applications may be throttled in their location accesses to some degree. + * + * The given location request may be used to provide hints on how a fresh location is computed + * if necessary. In particular {@link LocationRequest#getDurationMillis()} can be used to + * provide maximum duration allowed before failing. The system will always cap the maximum + * amount of time a request for current location may run to some reasonable value (less than a + * minute for example) before the request is failed. + * + * @param provider a provider listed by {@link #getAllProviders()} + * @param locationRequest the location request containing location parameters + * @param cancellationSignal an optional signal that allows for cancelling this call + * @param executor the callback will take place on this {@link Executor} + * @param consumer the callback invoked with either a {@link Location} or null + * + * @throws IllegalArgumentException if provider is null or doesn't exist + * @throws IllegalArgumentException if executor is null + * @throws IllegalArgumentException if consumer is null + * @throws SecurityException if no suitable permission is present + */ + @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) + public void getCurrentLocation(@NonNull String provider, + @NonNull LocationRequest locationRequest, + @Nullable CancellationSignal cancellationSignal, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) { + Preconditions.checkArgument(provider != null, "invalid null provider"); + Preconditions.checkArgument(locationRequest != null, "invalid null location request"); + ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport(); GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor, consumer, remoteCancellationSignal); @@ -752,7 +793,7 @@ public class LocationManager { } try { - mService.getCurrentLocation(locationRequest, remoteCancellationSignal, + mService.getCurrentLocation(provider, locationRequest, remoteCancellationSignal, transport, mContext.getPackageName(), mContext.getAttributionTag(), AppOpsManager.toReceiverId(consumer)); } catch (RemoteException e) { @@ -782,12 +823,16 @@ public class LocationManager { public void requestSingleUpdate( @NonNull String provider, @NonNull LocationListener listener, @Nullable Looper looper) { Preconditions.checkArgument(provider != null, "invalid null provider"); - Preconditions.checkArgument(listener != null, "invalid null listener"); - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - provider, 0, 0, true); - request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS); - requestLocationUpdates(request, listener, looper); + Handler handler = looper == null ? new Handler() : new Handler(looper); + requestLocationUpdates( + provider, + new LocationRequest.Builder(0) + .setMaxUpdates(1) + .setDurationMillis(MAX_SINGLE_LOCATION_TIMEOUT_MS) + .build(), + new HandlerExecutor(handler), + listener); } /** @@ -814,12 +859,17 @@ public class LocationManager { @NonNull LocationListener listener, @Nullable Looper looper) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - Preconditions.checkArgument(listener != null, "invalid null listener"); - LocationRequest request = LocationRequest.createFromDeprecatedCriteria( - criteria, 0, 0, true); - request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS); - requestLocationUpdates(request, listener, looper); + Handler handler = looper == null ? new Handler() : new Handler(looper); + requestLocationUpdates( + FUSED_PROVIDER, + new LocationRequest.Builder(0) + .setQuality(criteria) + .setMaxUpdates(1) + .setDurationMillis(MAX_SINGLE_LOCATION_TIMEOUT_MS) + .build(), + new HandlerExecutor(handler), + listener); } /** @@ -843,10 +893,13 @@ public class LocationManager { @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(provider != null, "invalid null provider"); - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - provider, 0, 0, true); - request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS); - requestLocationUpdates(request, pendingIntent); + requestLocationUpdates( + provider, + new LocationRequest.Builder(0) + .setMaxUpdates(1) + .setDurationMillis(MAX_SINGLE_LOCATION_TIMEOUT_MS) + .build(), + pendingIntent); } /** @@ -871,61 +924,27 @@ public class LocationManager { @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - LocationRequest request = LocationRequest.createFromDeprecatedCriteria( - criteria, 0, 0, true); - request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS); - requestLocationUpdates(request, pendingIntent); + requestLocationUpdates( + FUSED_PROVIDER, + new LocationRequest.Builder(0) + .setQuality(criteria) + .setMaxUpdates(1) + .setDurationMillis(MAX_SINGLE_LOCATION_TIMEOUT_MS) + .build(), + pendingIntent); } /** - * Register for location updates from the given provider with the given arguments. {@link - * LocationListener} callbacks will take place on the given {@link Looper} or {@link Executor}. - * If a null {@link Looper} is supplied, the Looper of the calling thread will be used instead. - * Only one request can be registered for each unique listener, so any subsequent requests with - * the same listener will overwrite all associated arguments. + * Register for location updates from the given provider with the given arguments, and a + * callback on the {@link Looper} of the calling thread. * - * <p> It may take a while to receive the first location update. If an immediate location is - * required, applications may use the {@link #getLastKnownLocation(String)} method. - * - * <p> The location update interval can be controlled using the minimum time parameter. The - * elapsed time between location updates will never be less than this parameter, although it may - * be more depending on location availability and other factors. Choosing a sensible value for - * the minimum time parameter is important to conserve battery life. Every location update - * requires power from a variety of sensors. Select a minimum time parameter as high as possible - * while still providing a reasonable user experience. If your application is not in the - * foreground and showing location to the user then your application should consider switching - * to the {@link #PASSIVE_PROVIDER} instead. - * - * <p> The minimum distance parameter can also be used to control the frequency of location - * updates. If it is greater than 0 then the location provider will only send your application - * an update when the location has changed by at least minDistance meters, AND when the minimum - * time has elapsed. However it is more difficult for location providers to save power using the - * minimum distance parameter, so the minimum time parameter should be the primary tool for - * conserving battery life. - * - * <p> If your application wants to passively observe location updates triggered by other - * applications, but not consume any additional power otherwise, then use the {@link - * #PASSIVE_PROVIDER}. This provider does not turn on or modify active location providers, so - * you do not need to be as careful about minimum time and minimum distance parameters. However, - * if your application performs heavy work on a location update (such as network activity) then - * you should select non-zero values for the parameters to rate-limit your update frequency in - * the case another application enables a location provider with extremely fast updates. - * - * <p>In case the provider you have selected is disabled, location updates will cease, and a - * provider availability update will be sent. As soon as the provider is enabled again, another - * provider availability update will be sent and location updates will immediately resume. - * - * <p> When location callbacks are invoked, the system will hold a wakelock on your - * application's behalf for some period of time, but not indefinitely. If your application - * requires a long running wakelock within the location callback, you should acquire it - * yourself. + * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} + * for more detail on how this method works. * * <p class="note"> Prior to Jellybean, the minTime parameter was only a hint, and some location * provider implementations ignored it. For Jellybean and onwards however, it is mandatory for * Android compatible devices to observe both the minTime and minDistance parameters. * - * <p>To unregister for location updates, use {@link #removeUpdates(LocationListener)}. - * * @param provider a provider listed by {@link #getAllProviders()} * @param minTimeMs minimum time interval between location updates in milliseconds * @param minDistanceM minimum distance between location updates in meters @@ -939,21 +958,20 @@ public class LocationManager { @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM, @NonNull LocationListener listener) { - Preconditions.checkArgument(provider != null, "invalid null provider"); - Preconditions.checkArgument(listener != null, "invalid null listener"); - - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - provider, minTimeMs, minDistanceM, false); - requestLocationUpdates(request, listener, null); + requestLocationUpdates(provider, minTimeMs, minDistanceM, listener, null); } /** - * Register for location updates using the named provider, and a callback on - * the specified {@link Looper}. + * Register for location updates from the given provider with the given arguments, and a + * callback on the specified {@link Looper}. * - * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)} + * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} * for more detail on how this method works. * + * <p class="note">Prior to Jellybean, the minTime parameter was only a hint, and some location + * provider implementations ignored it. For Jellybean and onwards however, it is mandatory for + * Android compatible devices to observe both the minTime and minDistance parameters. + * * @param provider a provider listed by {@link #getAllProviders()} * @param minTimeMs minimum time interval between location updates in milliseconds * @param minDistanceM minimum distance between location updates in meters @@ -968,21 +986,22 @@ public class LocationManager { @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM, @NonNull LocationListener listener, @Nullable Looper looper) { - Preconditions.checkArgument(provider != null, "invalid null provider"); - Preconditions.checkArgument(listener != null, "invalid null listener"); - - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - provider, minTimeMs, minDistanceM, false); - requestLocationUpdates(request, listener, looper); + Handler handler = looper == null ? new Handler() : new Handler(looper); + requestLocationUpdates(provider, minTimeMs, minDistanceM, new HandlerExecutor(handler), + listener); } /** * Register for location updates using the named provider, and a callback on * the specified {@link Executor}. * - * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)} + * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} * for more detail on how this method works. * + * <p class="note">Prior to Jellybean, the minTime parameter was only a hint, and some location + * provider implementations ignored it. For Jellybean and onwards however, it is mandatory for + * Android compatible devices to observe both the minTime and minDistance parameters. + * * @param provider a provider listed by {@link #getAllProviders()} * @param minTimeMs minimum time interval between location updates in milliseconds * @param minDistanceM minimum distance between location updates in meters @@ -1001,16 +1020,22 @@ public class LocationManager { float minDistanceM, @NonNull @CallbackExecutor Executor executor, @NonNull LocationListener listener) { - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - provider, minTimeMs, minDistanceM, false); - requestLocationUpdates(request, executor, listener); + Preconditions.checkArgument(provider != null, "invalid null provider"); + + requestLocationUpdates( + provider, + new LocationRequest.Builder(minTimeMs) + .setMinUpdateDistanceMeters(minDistanceM) + .build(), + executor, + listener); } /** * Register for location updates using a provider selected through the given Criteria, and a * callback on the specified {@link Looper}. * - * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)} + * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} * for more detail on how this method works. * * @param minTimeMs minimum time interval between location updates in milliseconds @@ -1026,19 +1051,16 @@ public class LocationManager { public void requestLocationUpdates(long minTimeMs, float minDistanceM, @NonNull Criteria criteria, @NonNull LocationListener listener, @Nullable Looper looper) { - Preconditions.checkArgument(criteria != null, "invalid null criteria"); - Preconditions.checkArgument(listener != null, "invalid null listener"); - - LocationRequest request = LocationRequest.createFromDeprecatedCriteria( - criteria, minTimeMs, minDistanceM, false); - requestLocationUpdates(request, listener, looper); + Handler handler = looper == null ? new Handler() : new Handler(looper); + requestLocationUpdates(minTimeMs, minDistanceM, criteria, new HandlerExecutor(handler), + listener); } /** * Register for location updates using a provider selected through the given Criteria, and a * callback on the specified {@link Executor}. * - * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)} + * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} * for more detail on how this method works. * * @param minTimeMs minimum time interval between location updates in milliseconds @@ -1059,23 +1081,24 @@ public class LocationManager { @NonNull Criteria criteria, @NonNull @CallbackExecutor Executor executor, @NonNull LocationListener listener) { - LocationRequest request = LocationRequest.createFromDeprecatedCriteria( - criteria, minTimeMs, minDistanceM, false); - requestLocationUpdates(request, executor, listener); + Preconditions.checkArgument(criteria != null, "invalid null criteria"); + + requestLocationUpdates( + FUSED_PROVIDER, + new LocationRequest.Builder(minTimeMs) + .setQuality(criteria) + .setMinUpdateDistanceMeters(minDistanceM) + .build(), + executor, + listener); } /** * Register for location updates using the named provider, and callbacks delivered via the * provided {@link PendingIntent}. * - * <p>The delivered pending intents will contain extras with the callback information. The keys - * used for the extras are {@link #KEY_LOCATION_CHANGED} and {@link #KEY_PROVIDER_ENABLED}. See - * the documentation for each respective extra key for information on the values. - * - * <p>To unregister for location updates, use {@link #removeUpdates(PendingIntent)}. - * - * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)} - * for more detail on how this method works. + * <p>See {@link #requestLocationUpdates(String, LocationRequest, PendingIntent)} for more + * detail on how this method works. * * @param provider a provider listed by {@link #getAllProviders()} * @param minTimeMs minimum time interval between location updates in milliseconds @@ -1091,9 +1114,12 @@ public class LocationManager { @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(provider != null, "invalid null provider"); - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - provider, minTimeMs, minDistanceM, false); - requestLocationUpdates(request, pendingIntent); + requestLocationUpdates( + provider, + new LocationRequest.Builder(minTimeMs) + .setMinUpdateDistanceMeters(minDistanceM) + .build(), + pendingIntent); } /** @@ -1116,10 +1142,13 @@ public class LocationManager { public void requestLocationUpdates(long minTimeMs, float minDistanceM, @NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - - LocationRequest request = LocationRequest.createFromDeprecatedCriteria( - criteria, minTimeMs, minDistanceM, false); - requestLocationUpdates(request, pendingIntent); + requestLocationUpdates( + FUSED_PROVIDER, + new LocationRequest.Builder(minTimeMs) + .setQuality(criteria) + .setMinUpdateDistanceMeters(minDistanceM) + .build(), + pendingIntent); } /** @@ -1131,7 +1160,7 @@ public class LocationManager { * choose default low power parameters for location updates, but this is heavily discouraged, * and an explicit LocationRequest should always be provided. * - * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)} + * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} * for more detail on how this method works. * * @param locationRequest the location request containing location parameters @@ -1143,7 +1172,10 @@ public class LocationManager { * @throws SecurityException if no suitable permission is present * * @hide + * @deprecated Use + * {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} instead. */ + @Deprecated @SystemApi @TestApi @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) @@ -1159,8 +1191,8 @@ public class LocationManager { * Register for location updates using a {@link LocationRequest}, and a callback on the * specified {@link Executor}. * - * <p>See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} for more - * detail on how this method works. + * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} + * for more detail on how this method works. * * @param locationRequest the location request containing location parameters * @param executor the executor handling listener callbacks @@ -1171,7 +1203,10 @@ public class LocationManager { * @throws SecurityException if no suitable permission is present * * @hide + * @deprecated Use + * {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} instead. */ + @Deprecated @SystemApi @TestApi @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) @@ -1180,8 +1215,93 @@ public class LocationManager { @NonNull @CallbackExecutor Executor executor, @NonNull LocationListener listener) { if (locationRequest == null) { - locationRequest = new LocationRequest(); + locationRequest = LocationRequest.create(); } + Preconditions.checkArgument(locationRequest.getProvider() != null); + requestLocationUpdates(locationRequest.getProvider(), locationRequest, executor, listener); + } + + /** + * Register for location updates using a {@link LocationRequest}, and callbacks delivered via + * the provided {@link PendingIntent}. + * + * <p>See {@link #requestLocationUpdates(String, LocationRequest, PendingIntent)} for more + * detail on how this method works. + * + * @param locationRequest the location request containing location parameters + * @param pendingIntent the pending intent to send location updates + * + * @throws IllegalArgumentException if pendingIntent is null + * @throws SecurityException if no suitable permission is present + * + * @hide + * @deprecated Use {@link #requestLocationUpdates(String, LocationRequest, PendingIntent)} + * instead. + */ + @Deprecated + @SystemApi + @TestApi + @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) + public void requestLocationUpdates( + @Nullable LocationRequest locationRequest, + @NonNull PendingIntent pendingIntent) { + if (locationRequest == null) { + locationRequest = LocationRequest.create(); + } + Preconditions.checkArgument(locationRequest.getProvider() != null); + requestLocationUpdates(locationRequest.getProvider(), locationRequest, pendingIntent); + } + + /** + * Register for location updates from the specified provider, using a {@link LocationRequest}, + * and a callback on the specified {@link Executor}. + * + * <p>Only one request can be registered for each unique listener/provider pair, so any + * subsequent requests with the same provider and listener will overwrite all associated + * arguments. The same listener may be used across multiple providers with different requests + * for each provider. + * + * <p>It may take a while to receive the first location update. If an immediate location is + * required, applications may use the {@link #getLastKnownLocation(String)} method. + * + * <p>See {@link LocationRequest} documentation for an explanation of various request parameters + * and how they can affect the received locations. + * + * <p> If your application wants to passively observe location updates from any provider, then + * use the {@link #PASSIVE_PROVIDER}. This provider does not turn on or modify active location + * providers, so you do not need to be as careful about minimum time and minimum distance + * parameters. However, if your application performs heavy work on a location update (such as + * network activity) then you should set an explicit fastest interval on your location request + * in case another application enables a location provider with extremely fast updates. + * + * <p>In case the provider you have selected is disabled, location updates will cease, and a + * provider availability update will be sent. As soon as the provider is enabled again, another + * provider availability update will be sent and location updates will immediately resume. + * + * <p> When location callbacks are invoked, the system will hold a wakelock on your + * application's behalf for some period of time, but not indefinitely. If your application + * requires a long running wakelock within the location callback, you should acquire it + * yourself. + * + * <p>To unregister for location updates, use {@link #removeUpdates(LocationListener)}. + * + * @param provider a provider listed by {@link #getAllProviders()} + * @param locationRequest the location request containing location parameters + * @param executor the executor handling listener callbacks + * @param listener the listener to receive location updates + * + * @throws IllegalArgumentException if provider is null or doesn't exist + * @throws IllegalArgumentException if locationRequest is null + * @throws IllegalArgumentException if listener is null + * @throws SecurityException if no suitable permission is present + */ + @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) + public void requestLocationUpdates(@NonNull String provider, + @NonNull LocationRequest locationRequest, + @NonNull @CallbackExecutor Executor executor, + @NonNull LocationListener listener) { + Preconditions.checkArgument(provider != null, "invalid null provider"); + Preconditions.checkArgument(locationRequest != null, "invalid null location request"); synchronized (sLocationListeners) { WeakReference<LocationListenerTransport> reference = sLocationListeners.get(listener); @@ -1198,7 +1318,7 @@ public class LocationManager { // make sure that callbacks are not made on the same thread - however it is the // easiest way to guarantee that clients will not receive callbacks after // unregistration is complete. - mService.registerLocationListener(locationRequest, transport, + mService.registerLocationListener(provider, locationRequest, transport, mContext.getPackageName(), mContext.getAttributionTag(), AppOpsManager.toReceiverId(listener)); } catch (RemoteException e) { @@ -1208,39 +1328,39 @@ public class LocationManager { } /** - * Register for location updates using a {@link LocationRequest}, and callbacks delivered via - * the provided {@link PendingIntent}. + * Register for location updates from the specified provider, using a {@link LocationRequest}, + * and callbacks delivered via the provided {@link PendingIntent}. * - * <p>See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} and - * {@link #requestLocationUpdates(String, long, float, PendingIntent)} for more detail on how - * this method works. + * <p>The delivered pending intents will contain extras with the callback information. The keys + * used for the extras are {@link #KEY_LOCATION_CHANGED} and {@link #KEY_PROVIDER_ENABLED}. See + * the documentation for each respective extra key for information on the values. + * + * <p>To unregister for location updates, use {@link #removeUpdates(PendingIntent)}. * + * @param provider a provider listed by {@link #getAllProviders()} * @param locationRequest the location request containing location parameters * @param pendingIntent the pending intent to send location updates * + * @throws IllegalArgumentException if provider is null or doesn't exist + * @throws IllegalArgumentException if locationRequest is null * @throws IllegalArgumentException if pendingIntent is null * @throws SecurityException if no suitable permission is present - * - * @hide */ - @SystemApi - @TestApi @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) - public void requestLocationUpdates( - @Nullable LocationRequest locationRequest, + public void requestLocationUpdates(@NonNull String provider, + @NonNull LocationRequest locationRequest, @NonNull PendingIntent pendingIntent) { + Preconditions.checkArgument(provider != null, "invalid null provider"); + Preconditions.checkArgument(locationRequest != null, "invalid null location request"); Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { Preconditions.checkArgument(pendingIntent.isTargetedToPackage(), "pending intent must be targeted to a package"); } - if (locationRequest == null) { - locationRequest = new LocationRequest(); - } - try { - mService.registerLocationPendingIntent(locationRequest, pendingIntent, + mService.registerLocationPendingIntent(provider, locationRequest, pendingIntent, mContext.getPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 280bd058ef0f..c53d08bdcd16 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -16,7 +16,12 @@ package android.location; +import static java.lang.Math.max; +import static java.lang.Math.min; + import android.Manifest; +import android.annotation.FloatRange; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -26,7 +31,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; -import android.os.SystemClock; import android.os.WorkSource; import android.util.TimeUtils; @@ -36,73 +40,25 @@ import java.util.Objects; /** - * A data object that contains quality of service parameters for requests - * to the {@link LocationManager}. - * - * <p>LocationRequest objects are used to request a quality of service - * for location updates from the Location Manager. - * - * <p>For example, if your application wants high accuracy location - * it should create a location request with {@link #setQuality} set to - * {@link #ACCURACY_FINE} or {@link #POWER_HIGH}, and it should set - * {@link #setInterval} to less than one second. This would be - * appropriate for mapping applications that are showing your location - * in real-time. - * - * <p>At the other extreme, if you want negligible power - * impact, but to still receive location updates when available, then use - * {@link #setQuality} with {@link #POWER_NONE}. With this request your - * application will not trigger (and therefore will not receive any - * power blame) any location updates, but will receive locations - * triggered by other applications. This would be appropriate for - * applications that have no firm requirement for location, but can - * take advantage when available. - * - * <p>In between these two extremes is a very common use-case, where - * applications definitely want to receive - * updates at a specified interval, and can receive them faster when - * available, but still want a low power impact. These applications - * should consider {@link #POWER_LOW} combined with a faster - * {@link #setFastestInterval} (such as 1 minute) and a slower - * {@link #setInterval} (such as 60 minutes). They will only be assigned - * power blame for the interval set by {@link #setInterval}, but can - * still receive locations triggered by other applications at a rate up - * to {@link #setFastestInterval}. This style of request is appropriate for - * many location aware applications, including background usage. Do be - * careful to also throttle {@link #setFastestInterval} if you perform - * heavy-weight work after receiving an update - such as using the network. - * - * <p>Activities should strongly consider removing all location - * request when entering the background, or - * at least swap the request to a larger interval and lower quality. - * Future version of the location manager may automatically perform background - * throttling on behalf of applications. - * - * <p>Applications cannot specify the exact location sources that are - * used by Android's <em>Fusion Engine</em>. In fact, the system - * may have multiple location sources (providers) running and may - * fuse the results from several sources into a single Location object. - * - * <p>Location requests from applications with - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and not - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} will - * be automatically throttled to a slower interval, and the location - * object will be obfuscated to only show a coarse level of accuracy. - * - * <p>All location requests are considered hints, and you may receive - * locations that are more accurate, less accurate, and slower - * than requested. - * - * @hide + * An encapsulation of various parameters for requesting location via {@link LocationManager}. */ -@SystemApi -@TestApi public final class LocationRequest implements Parcelable { + + /** + * Represents a passive only request. Such a request will not trigger any active locations or + * power usage itself, but may receive locations generated in response to other requests. + */ + public static final long PASSIVE_INTERVAL = Long.MAX_VALUE; + /** * Used with {@link #setQuality} to request the most accurate locations available. * * <p>This may be up to 1 meter accuracy, although this is implementation dependent. + * + * @hide */ + @SystemApi + @TestApi public static final int ACCURACY_FINE = 100; /** @@ -111,7 +67,11 @@ public final class LocationRequest implements Parcelable { * <p>Block level accuracy is considered to be about 100 meter accuracy, * although this is implementation dependent. Using a coarse accuracy * such as this often consumes less power. + * + * @hide */ + @SystemApi + @TestApi public static final int ACCURACY_BLOCK = 102; /** @@ -120,7 +80,11 @@ public final class LocationRequest implements Parcelable { * <p>City level accuracy is considered to be about 10km accuracy, * although this is implementation dependent. Using a coarse accuracy * such as this often consumes less power. + * + * @hide */ + @SystemApi + @TestApi public static final int ACCURACY_CITY = 104; /** @@ -129,7 +93,12 @@ public final class LocationRequest implements Parcelable { * <p>This location request will not trigger any active location requests, * but will receive locations triggered by other applications. Your application * will not receive any direct power blame for location work. + * + * @hide + * @deprecated Use {@link #PASSIVE_INTERVAL} instead. */ + @SystemApi + @Deprecated public static final int POWER_NONE = 200; /** @@ -137,66 +106,76 @@ public final class LocationRequest implements Parcelable { * * <p>This location request will avoid high power location work where * possible. + * + * @hide */ + @SystemApi + @TestApi public static final int POWER_LOW = 201; /** * Used with {@link #setQuality} to allow high power consumption for location. * * <p>This location request will allow high power location work. + * + * @hide */ + @SystemApi + @TestApi public static final int POWER_HIGH = 203; - private static final long DEFAULT_INTERVAL_MS = 60 * 60 * 1000; // 1 hour - private static final double FASTEST_INTERVAL_FACTOR = 6.0; // 6x + private static final long IMPLICIT_MIN_UPDATE_INTERVAL = -1; - @UnsupportedAppUsage - private String mProvider; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link " + + "LocationManager} methods to provide the provider explicitly.") + @Nullable private String mProvider; private int mQuality; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link " + + "LocationRequest} instead.") private long mInterval; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private long mFastestInterval; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private boolean mExplicitFastestInterval; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private long mExpireAt; - private long mExpireIn; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private int mNumUpdates; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private float mSmallestDisplacement; - @UnsupportedAppUsage + private long mMinUpdateIntervalMillis; + private long mExpireAtRealtimeMillis; + private long mDurationMillis; + private int mMaxUpdates; + private float mMinUpdateDistanceMeters; private boolean mHideFromAppOps; private boolean mLocationSettingsIgnored; - private boolean mLowPowerMode; - @UnsupportedAppUsage + private boolean mLowPower; private @Nullable WorkSource mWorkSource; /** - * Create a location request with default parameters. - * - * <p>Default parameters are for a low power, slowly updated location. - * It can then be adjusted as required by the applications before passing - * to the {@link LocationManager} - * - * @return a new location request + * @hide + * @deprecated Use the Builder to construct new LocationRequests. */ + @SystemApi + @Deprecated @NonNull public static LocationRequest create() { - return new LocationRequest(); + // 60 minutes is the default legacy interval + return new LocationRequest.Builder(60 * 60 * 1000) + .setQuality(POWER_LOW) + .build(); } - /** @hide */ + /** + * @hide + * @deprecated Use the Builder to construct new LocationRequests. + */ @SystemApi + @Deprecated @NonNull - public static LocationRequest createFromDeprecatedProvider( - @NonNull String provider, long minTime, float minDistance, boolean singleShot) { + public static LocationRequest createFromDeprecatedProvider(@NonNull String provider, + long intervalMillis, float minUpdateDistanceMeters, boolean singleShot) { Preconditions.checkArgument(provider != null, "invalid null provider"); - if (minTime < 0) minTime = 0; - if (minDistance < 0) minDistance = 0; + if (intervalMillis < 0) { + intervalMillis = 0; + } else if (intervalMillis == PASSIVE_INTERVAL) { + intervalMillis = Long.MAX_VALUE - 1; + } + if (minUpdateDistanceMeters < 0) { + minUpdateDistanceMeters = 0; + } int quality; if (LocationManager.PASSIVE_PROVIDER.equals(provider)) { @@ -207,512 +186,484 @@ public final class LocationRequest implements Parcelable { quality = POWER_LOW; } - LocationRequest request = new LocationRequest() + return new LocationRequest.Builder(intervalMillis) + .setMinUpdateIntervalMillis(intervalMillis) + .setMinUpdateDistanceMeters(minUpdateDistanceMeters) + .setMaxUpdates(singleShot ? 1 : Integer.MAX_VALUE) + .build() .setProvider(provider) - .setQuality(quality) - .setInterval(minTime) - .setFastestInterval(minTime) - .setSmallestDisplacement(minDistance); - if (singleShot) request.setNumUpdates(1); - return request; + .setQuality(quality); } - /** @hide */ + /** + * @hide + * @deprecated Use the Builder to construct new LocationRequests. + */ @SystemApi + @Deprecated @NonNull - public static LocationRequest createFromDeprecatedCriteria( - @NonNull Criteria criteria, long minTime, float minDistance, boolean singleShot) { + public static LocationRequest createFromDeprecatedCriteria(@NonNull Criteria criteria, + long intervalMillis, float minUpdateDistanceMeters, boolean singleShot) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - if (minTime < 0) minTime = 0; - if (minDistance < 0) minDistance = 0; - - int quality; - switch (criteria.getAccuracy()) { - case Criteria.ACCURACY_COARSE: - quality = ACCURACY_BLOCK; - break; - case Criteria.ACCURACY_FINE: - quality = ACCURACY_FINE; - break; - default: { - if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) { - quality = POWER_HIGH; - } else { - quality = POWER_LOW; - } - } + if (intervalMillis < 0) { + intervalMillis = 0; + } else if (intervalMillis == PASSIVE_INTERVAL) { + intervalMillis = Long.MAX_VALUE - 1; + } + if (minUpdateDistanceMeters < 0) { + minUpdateDistanceMeters = 0; } - LocationRequest request = new LocationRequest() - .setQuality(quality) - .setInterval(minTime) - .setFastestInterval(minTime) - .setSmallestDisplacement(minDistance); - if (singleShot) request.setNumUpdates(1); - return request; - } - - /** @hide */ - public LocationRequest() { - this( - /* provider= */ LocationManager.FUSED_PROVIDER, - /* quality= */ POWER_LOW, - /* interval= */ DEFAULT_INTERVAL_MS, - /* fastestInterval= */ (long) (DEFAULT_INTERVAL_MS / FASTEST_INTERVAL_FACTOR), - /* explicitFastestInterval= */ false, - /* expireAt= */ Long.MAX_VALUE, - /* expireIn= */ Long.MAX_VALUE, - /* numUpdates= */ Integer.MAX_VALUE, - /* smallestDisplacement= */ 0, - /* hideFromAppOps= */ false, - /* locationSettingsIgnored= */ false, - /* lowPowerMode= */ false, - /* workSource= */ null); - } - - /** @hide */ - public LocationRequest(LocationRequest src) { - this( - src.mProvider, - src.mQuality, - src.mInterval, - src.mFastestInterval, - src.mExplicitFastestInterval, - src.mExpireAt, - src.mExpireIn, - src.mNumUpdates, - src.mSmallestDisplacement, - src.mHideFromAppOps, - src.mLocationSettingsIgnored, - src.mLowPowerMode, - src.mWorkSource); + return new LocationRequest.Builder(intervalMillis) + .setQuality(criteria) + .setMinUpdateIntervalMillis(intervalMillis) + .setMinUpdateDistanceMeters(minUpdateDistanceMeters) + .setMaxUpdates(singleShot ? 1 : Integer.MAX_VALUE) + .build(); } private LocationRequest( - @NonNull String provider, + @Nullable String provider, + long intervalMillis, int quality, - long intervalMs, - long fastestIntervalMs, - boolean explicitFastestInterval, - long expireAt, - long expireInMs, - int numUpdates, - float smallestDisplacementM, - boolean hideFromAppOps, + long expireAtRealtimeMillis, + long durationMillis, + int maxUpdates, + long minUpdateIntervalMillis, + float minUpdateDistanceMeters, + boolean hiddenFromAppOps, boolean locationSettingsIgnored, - boolean lowPowerMode, - WorkSource workSource) { - Preconditions.checkArgument(provider != null, "invalid provider: null"); - checkQuality(quality); + boolean lowPower, + @Nullable WorkSource workSource) { + Preconditions.checkArgument(intervalMillis != PASSIVE_INTERVAL || quality == POWER_NONE); + Preconditions.checkArgument(minUpdateIntervalMillis <= intervalMillis); mProvider = provider; + mInterval = intervalMillis; mQuality = quality; - mInterval = intervalMs; - mFastestInterval = fastestIntervalMs; - mExplicitFastestInterval = explicitFastestInterval; - mExpireAt = expireAt; - mExpireIn = expireInMs; - mNumUpdates = numUpdates; - mSmallestDisplacement = Preconditions.checkArgumentInRange(smallestDisplacementM, 0, - Float.MAX_VALUE, "smallestDisplacementM"); - mHideFromAppOps = hideFromAppOps; - mLowPowerMode = lowPowerMode; + mMinUpdateIntervalMillis = minUpdateIntervalMillis; + mExpireAtRealtimeMillis = expireAtRealtimeMillis; + mDurationMillis = durationMillis; + mMaxUpdates = maxUpdates; + mMinUpdateDistanceMeters = minUpdateDistanceMeters; + mHideFromAppOps = hiddenFromAppOps; + mLowPower = lowPower; mLocationSettingsIgnored = locationSettingsIgnored; mWorkSource = workSource; } /** - * Set the quality of the request. - * - * <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power - * constant such as {@link #POWER_LOW}. You cannot request both accuracy and - * power, only one or the other can be specified. The system will then - * maximize accuracy or minimize power as appropriate. - * - * <p>The quality of the request is a strong hint to the system for which - * location sources to use. For example, {@link #ACCURACY_FINE} is more likely - * to use GPS, and {@link #POWER_LOW} is more likely to use WIFI & Cell tower - * positioning, but it also depends on many other factors (such as which sources - * are available) and is implementation dependent. - * - * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters - * on a location request. - * - * @param quality an accuracy or power constant - * @return the same object, so that setters can be chained - * @throws IllegalArgumentException if the quality constant is not valid + * @hide + * @deprecated LocationRequests should be treated as immutable. */ - public @NonNull LocationRequest setQuality(int quality) { - checkQuality(quality); - mQuality = quality; + @SystemApi + @Deprecated + public @NonNull LocationRequest setProvider(@NonNull String provider) { + Preconditions.checkArgument(provider != null); + mProvider = provider; return this; } /** - * Get the quality of the request. - * - * @return an accuracy or power constant + * @hide + * @deprecated Providers are no longer an explicit part of a location request. */ - public int getQuality() { - return mQuality; + @SystemApi + @Deprecated + public @NonNull String getProvider() { + return mProvider != null ? mProvider : LocationManager.FUSED_PROVIDER; } /** - * Set the desired interval for active location updates, in milliseconds. - * - * <p>The location manager will actively try to obtain location updates - * for your application at this interval, so it has a - * direct influence on the amount of power used by your application. - * Choose your interval wisely. - * - * <p>This interval is inexact. You may not receive updates at all (if - * no location sources are available), or you may receive them - * slower than requested. You may also receive them faster than - * requested (if other applications are requesting location at a - * faster interval). The fastest rate that you will receive - * updates can be controlled with {@link #setFastestInterval}. - * - * <p>Applications with only the coarse location permission may have their - * interval silently throttled. - * - * <p>An interval of 0 is allowed, but not recommended, since - * location updates may be extremely fast on future implementations. - * - * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters - * on a location request. - * - * @param millis desired interval in millisecond, inexact - * @return the same object, so that setters can be chained - * @throws IllegalArgumentException if the interval is less than zero + * @hide + * @deprecated LocationRequests should be treated as immutable. */ - public @NonNull LocationRequest setInterval(long millis) { - Preconditions.checkArgument(millis >= 0, "invalid interval: + millis"); - mInterval = millis; - if (!mExplicitFastestInterval) { - mFastestInterval = (long) (mInterval / FASTEST_INTERVAL_FACTOR); - } + @SystemApi + @Deprecated + public @NonNull LocationRequest setQuality(int quality) { + mQuality = Builder.checkQuality(quality, true); return this; } /** - * Get the desired interval of this request, in milliseconds. + * Returns the quality of the location request. + * + * @return the quality of the location request * - * @return desired interval in milliseconds, inexact + * @hide */ - public long getInterval() { - return mInterval; + @SystemApi + public int getQuality() { + if (mInterval == PASSIVE_INTERVAL) { + return POWER_NONE; + } else { + return mQuality; + } } - /** - * Requests the GNSS chipset to run in a low power mode and make strong tradeoffs to - * substantially restrict power. - * - * <p>In this mode, the GNSS chipset will not, on average, run power hungry operations like RF & - * signal searches for more than one second per interval (specified by - * {@link #setInterval(long)}). - * - * @param enabled Enable or disable low power mode - * @return the same object, so that setters can be chained + * @hide + * @deprecated LocationRequests should be treated as immutable. */ - public @NonNull LocationRequest setLowPowerMode(boolean enabled) { - mLowPowerMode = enabled; + @SystemApi + @Deprecated + public @NonNull LocationRequest setInterval(long millis) { + Preconditions.checkArgument(millis >= 0); + + // legacy clients don't know about the passive interval + if (millis == PASSIVE_INTERVAL) { + millis = Long.MAX_VALUE - 1; + } + + mInterval = millis; + if (mMinUpdateIntervalMillis > mInterval) { + mMinUpdateIntervalMillis = mInterval; + } return this; } /** - * Returns true if low power mode is enabled. + * @hide + * @deprecated Use {@link #getIntervalMillis()} instead. */ - public boolean isLowPowerMode() { - return mLowPowerMode; + @SystemApi + @Deprecated + public long getInterval() { + return getIntervalMillis(); } /** - * Requests that user location settings be ignored in order to satisfy this request. This API - * is only for use in extremely rare scenarios where it is appropriate to ignore user location - * settings, such as a user initiated emergency (dialing 911 for instance). + * Returns the desired interval of location updates, or {@link #PASSIVE_INTERVAL} if this is a + * passive, no power request. A passive request will not actively generate location updates + * (and thus will not be power blamed for location), but may receive location updates generated + * as a result of other location requests. A passive request must always have an explicit + * minimum update interval set. * - * @param locationSettingsIgnored Whether to ignore location settings - * @return the same object, so that setters can be chained - */ - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - public @NonNull LocationRequest setLocationSettingsIgnored(boolean locationSettingsIgnored) { - mLocationSettingsIgnored = locationSettingsIgnored; - return this; - } - - /** - * Returns true if location settings will be ignored in order to satisfy this request. + * <p>Locations may be available at a faster interval than specified here, see + * {@link #getMinUpdateIntervalMillis()} for the behavior in that case. + * + * @return the desired interval of location updates */ - public boolean isLocationSettingsIgnored() { - return mLocationSettingsIgnored; + public long getIntervalMillis() { + return mInterval; } /** - * Explicitly set the fastest interval for location updates, in - * milliseconds. - * - * <p>This controls the fastest rate at which your application will - * receive location updates, which might be faster than - * {@link #setInterval} in some situations (for example, if other - * applications are triggering location updates). - * - * <p>This allows your application to passively acquire locations - * at a rate faster than it actively acquires locations, saving power. - * - * <p>Unlike {@link #setInterval}, this parameter is exact. Your - * application will never receive updates faster than this value. - * - * <p>If you don't call this method, a fastest interval - * will be selected for you. It will be a value faster than your - * active interval ({@link #setInterval}). - * - * <p>An interval of 0 is allowed, but not recommended, since - * location updates may be extremely fast on future implementations. - * - * <p>If the fastest interval set is slower than {@link #setInterval}, - * then your effective fastest interval is {@link #setInterval}. - * - * @param millis fastest interval for updates in milliseconds - * @return the same object, so that setters can be chained - * @throws IllegalArgumentException if the interval is less than zero + * @hide + * @deprecated LocationRequests should be treated as immutable. */ + @SystemApi + @Deprecated public @NonNull LocationRequest setFastestInterval(long millis) { - Preconditions.checkArgument(millis >= 0, "invalid interval: + millis"); - mExplicitFastestInterval = true; - mFastestInterval = millis; + Preconditions.checkArgument(millis >= 0); + mMinUpdateIntervalMillis = millis; return this; } /** - * Get the fastest interval of this request in milliseconds. The system will never provide - * location updates faster than the minimum of the fastest interval and {@link #getInterval}. - * - * @return fastest interval in milliseconds + * @hide + * @deprecated Use {@link #getMinUpdateIntervalMillis()} instead. */ + @SystemApi + @Deprecated public long getFastestInterval() { - return mFastestInterval; + return getMinUpdateIntervalMillis(); } /** - * Set the expiration time of this request in milliseconds of realtime since boot. Values in the - * past are allowed, but indicate that the request has already expired. The location manager - * will automatically stop updates after the request expires. - * - * @param millis expiration time of request in milliseconds since boot - * @return the same object, so that setters can be chained - * @see SystemClock#elapsedRealtime() - * @deprecated Prefer {@link #setExpireIn(long)}. + * @hide + * @deprecated LocationRequests should be treated as immutable. */ + @SystemApi @Deprecated public @NonNull LocationRequest setExpireAt(long millis) { - mExpireAt = Math.max(millis, 0); + mExpireAtRealtimeMillis = max(millis, 0); return this; } /** - * Get the request expiration time in milliseconds of realtime since boot. - * - * @return request expiration time in milliseconds since boot - * @see SystemClock#elapsedRealtime() - * @deprecated Prefer {@link #getExpireIn()}. + * @hide + * @deprecated Prefer {@link #getDurationMillis()} where possible. */ + @SystemApi @Deprecated public long getExpireAt() { - return mExpireAt; + return mExpireAtRealtimeMillis; } /** - * Set the duration of this request in milliseconds of realtime. Values less than 0 are allowed, - * but indicate that the request has already expired. The location manager will automatically - * stop updates after the request expires. - * - * @param millis duration of request in milliseconds - * @return the same object, so that setters can be chained - * @see SystemClock#elapsedRealtime() + * @hide + * @deprecated LocationRequests should be treated as immutable. */ + @SystemApi + @Deprecated public @NonNull LocationRequest setExpireIn(long millis) { - mExpireIn = millis; + mDurationMillis = millis; return this; } /** - * Get the request expiration duration in milliseconds of realtime. - * - * @return request expiration duration in milliseconds - * @see SystemClock#elapsedRealtime() + * @hide + * @deprecated Use {@link #getDurationMillis()} instead. */ + @SystemApi + @Deprecated public long getExpireIn() { - return mExpireIn; + return getDurationMillis(); } /** - * Returns the realtime at which this request expires, taking into account both - * {@link #setExpireAt(long)} and {@link #setExpireIn(long)} relative to the given realtime. + * Returns the duration for which location will be provided before the request is automatically + * removed. A duration of <code>Long.MAX_VALUE</code> represents an unlimited duration. * + * @return the duration for which location will be provided + */ + public long getDurationMillis() { + return mDurationMillis; + } + + /** * @hide */ public long getExpirationRealtimeMs(long startRealtimeMs) { long expirationRealtimeMs; // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0): - if (mExpireIn > Long.MAX_VALUE - startRealtimeMs) { + if (mDurationMillis > Long.MAX_VALUE - startRealtimeMs) { expirationRealtimeMs = Long.MAX_VALUE; } else { - expirationRealtimeMs = startRealtimeMs + mExpireIn; + expirationRealtimeMs = startRealtimeMs + mDurationMillis; } - return Math.min(expirationRealtimeMs, mExpireAt); + return min(expirationRealtimeMs, mExpireAtRealtimeMillis); } /** - * Set the number of location updates. - * - * <p>By default locations are continuously updated until the request is explicitly - * removed, however you can optionally request a set number of updates. - * For example, if your application only needs a single fresh location, - * then call this method with a value of 1 before passing the request - * to the location manager. - * - * @param numUpdates the number of location updates requested - * @return the same object, so that setters can be chained - * @throws IllegalArgumentException if numUpdates is 0 or less + * @hide + * @deprecated LocationRequests should be treated as immutable. */ + @SystemApi + @Deprecated public @NonNull LocationRequest setNumUpdates(int numUpdates) { if (numUpdates <= 0) { throw new IllegalArgumentException( "invalid numUpdates: " + numUpdates); } - mNumUpdates = numUpdates; + mMaxUpdates = numUpdates; return this; } /** - * Get the number of updates requested. - * - * <p>By default this is {@link Integer#MAX_VALUE}, which indicates that - * locations are updated until the request is explicitly removed. - * - * @return number of updates + * @hide + * @deprecated Use {@link #getMaxUpdates()} instead. */ + @SystemApi + @Deprecated public int getNumUpdates() { - return mNumUpdates; + return getMaxUpdates(); } - /** @hide */ - public void decrementNumUpdates() { - if (mNumUpdates != Integer.MAX_VALUE) { - mNumUpdates--; - } - if (mNumUpdates < 0) { - mNumUpdates = 0; + /** + * Returns the maximum number of location updates for this request before the request is + * automatically removed. A max updates value of <code>Integer.MAX_VALUE</code> represents an + * unlimited number of updates. + */ + public int getMaxUpdates() { + return mMaxUpdates; + } + + /** + * Returns the minimum update interval. If location updates are available faster than the + * request interval then locations will only be delivered if the minimum update interval has + * expired since the last location update. + * + * <p class=note><strong>Note:</strong> Some allowance for jitter is already built into the + * minimum update interval, so you need not worry about updates blocked simply because they + * arrived a fraction of a second earlier than expected. + * + * @return the minimum update interval + */ + public long getMinUpdateIntervalMillis() { + if (mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) { + return mInterval; + } else { + // the min is only necessary in case someone use a deprecated function to mess with the + // interval or min update interval + return min(mMinUpdateIntervalMillis, mInterval); } } - /** Sets the provider to use for this location request. */ - public @NonNull LocationRequest setProvider(@NonNull String provider) { - Preconditions.checkArgument(provider != null, "invalid provider: null"); - mProvider = provider; + /** + * @hide + * @deprecated LocationRequests should be treated as immutable. + */ + @SystemApi + @Deprecated + public @NonNull LocationRequest setSmallestDisplacement(float minDisplacementMeters) { + mMinUpdateDistanceMeters = Preconditions.checkArgumentInRange(minDisplacementMeters, 0, + Float.MAX_VALUE, "minDisplacementMeters"); return this; } - /** @hide */ + /** + * @hide + * @deprecated Use {@link #getMinUpdateDistanceMeters()} instead. + */ @SystemApi - public @NonNull String getProvider() { - return mProvider; + @Deprecated + public float getSmallestDisplacement() { + return getMinUpdateDistanceMeters(); + } + + /** + * Returns the minimum distance between location updates. If a potential location update is + * closer to the last location update than the minimum update distance, then the potential + * location update will not occur. A value of 0 meters implies that no location update will ever + * be rejected due to failing this constraint. + * + * @return the minimum distance between location updates + */ + public float getMinUpdateDistanceMeters() { + return mMinUpdateDistanceMeters; } - /** @hide */ + /** + * @hide + * @deprecated LocationRequests should be treated as immutable. + */ @SystemApi - public @NonNull LocationRequest setSmallestDisplacement(float smallestDisplacementM) { - mSmallestDisplacement = Preconditions.checkArgumentInRange(smallestDisplacementM, 0, - Float.MAX_VALUE, "smallestDisplacementM"); - return this; + @Deprecated + public void setHideFromAppOps(boolean hiddenFromAppOps) { + mHideFromAppOps = hiddenFromAppOps; } - /** @hide */ + /** + * @hide + * @deprecated Use {@link #isHiddenFromAppOps()} instead. + */ @SystemApi - public float getSmallestDisplacement() { - return mSmallestDisplacement; + @Deprecated + public boolean getHideFromAppOps() { + return isHiddenFromAppOps(); } /** - * Sets the WorkSource to use for power blaming of this location request. + * Returns true if this request should be ignored while updating app ops with location usage. + * This implies that someone else (usually the creator of the location request) is responsible + * for updating app ops. * - * <p>No permissions are required to make this call, however the LocationManager - * will throw a SecurityException when requesting location updates if the caller - * doesn't have the {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission. + * @return true if this request should be ignored while updating app ops with location usage * - * @param workSource WorkSource defining power blame for this location request. * @hide */ + @TestApi @SystemApi - public void setWorkSource(@Nullable WorkSource workSource) { - mWorkSource = workSource; + public boolean isHiddenFromAppOps() { + return mHideFromAppOps; } - /** @hide */ + /** + * @hide + * @deprecated LocationRequests should be treated as immutable. + */ @SystemApi - public @Nullable WorkSource getWorkSource() { - return mWorkSource; + @Deprecated + @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + public @NonNull LocationRequest setLocationSettingsIgnored(boolean locationSettingsIgnored) { + mLocationSettingsIgnored = locationSettingsIgnored; + return this; } /** - * Sets whether or not this location request should be hidden from AppOps. + * Returns true if location settings, throttling, background location limits, and any other + * possible limiting factors will be ignored in order to satisfy this request. + * + * @return true if all limiting factors will be ignored to satisfy this request * - * <p>Hiding a location request from AppOps will remove user visibility in the UI as to this - * request's existence. It does not affect power blaming in the Battery page. + * @hide + */ + @TestApi + @SystemApi + public boolean isLocationSettingsIgnored() { + return mLocationSettingsIgnored; + } + + /** + * @hide + * @deprecated LocationRequests should be treated as immutable. + */ + @SystemApi + @Deprecated + public @NonNull LocationRequest setLowPowerMode(boolean enabled) { + mLowPower = enabled; + return this; + } + + /** + * @hide + * @deprecated Use {@link #isLowPower()} instead. + */ + @Deprecated + @SystemApi + public boolean isLowPowerMode() { + return isLowPower(); + } + + /** + * Returns true if extreme trade-offs should be made to save power for this request. This + * usually involves specialized hardware modes which can greatly affect the quality of + * locations. * - * <p>No permissions are required to make this call, however the LocationManager - * will throw a SecurityException when requesting location updates if the caller - * doesn't have the {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} permission. + * @return true if extreme trade-offs should be made to save power for this request * - * @param hideFromAppOps If true AppOps won't keep track of this location request. * @hide - * @see android.app.AppOpsManager */ + @TestApi @SystemApi - public void setHideFromAppOps(boolean hideFromAppOps) { - mHideFromAppOps = hideFromAppOps; + public boolean isLowPower() { + return mLowPower; } - /** @hide */ + /** + * @hide + * @deprecated LocationRequests should be treated as immutable. + */ @SystemApi - public boolean getHideFromAppOps() { - return mHideFromAppOps; + @Deprecated + public void setWorkSource(@Nullable WorkSource workSource) { + mWorkSource = workSource; } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private static void checkQuality(int quality) { - switch (quality) { - case ACCURACY_FINE: - case ACCURACY_BLOCK: - case ACCURACY_CITY: - case POWER_NONE: - case POWER_LOW: - case POWER_HIGH: - break; - default: - throw new IllegalArgumentException("invalid quality: " + quality); - } + /** + * Returns the work source used for power blame for this request. If null, the system is free to + * assign power blame as it deems most appropriate. + * + * @return the work source used for power blame for this request + * + * @hide + */ + @TestApi + @SystemApi + public @Nullable WorkSource getWorkSource() { + return mWorkSource; } + public static final @NonNull Parcelable.Creator<LocationRequest> CREATOR = new Parcelable.Creator<LocationRequest>() { @Override public LocationRequest createFromParcel(Parcel in) { return new LocationRequest( /* provider= */ in.readString(), + /* intervalMillis= */ in.readLong(), /* quality= */ in.readInt(), - /* interval= */ in.readLong(), - /* fastestInterval= */ in.readLong(), - /* explicitFastestInterval= */ in.readBoolean(), - /* expireAt= */ in.readLong(), - /* expireIn= */ in.readLong(), - /* numUpdates= */ in.readInt(), - /* smallestDisplacement= */ in.readFloat(), - /* hideFromAppOps= */ in.readBoolean(), + /* expireAtRealtimeMillis= */ in.readLong(), + /* durationMillis= */ in.readLong(), + /* maxUpdates= */ in.readInt(), + /* minUpdateIntervalMillis= */ in.readLong(), + /* minUpdateDistanceMeters= */ in.readFloat(), + /* hiddenFromAppOps= */ in.readBoolean(), /* locationSettingsIgnored= */ in.readBoolean(), - /* lowPowerMode= */ in.readBoolean(), + /* lowPower= */ in.readBoolean(), /* workSource= */ in.readTypedObject(WorkSource.CREATOR)); } @@ -728,42 +679,21 @@ public final class LocationRequest implements Parcelable { } @Override - public void writeToParcel(Parcel parcel, int flags) { + public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeString(mProvider); - parcel.writeInt(mQuality); parcel.writeLong(mInterval); - parcel.writeLong(mFastestInterval); - parcel.writeBoolean(mExplicitFastestInterval); - parcel.writeLong(mExpireAt); - parcel.writeLong(mExpireIn); - parcel.writeInt(mNumUpdates); - parcel.writeFloat(mSmallestDisplacement); + parcel.writeInt(mQuality); + parcel.writeLong(mExpireAtRealtimeMillis); + parcel.writeLong(mDurationMillis); + parcel.writeInt(mMaxUpdates); + parcel.writeLong(mMinUpdateIntervalMillis); + parcel.writeFloat(mMinUpdateDistanceMeters); parcel.writeBoolean(mHideFromAppOps); parcel.writeBoolean(mLocationSettingsIgnored); - parcel.writeBoolean(mLowPowerMode); + parcel.writeBoolean(mLowPower); parcel.writeTypedObject(mWorkSource, 0); } - /** @hide */ - public static String qualityToString(int quality) { - switch (quality) { - case ACCURACY_FINE: - return "ACCURACY_FINE"; - case ACCURACY_BLOCK: - return "ACCURACY_BLOCK"; - case ACCURACY_CITY: - return "ACCURACY_CITY"; - case POWER_NONE: - return "POWER_NONE"; - case POWER_LOW: - return "POWER_LOW"; - case POWER_HIGH: - return "POWER_HIGH"; - default: - return "???"; - } - } - @Override public boolean equals(Object o) { if (this == o) { @@ -774,18 +704,17 @@ public final class LocationRequest implements Parcelable { } LocationRequest that = (LocationRequest) o; - return mQuality == that.mQuality - && mInterval == that.mInterval - && mFastestInterval == that.mFastestInterval - && mExplicitFastestInterval == that.mExplicitFastestInterval - && mExpireAt == that.mExpireAt - && mExpireIn == that.mExpireIn - && mNumUpdates == that.mNumUpdates - && Float.compare(that.mSmallestDisplacement, mSmallestDisplacement) == 0 + return mInterval == that.mInterval + && mQuality == that.mQuality + && mExpireAtRealtimeMillis == that.mExpireAtRealtimeMillis + && mDurationMillis == that.mDurationMillis + && mMaxUpdates == that.mMaxUpdates + && mMinUpdateIntervalMillis == that.mMinUpdateIntervalMillis + && Float.compare(that.mMinUpdateDistanceMeters, mMinUpdateDistanceMeters) == 0 && mHideFromAppOps == that.mHideFromAppOps && mLocationSettingsIgnored == that.mLocationSettingsIgnored - && mLowPowerMode == that.mLowPowerMode - && mProvider.equals(that.mProvider) + && mLowPower == that.mLowPower + && Objects.equals(mProvider, that.mProvider) && Objects.equals(mWorkSource, that.mWorkSource); } @@ -799,33 +728,371 @@ public final class LocationRequest implements Parcelable { public String toString() { StringBuilder s = new StringBuilder(); s.append("Request["); - s.append(qualityToString(mQuality)); - s.append(" ").append(mProvider); - if (mQuality != POWER_NONE) { - s.append(" interval="); + if (mProvider != null) { + s.append(mProvider).append(" "); + } + if (mQuality != POWER_NONE && mQuality != ACCURACY_BLOCK) { + s.append(qualityToString(mQuality)).append(" "); + } + if (mInterval != PASSIVE_INTERVAL) { + s.append("interval="); TimeUtils.formatDuration(mInterval, s); - if (mExplicitFastestInterval && mFastestInterval != mInterval) { - s.append(" fastestInterval="); - TimeUtils.formatDuration(mFastestInterval, s); - } + } else { + s.append("PASSIVE"); + } + if (mExpireAtRealtimeMillis != Long.MAX_VALUE) { + s.append(" expireAt=").append(TimeUtils.formatRealtime(mExpireAtRealtimeMillis)); } - if (mExpireAt != Long.MAX_VALUE) { - s.append(" expireAt=").append(TimeUtils.formatRealtime(mExpireAt)); + if (mDurationMillis != Long.MAX_VALUE) { + s.append(" duration="); + TimeUtils.formatDuration(mDurationMillis, s); } - if (mExpireIn != Long.MAX_VALUE) { - s.append(" expireIn="); - TimeUtils.formatDuration(mExpireIn, s); + if (mMaxUpdates != Integer.MAX_VALUE) { + s.append(" maxUpdates=").append(mMaxUpdates); } - if (mNumUpdates != Integer.MAX_VALUE) { - s.append(" num=").append(mNumUpdates); + if (mMinUpdateIntervalMillis < mInterval) { + s.append(" minUpdateInterval="); + TimeUtils.formatDuration(mMinUpdateIntervalMillis, s); } - if (mLowPowerMode) { - s.append(" lowPowerMode"); + if (mMinUpdateDistanceMeters > 0.0) { + s.append(" minUpdateDistance=").append(mMinUpdateDistanceMeters); + } + if (mLowPower) { + s.append(" lowPower"); + } + if (mHideFromAppOps) { + s.append(" hiddenFromAppOps"); } if (mLocationSettingsIgnored) { s.append(" locationSettingsIgnored"); } + if (mWorkSource != null) { + s.append(" ").append(mWorkSource); + } s.append(']'); return s.toString(); } + + private static String qualityToString(int quality) { + switch (quality) { + case ACCURACY_FINE: + return "ACCURACY_FINE"; + case ACCURACY_BLOCK: + return "ACCURACY_BLOCK"; + case ACCURACY_CITY: + return "ACCURACY_CITY"; + case POWER_NONE: + return "POWER_NONE"; + case POWER_LOW: + return "POWER_LOW"; + case POWER_HIGH: + return "POWER_HIGH"; + default: + return "???"; + } + } + + /** + * A builder class for {@link LocationRequest}. + */ + public static final class Builder { + + private static int checkQuality(int quality, boolean allowDeprecated) { + switch (quality) { + case ACCURACY_FINE: + // fall through + case ACCURACY_BLOCK: + // fall through + case ACCURACY_CITY: + // fall through + case POWER_LOW: + // fall through + case POWER_HIGH: + return quality; + case POWER_NONE: + if (allowDeprecated) { + return quality; + } + // fall through + default: + throw new IllegalArgumentException("invalid quality: " + quality); + } + } + + private long mIntervalMillis; + private int mQuality; + private long mDurationMillis; + private int mMaxUpdates; + private long mMinUpdateIntervalMillis; + private float mMinUpdateDistanceMeters; + private boolean mHiddenFromAppOps; + private boolean mLocationSettingsIgnored; + private boolean mLowPower; + @Nullable private WorkSource mWorkSource; + + /** + * Creates a new Builder with the given interval. See {@link #setIntervalMillis(long)} for + * more information on the interval. + */ + public Builder(long intervalMillis) { + // gives us a range check + setIntervalMillis(intervalMillis); + + mQuality = ACCURACY_BLOCK; + mDurationMillis = Long.MAX_VALUE; + mMaxUpdates = Integer.MAX_VALUE; + mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL; + mMinUpdateDistanceMeters = 0; + mHiddenFromAppOps = false; + mLocationSettingsIgnored = false; + mLowPower = false; + mWorkSource = null; + } + + /** + * Creates a new Builder with all parameters copied from the given location request. + */ + public Builder(@NonNull LocationRequest locationRequest) { + mIntervalMillis = locationRequest.mInterval; + mQuality = locationRequest.mQuality; + mDurationMillis = locationRequest.mDurationMillis; + mMaxUpdates = locationRequest.mMaxUpdates; + mMinUpdateIntervalMillis = locationRequest.mMinUpdateIntervalMillis; + mMinUpdateDistanceMeters = locationRequest.mMinUpdateDistanceMeters; + mHiddenFromAppOps = locationRequest.mHideFromAppOps; + mLocationSettingsIgnored = locationRequest.mLocationSettingsIgnored; + mLowPower = locationRequest.mLowPower; + mWorkSource = locationRequest.mWorkSource; + + // handle edge cases that can only happen with location request that has been modified + // by deprecated SystemApi methods + if (mQuality == POWER_NONE) { + mIntervalMillis = PASSIVE_INTERVAL; + } + if (mIntervalMillis == PASSIVE_INTERVAL + && mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) { + // this is the legacy default minimum update interval, so if we're forced to + // change the value, at least this should be unsuprising to legacy clients (which + // should be the only clients capable of getting in this weird state). + mMinUpdateIntervalMillis = 10 * 60 * 1000; + } + } + + /** + * Sets the request interval. The request interval may be set to {@link #PASSIVE_INTERVAL} + * which indicates this request will not actively generate location updates (and thus will + * not be power blamed for location), but may receive location updates generated as a result + * of other location requests. A passive request must always have an explicit minimum + * update interval set. + * + * <p>Locations may be available at a faster interval than specified here, see + * {@link #setMinUpdateIntervalMillis(long)} for the behavior in that case. + */ + public @NonNull Builder setIntervalMillis(@IntRange(from = 0) long intervalMillis) { + mIntervalMillis = Preconditions.checkArgumentInRange(intervalMillis, 0, Long.MAX_VALUE, + "intervalMillis"); + return this; + } + + /** + * @hide + */ + @SystemApi + public @NonNull Builder setQuality(int quality) { + mQuality = checkQuality(quality, false); + return this; + } + + /** + * @hide + */ + public @NonNull Builder setQuality(@NonNull Criteria criteria) { + switch (criteria.getAccuracy()) { + case Criteria.ACCURACY_COARSE: + mQuality = ACCURACY_BLOCK; + break; + case Criteria.ACCURACY_FINE: + mQuality = ACCURACY_FINE; + break; + default: { + if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) { + mQuality = POWER_HIGH; + } else { + mQuality = POWER_LOW; + } + } + } + return this; + } + + /** + * Sets the duration this request will continue before being automatically removed. Defaults + * to <code>Long.MAX_VALUE</code>, which represents an unlimited duration. + */ + public @NonNull Builder setDurationMillis(@IntRange(from = 1) long durationMillis) { + mDurationMillis = Preconditions.checkArgumentInRange(durationMillis, 1, Long.MAX_VALUE, + "durationMillis"); + return this; + } + + /** + * Sets the maximum number of location updates for this request before this request is + * automatically removed. Defaults to <code>Integer.MAX_VALUE</code>, which represents an + * unlimited number of updates. + */ + public @NonNull Builder setMaxUpdates( + @IntRange(from = 1, to = Integer.MAX_VALUE) int maxUpdates) { + mMaxUpdates = Preconditions.checkArgumentInRange(maxUpdates, 1, Integer.MAX_VALUE, + "maxUpdates"); + return this; + } + + /** + * Sets an explicit minimum update interval. If location updates are available faster than + * the request interval then locations will only be delivered if the minimum update interval + * has expired since the last location update. Defaults to no explicit minimum update + * interval set, which means the minimum update interval is the same as the interval. + * + * <p class=note><strong>Note:</strong> Some allowance for jitter is already built into the + * minimum update interval, so you need not worry about updates blocked simply because they + * arrived a fraction of a second earlier than expected. + * + * <p class="note"><strong>Note:</strong> When {@link #build()} is invoked, the minimum of + * the interval and the minimum update interval will be used as the minimum update interval + * of the built request. + */ + public @NonNull Builder setMinUpdateIntervalMillis( + @IntRange(from = 0) long minUpdateIntervalMillis) { + mMinUpdateIntervalMillis = Preconditions.checkArgumentInRange(minUpdateIntervalMillis, + 0, Long.MAX_VALUE, "minUpdateIntervalMillis"); + return this; + } + + /** + * Clears an explicitly set minimum update interval and reverts to an implicit minimum + * update interval which is the same as the interval. + */ + public @NonNull Builder clearMinUpdateIntervalMillis() { + mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL; + return this; + } + + /** + * Sets the minimum update distance between delivered locations. If a potential location + * update is closer to the last delivered location than the minimum update distance, then + * the potential location update will not occur. Defaults to 0, which represents no minimum + * update distance. + */ + public @NonNull Builder setMinUpdateDistanceMeters( + @FloatRange(from = 0, to = Float.MAX_VALUE) float minUpdateDistanceMeters) { + mMinUpdateDistanceMeters = Preconditions.checkArgumentInRange(minUpdateDistanceMeters, + 0, Float.MAX_VALUE, "minUpdateDistanceMeters"); + return this; + } + + /** + * If set to true, indicates that app ops should not be updated with location usage due to + * this request. This implies that someone else (usually the creator of the location + * request) is responsible for updating app ops as appropriate. Defaults to false. + * + * <p>Permissions enforcement occurs when resulting location request is actually used, not + * when this method is invoked. + * + * @hide + */ + @TestApi + @SystemApi + @RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS) + public @NonNull Builder setHiddenFromAppOps(boolean hiddenFromAppOps) { + mHiddenFromAppOps = hiddenFromAppOps; + return this; + } + + /** + * If set to true, indicates that location settings, throttling, background location limits, + * and any other possible limiting factors should be ignored in order to satisfy this + * request. This is only intended for use in user initiated emergency situations, and + * should be used extremely cautiously. Defaults to false. + * + * <p>Permissions enforcement occurs when resulting location request is actually used, not + * when this method is invoked. + * + * @hide + */ + @TestApi + @SystemApi + @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) { + mLocationSettingsIgnored = locationSettingsIgnored; + return this; + } + + /** + * It set to true, indicates that extreme trade-offs should be made if possible to save + * power for this request. This usually involves specialized hardware modes which can + * greatly affect the quality of locations. Defaults to false. + * + * <p>Permissions enforcement occurs when resulting location request is actually used, not + * when this method is invoked. + * + * @hide + */ + @TestApi + @SystemApi + @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) + public @NonNull Builder setLowPower(boolean lowPower) { + mLowPower = lowPower; + return this; + } + + /** + * Sets the work source to use for power blame for this location request. Defaults to null, + * which implies the system is free to assign power blame as it determines best for this + * request (which usually means blaming the owner of the location listener). + * + * <p>Permissions enforcement occurs when resulting location request is actually used, not + * when this method is invoked. + * + * @hide + */ + @TestApi + @SystemApi + @RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS) + public @NonNull Builder setWorkSource(@Nullable WorkSource workSource) { + mWorkSource = workSource; + return this; + } + + /** + * Builds a location request from this builder. If an explicit minimum update interval is + * set, the minimum update interval of the location request will be the minimum of the + * interval and minimum update interval. + * + * <p>If building a passive request then you must have set an explicit minimum update + * interval. + * + * @throws IllegalStateException if building a passive request with no explicit minimum + * update interval set + * @return a new location request + */ + public @NonNull LocationRequest build() { + Preconditions.checkState(mIntervalMillis != PASSIVE_INTERVAL + || mMinUpdateIntervalMillis != IMPLICIT_MIN_UPDATE_INTERVAL, + "passive location requests must have an explicit minimum update interval"); + + return new LocationRequest( + null, + mIntervalMillis, + mIntervalMillis != PASSIVE_INTERVAL ? mQuality : POWER_NONE, + Long.MAX_VALUE, + mDurationMillis, + mMaxUpdates, + min(mMinUpdateIntervalMillis, mIntervalMillis), + mMinUpdateDistanceMeters, + mHiddenFromAppOps, + mLocationSettingsIgnored, + mLowPower, + mWorkSource); + } + } } diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java index 2511c39caf5c..92e05ef68e3b 100644 --- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java +++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java @@ -82,25 +82,21 @@ public final class LocationRequestUnbundled { } /** - * Get the desired interval of this request, in milliseconds. + * Get the location update interval. * - * @return desired interval in milliseconds, inexact + * @return location update interval */ public long getInterval() { - return delegate.getInterval(); + return delegate.getIntervalMillis(); } /** - * Get the fastest interval of this request, in milliseconds. + * Get the minimum delivery interval. * - * <p>The system will never provide location updates faster - * than the minimum of {@link #getFastestInterval} and - * {@link #getInterval}. - * - * @return fastest interval in milliseconds, exact + * @return minimum delivery interval */ public long getFastestInterval() { - return delegate.getFastestInterval(); + return delegate.getMinUpdateIntervalMillis(); } /** @@ -118,7 +114,7 @@ public final class LocationRequestUnbundled { * @return minimum distance between location updates in meters */ public float getSmallestDisplacement() { - return delegate.getSmallestDisplacement(); + return delegate.getMinUpdateDistanceMeters(); } /** diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java index d8a0bb334c53..d248f61f03ca 100644 --- a/media/java/android/media/ImageUtils.java +++ b/media/java/android/media/ImageUtils.java @@ -63,6 +63,7 @@ class ImageUtils { case ImageFormat.DEPTH16: case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.RAW_DEPTH: + case ImageFormat.RAW_DEPTH10: case ImageFormat.DEPTH_JPEG: case ImageFormat.HEIC: return 1; @@ -110,6 +111,10 @@ class ImageUtils { throw new IllegalArgumentException( "Copy of RAW_DEPTH format has not been implemented"); } + if (src.getFormat() == ImageFormat.RAW_DEPTH10) { + throw new IllegalArgumentException( + "Copy of RAW_DEPTH10 format has not been implemented"); + } if (!(dst.getOwner() instanceof ImageWriter)) { throw new IllegalArgumentException("Destination image is not from ImageWriter. Only" + " the images from ImageWriter are writable"); @@ -202,6 +207,7 @@ class ImageUtils { estimatedBytePerPixel = 1.0; break; case ImageFormat.RAW10: + case ImageFormat.RAW_DEPTH10: estimatedBytePerPixel = 1.25; break; case ImageFormat.YV12: @@ -264,6 +270,7 @@ class ImageUtils { case ImageFormat.RAW10: case ImageFormat.RAW12: case ImageFormat.RAW_DEPTH: + case ImageFormat.RAW_DEPTH10: case ImageFormat.HEIC: return new Size(image.getWidth(), image.getHeight()); case ImageFormat.PRIVATE: diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java index 876628fefff4..acd284f3fad9 100644 --- a/media/java/android/media/MicrophoneInfo.java +++ b/media/java/android/media/MicrophoneInfo.java @@ -268,8 +268,11 @@ public final class MicrophoneInfo { /** * Returns A {@link Coordinate3F} object that represents the geometric location of microphone - * in meters, from bottom-left-back corner of appliance. X-axis, Y-axis and Z-axis show - * as the x, y, z values. + * in meters. X-axis, Y-axis and Z-axis show as the x, y, z values. For mobile devices, the axes + * originate from the bottom-left-back corner of the appliance. In devices with + * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE}, axes are defined with respect + * to the vehicle body frame, originating from the center of the vehicle's rear axle. + * @see <a href="https://source.android.com/devices/sensors/sensor-types#auto_axes">auto axes</a> * * @return the geometric location of the microphone or {@link #POSITION_UNKNOWN} if the * geometric location is unknown diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 20006934ee46..9494295e4bd9 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -68,16 +68,19 @@ public final class MediaSessionManager { private static final String TAG = "SessionManager"; /** - * Used by IOnMediaKeyListener to indicate that the media key event isn't handled. + * Used to indicate that the media key event isn't handled. * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; /** - * Used by IOnMediaKeyListener to indicate that the media key event is handled. + * Used to indicate that the media key event is handled. * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final int RESULT_MEDIA_KEY_HANDLED = 1; + private final ISessionManager mService; private final OnMediaKeyEventDispatchedListenerStub mOnMediaKeyEventDispatchedListenerStub = new OnMediaKeyEventDispatchedListenerStub(); diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index d62e132b5745..4dd3f008f97a 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -23907,6 +23907,7 @@ package android.location { method @NonNull public java.util.List<java.lang.String> getAllProviders(); method @Nullable public String getBestProvider(@NonNull android.location.Criteria, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); method @NonNull public android.location.GnssCapabilities getGnssCapabilities(); method @Nullable public String getGnssHardwareModelName(); method public int getGnssYearOfHardware(); @@ -23941,6 +23942,8 @@ package android.location { method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, long, float, @NonNull android.app.PendingIntent); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull android.app.PendingIntent); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, @NonNull android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, @NonNull android.location.LocationRequest, @NonNull android.app.PendingIntent); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.location.LocationListener, @Nullable android.os.Looper); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull android.location.Criteria, @NonNull android.location.LocationListener, @Nullable android.os.Looper); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.app.PendingIntent); @@ -23984,6 +23987,30 @@ package android.location { field @Deprecated public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1 } + public final class LocationRequest implements android.os.Parcelable { + method public int describeContents(); + method public long getDurationMillis(); + method public long getIntervalMillis(); + method public int getMaxUpdates(); + method public float getMinUpdateDistanceMeters(); + method public long getMinUpdateIntervalMillis(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR; + field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL + } + + public static final class LocationRequest.Builder { + ctor public LocationRequest.Builder(long); + ctor public LocationRequest.Builder(@NonNull android.location.LocationRequest); + method @NonNull public android.location.LocationRequest build(); + method @NonNull public android.location.LocationRequest.Builder clearMinUpdateIntervalMillis(); + method @NonNull public android.location.LocationRequest.Builder setDurationMillis(@IntRange(from=1) long); + method @NonNull public android.location.LocationRequest.Builder setIntervalMillis(@IntRange(from=0) long); + method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int); + method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float); + method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long); + } + public interface OnNmeaMessageListener { method public void onNmeaMessage(String, long); } diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index f7f42d0a5956..63b98cb5bd6a 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -66,6 +66,8 @@ package android.media.session { method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + field public static final int RESULT_MEDIA_KEY_HANDLED = 1; // 0x1 + field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0 } } diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 8fae240a95b4..7f95e1a35b85 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -671,6 +671,9 @@ package android.app { method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; + field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; } public final class RuntimeAppOpAccessMessage implements android.os.Parcelable { @@ -4015,7 +4018,7 @@ package android.location { public class LocationManager { method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); method @Nullable public String getExtraLocationControllerPackage(); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize(); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections); @@ -4026,9 +4029,9 @@ package android.location { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.LOCATION_HARDWARE}) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle); @@ -4037,42 +4040,49 @@ package android.location { } public final class LocationRequest implements android.os.Parcelable { - method @NonNull public static android.location.LocationRequest create(); - method @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean); - method @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean); - method public int describeContents(); + method @Deprecated @NonNull public static android.location.LocationRequest create(); + method @Deprecated @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean); + method @Deprecated @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean); method @Deprecated public long getExpireAt(); - method public long getExpireIn(); - method public long getFastestInterval(); - method public boolean getHideFromAppOps(); - method public long getInterval(); - method public int getNumUpdates(); - method @NonNull public String getProvider(); + method @Deprecated public long getExpireIn(); + method @Deprecated public long getFastestInterval(); + method @Deprecated public boolean getHideFromAppOps(); + method @Deprecated public long getInterval(); + method @Deprecated public int getNumUpdates(); + method @Deprecated @NonNull public String getProvider(); method public int getQuality(); - method public float getSmallestDisplacement(); + method @Deprecated public float getSmallestDisplacement(); method @Nullable public android.os.WorkSource getWorkSource(); + method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); - method public boolean isLowPowerMode(); + method public boolean isLowPower(); + method @Deprecated public boolean isLowPowerMode(); method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long); - method @NonNull public android.location.LocationRequest setExpireIn(long); - method @NonNull public android.location.LocationRequest setFastestInterval(long); - method public void setHideFromAppOps(boolean); - method @NonNull public android.location.LocationRequest setInterval(long); - method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean); - method @NonNull public android.location.LocationRequest setLowPowerMode(boolean); - method @NonNull public android.location.LocationRequest setNumUpdates(int); - method @NonNull public android.location.LocationRequest setProvider(@NonNull String); - method @NonNull public android.location.LocationRequest setQuality(int); - method @NonNull public android.location.LocationRequest setSmallestDisplacement(float); - method public void setWorkSource(@Nullable android.os.WorkSource); - method public void writeToParcel(android.os.Parcel, int); + method @Deprecated @NonNull public android.location.LocationRequest setExpireIn(long); + method @Deprecated @NonNull public android.location.LocationRequest setFastestInterval(long); + method @Deprecated public void setHideFromAppOps(boolean); + method @Deprecated @NonNull public android.location.LocationRequest setInterval(long); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean); + method @Deprecated @NonNull public android.location.LocationRequest setLowPowerMode(boolean); + method @Deprecated @NonNull public android.location.LocationRequest setNumUpdates(int); + method @Deprecated @NonNull public android.location.LocationRequest setProvider(@NonNull String); + method @Deprecated @NonNull public android.location.LocationRequest setQuality(int); + method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float); + method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource); field public static final int ACCURACY_BLOCK = 102; // 0x66 field public static final int ACCURACY_CITY = 104; // 0x68 field public static final int ACCURACY_FINE = 100; // 0x64 - field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR; field public static final int POWER_HIGH = 203; // 0xcb field public static final int POWER_LOW = 201; // 0xc9 - field public static final int POWER_NONE = 200; // 0xc8 + field @Deprecated public static final int POWER_NONE = 200; // 0xc8 + } + + public static final class LocationRequest.Builder { + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean); + method @NonNull public android.location.LocationRequest.Builder setQuality(int); + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource); } } @@ -10385,6 +10395,7 @@ package android.telephony.data { method public int getCause(); method @NonNull public java.util.List<java.net.InetAddress> getDnsAddresses(); method @NonNull public java.util.List<java.net.InetAddress> getGatewayAddresses(); + method public int getHandoverFailureMode(); method public int getId(); method @NonNull public String getInterfaceName(); method public int getLinkStatus(); @@ -10396,6 +10407,11 @@ package android.telephony.data { method public int getSuggestedRetryTime(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; + field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2; // 0x2 + field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1 + field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3 + field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4 + field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0 field public static final int LINK_STATUS_ACTIVE = 2; // 0x2 field public static final int LINK_STATUS_DORMANT = 1; // 0x1 field public static final int LINK_STATUS_INACTIVE = 0; // 0x0 @@ -10409,6 +10425,7 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setCause(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setDnsAddresses(@NonNull java.util.List<java.net.InetAddress>); method @NonNull public android.telephony.data.DataCallResponse.Builder setGatewayAddresses(@NonNull java.util.List<java.net.InetAddress>); + method @NonNull public android.telephony.data.DataCallResponse.Builder setHandoverFailureMode(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setId(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@NonNull String); method @NonNull public android.telephony.data.DataCallResponse.Builder setLinkStatus(int); diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java index 781efc118429..900e68d36c32 100644 --- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java +++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java @@ -30,7 +30,6 @@ import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationRequest; -import android.os.Looper; import android.os.WorkSource; import com.android.internal.annotations.GuardedBy; @@ -182,6 +181,7 @@ public class FusedLocationProvider extends LocationProviderBase { for (LocationRequestUnbundled request : mRequest.getLocationRequests()) { switch (request.getQuality()) { case LocationRequestUnbundled.ACCURACY_FINE: + case LocationRequestUnbundled.ACCURACY_BLOCK: case LocationRequestUnbundled.POWER_HIGH: if (request.getInterval() < gpsInterval) { gpsInterval = request.getInterval(); @@ -190,7 +190,6 @@ public class FusedLocationProvider extends LocationProviderBase { networkInterval = request.getInterval(); } break; - case LocationRequestUnbundled.ACCURACY_BLOCK: case LocationRequestUnbundled.ACCURACY_CITY: case LocationRequestUnbundled.POWER_LOW: if (request.getInterval() < networkInterval) { @@ -219,13 +218,12 @@ public class FusedLocationProvider extends LocationProviderBase { mLocationManager.removeUpdates(listener); } if (newInterval != Long.MAX_VALUE) { - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - provider, newInterval, 0, false); - if (mRequest.isLocationSettingsIgnored()) { - request.setLocationSettingsIgnored(true); - } - request.setWorkSource(mWorkSource); - mLocationManager.requestLocationUpdates(request, listener, Looper.getMainLooper()); + LocationRequest request = new LocationRequest.Builder(newInterval) + .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored()) + .setWorkSource(mWorkSource) + .build(); + mLocationManager.requestLocationUpdates(provider, request, mContext.getMainExecutor(), + listener); } } diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java index 2c4545e7b265..687dd13f8732 100644 --- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java +++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java @@ -16,7 +16,6 @@ package com.android.location.fused.tests; -import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; import static android.location.LocationManager.NETWORK_PROVIDER; @@ -121,8 +120,7 @@ public class FusedLocationServiceTest { @Test public void testNetworkRequest() throws Exception { - LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000, - 0, false); + LocationRequest request = new LocationRequest.Builder(1000).build(); mProvider.setRequest( new ProviderRequest.Builder() @@ -139,8 +137,9 @@ public class FusedLocationServiceTest { @Test public void testGpsRequest() throws Exception { - LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000, - 0, false).setQuality(LocationRequest.POWER_HIGH); + LocationRequest request = new LocationRequest.Builder(1000) + .setQuality(LocationRequest.POWER_HIGH) + .build(); mProvider.setRequest( new ProviderRequest.Builder() diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml index f3e11fb923fe..eb89ee56345a 100644 --- a/packages/InputDevices/res/values-af/strings.xml +++ b/packages/InputDevices/res/values-af/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fins"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroaties"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tsjeggies"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tsjeggiese QWERTY-styl"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estnies"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongaars"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Yslands"</string> diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml index 1729f2903f48..2970bfe722d8 100644 --- a/packages/InputDevices/res/values-am/strings.xml +++ b/packages/InputDevices/res/values-am/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ፊኒሽ"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"ክሮሽያ"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"ቼክ"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"የቼክኛ QWERTY ቅጥ"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"ኤስቶኒያ"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ሀንጋሪ"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"አይስላንድ"</string> diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml index 01feb95a127d..a1edb21d4b5a 100644 --- a/packages/InputDevices/res/values-bg/strings.xml +++ b/packages/InputDevices/res/values-bg/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"финландски"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"хърватски"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"чешки"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешки стил за QWERTY"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"естонски"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"унгарски"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландски"</string> diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml index 893d45376796..436a3b02eb54 100644 --- a/packages/InputDevices/res/values-bs/strings.xml +++ b/packages/InputDevices/res/values-bs/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finski"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvatski"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"češki"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Češka QWERTY tastatura"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonski"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"mađarski"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandski"</string> diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml index 9da74e14e140..1f089e18d5cb 100644 --- a/packages/InputDevices/res/values-ca/strings.xml +++ b/packages/InputDevices/res/values-ca/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finès"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croat"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Txec"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estil QWERTY txec"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonià"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongarès"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandès"</string> diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml index 041fcbbe8960..b36861a99cc0 100644 --- a/packages/InputDevices/res/values-cs/strings.xml +++ b/packages/InputDevices/res/values-cs/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finské"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"chorvatské"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"české"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Český styl QWERTY"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonské"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"maďarské"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandské"</string> diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml index bde1fd2c9d9e..7e446fd44bc4 100644 --- a/packages/InputDevices/res/values-da/strings.xml +++ b/packages/InputDevices/res/values-da/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finsk"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisk"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tjekkisk"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tjekkisk – QWERTY-layout"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estisk"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungarsk"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandsk"</string> diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml index 975308f4ffda..c8a7ab1f34fe 100644 --- a/packages/InputDevices/res/values-de/strings.xml +++ b/packages/InputDevices/res/values-de/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnisch"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisch"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tschechisch"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY-Tastatur, tschechisch"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estnisch"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungarisch"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Isländisch"</string> diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml index 52088f6199d0..ff7cd022cee7 100644 --- a/packages/InputDevices/res/values-el/strings.xml +++ b/packages/InputDevices/res/values-el/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Φινλανδικά"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Κροατικά"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Τσεχικά"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Τσεχικό πληκτρολόγιο στιλ QWERTY"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Εσθονικά"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ουγγρικά"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Ισλανδικά"</string> diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml index 17a1fb754cad..02a8e6d4138f 100644 --- a/packages/InputDevices/res/values-en-rAU/strings.xml +++ b/packages/InputDevices/res/values-en-rAU/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string> diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml index 17a1fb754cad..02a8e6d4138f 100644 --- a/packages/InputDevices/res/values-en-rCA/strings.xml +++ b/packages/InputDevices/res/values-en-rCA/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string> diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml index 17a1fb754cad..02a8e6d4138f 100644 --- a/packages/InputDevices/res/values-en-rGB/strings.xml +++ b/packages/InputDevices/res/values-en-rGB/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string> diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml index 17a1fb754cad..02a8e6d4138f 100644 --- a/packages/InputDevices/res/values-en-rIN/strings.xml +++ b/packages/InputDevices/res/values-en-rIN/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string> diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml index 8603c54c6b46..a8d342f04258 100644 --- a/packages/InputDevices/res/values-en-rXC/strings.xml +++ b/packages/InputDevices/res/values-en-rXC/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string> diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml index fc5e3d88228b..9090cb7b02b2 100644 --- a/packages/InputDevices/res/values-es-rUS/strings.xml +++ b/packages/InputDevices/res/values-es-rUS/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandés"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Checo"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY estilo checo"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonio"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandés"</string> diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml index 773cdf9a4459..66ea65557538 100644 --- a/packages/InputDevices/res/values-es/strings.xml +++ b/packages/InputDevices/res/values-es/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandés"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Checo"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY checo"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonio"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandés"</string> diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml index dbe5865aeadb..9674a0d1d616 100644 --- a/packages/InputDevices/res/values-et/strings.xml +++ b/packages/InputDevices/res/values-et/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Soome"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Horvaatia"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tšehhi"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tšehhi QWERTY-stiil"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Eesti"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungari"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandi"</string> diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml index e89ffa8b6c68..357b6184648f 100644 --- a/packages/InputDevices/res/values-eu/strings.xml +++ b/packages/InputDevices/res/values-eu/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandiarra"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroaziarra"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Txekiarra"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY estilo txekiarra"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniarra"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungariarra"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandiarra"</string> diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml index 2d5f81d06d21..5395d27c2cef 100644 --- a/packages/InputDevices/res/values-fa/strings.xml +++ b/packages/InputDevices/res/values-fa/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"فنلاندی"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"کرواسی"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"چک"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"سبک QWERTY چک"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"استونیایی"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"مجارستانی"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ایسلندی"</string> diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml index beb0e07bc751..e714e8380800 100644 --- a/packages/InputDevices/res/values-fr-rCA/strings.xml +++ b/packages/InputDevices/res/values-fr-rCA/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnois"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croate"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tchèque"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Clavier QWERTY tchèque"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonien"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongrois"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandais"</string> diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml index 223ed3c78a9e..0a022f192ea2 100644 --- a/packages/InputDevices/res/values-fr/strings.xml +++ b/packages/InputDevices/res/values-fr/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnois"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croate"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tchèque"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Clavier QWERTY tchèque"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonien"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongrois"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandais"</string> diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml index 7d3e1d6fb903..0c86f816fa85 100644 --- a/packages/InputDevices/res/values-gl/strings.xml +++ b/packages/InputDevices/res/values-gl/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finés"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Checo"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY checo"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniano"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandés"</string> diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml index 0f7863a11aaa..8648389db862 100644 --- a/packages/InputDevices/res/values-gu/strings.xml +++ b/packages/InputDevices/res/values-gu/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ફિનિશ"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"ક્રોએશિયન"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"ચેક"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ચેક QWERTY શૈલી"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"એસ્ટોનિયન"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"હંગેરિયન"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"આઇસલેન્ડિક"</string> diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml index 37a5e582b34c..6e674edbedc1 100644 --- a/packages/InputDevices/res/values-hi/strings.xml +++ b/packages/InputDevices/res/values-hi/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"फ़िनिश"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"क्रोएशियन"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"चेक"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"चेक QWERTY स्टाइल"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"एस्टोनियाई"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"हंगेरियाई"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"आइसलैंडिक"</string> diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml index 83c2fc1f6b7b..cd2dcc183058 100644 --- a/packages/InputDevices/res/values-hr/strings.xml +++ b/packages/InputDevices/res/values-hr/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finska"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvatska"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"češka"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Češka QWERTY tipkovnica"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonska"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"mađarska"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandska"</string> diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml index cd11cb423e8d..1c7a89a06059 100644 --- a/packages/InputDevices/res/values-hu/strings.xml +++ b/packages/InputDevices/res/values-hu/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finn"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"horvát"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"cseh"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY kiosztás (cseh)"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"észt"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"magyar"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"izlandi"</string> diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml index 4528377695c6..fff9fbc5bfd8 100644 --- a/packages/InputDevices/res/values-hy/strings.xml +++ b/packages/InputDevices/res/values-hy/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Ֆիններեն"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Խորվաթերեն"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Չեխերեն"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY – չեխական ոճ"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Էստոներեն"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Հունգարերեն"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Իսլանդերեն"</string> diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml index cd14c31cd89d..04a6dfa38bdd 100644 --- a/packages/InputDevices/res/values-in/strings.xml +++ b/packages/InputDevices/res/values-in/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandia"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroasia"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Ceko"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Gaya QWERTY Ceko"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonia"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungaria"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandia"</string> diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml index deb1f1c84e88..a60332a3f979 100644 --- a/packages/InputDevices/res/values-is/strings.xml +++ b/packages/InputDevices/res/values-is/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnskt"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Króatískt"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tékkneskt"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tékkneskt QWERTY-útlit"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Eistneskt"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungverskt"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Íslenskt"</string> diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml index 6c4adb816264..ac137e41b945 100644 --- a/packages/InputDevices/res/values-it/strings.xml +++ b/packages/InputDevices/res/values-it/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandese"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croato"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Ceco"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Stile QWERTY ceco"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estone"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungherese"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandese"</string> diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml index 3e9ffa8c1b44..544dde22c2cb 100644 --- a/packages/InputDevices/res/values-iw/strings.xml +++ b/packages/InputDevices/res/values-iw/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"פינית"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"קרואטית"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"צ\'כית"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"סגנון מקלדת QWERTY בצ\'כית"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"אסטונית"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"הונגרית"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"איסלנדית"</string> diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml index e60b50cfa964..717cbb985c74 100644 --- a/packages/InputDevices/res/values-ja/strings.xml +++ b/packages/InputDevices/res/values-ja/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"フィンランド語"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"クロアチア語"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"チェコ語"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY スタイル(チェコ語)"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"エストニア語"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ハンガリー語"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"アイスランド語"</string> diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml index 3e02764c5593..ee42b35440b3 100644 --- a/packages/InputDevices/res/values-ka/strings.xml +++ b/packages/InputDevices/res/values-ka/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ფინური"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"ხორვატიული"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"ჩეხური"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ჩეხური QWERTY სტილი"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"ესტონური"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"უნგრული"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ისლანდიური"</string> diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml index 8be590e3cd89..a2c3262559c9 100644 --- a/packages/InputDevices/res/values-km/strings.xml +++ b/packages/InputDevices/res/values-km/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ហ្វាំងឡង់"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"ក្រូអាត"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"ឆេក"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"រចនាប័ទ្ម Czech QWERTY"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"អេស្តូនី"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ហុងគ្រី"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"អ៊ីស្លង់"</string> diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml index b999498e9414..26e7ad471bad 100644 --- a/packages/InputDevices/res/values-lo/strings.xml +++ b/packages/InputDevices/res/values-lo/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ຟິນນິຊ"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"ໂຄຣເອທຽນ"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"ເຊກ"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ຮູບແບບ QWERTY ເຊກ"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"ເອສໂຕນຽນ"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ຮັງກາຣຽນ"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ໄອສແລນດິກ"</string> diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml index df2e376b7520..d0b855d10822 100644 --- a/packages/InputDevices/res/values-lt/strings.xml +++ b/packages/InputDevices/res/values-lt/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Suomių k."</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatų k."</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Čekų k."</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Čekų QWERTY stilius"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estų k."</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Vengrų k."</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandų k."</string> diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml index b65db16c8dd5..ce5e8f22f8ef 100644 --- a/packages/InputDevices/res/values-mk/strings.xml +++ b/packages/InputDevices/res/values-mk/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Фински"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Хрватски"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Чешки"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешка QWERTY-тастатура"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Естонски"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Унгарски"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Исландски"</string> diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml index 6eaeeea341ad..57748dbfa4e1 100644 --- a/packages/InputDevices/res/values-my/strings.xml +++ b/packages/InputDevices/res/values-my/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ဖင်လန်"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"ခရိုအေးရှန်း"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"ချက်"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"\'ချက် QWERTY\' ပုံစံ"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"အက်စ်စတိုးနီးယန်း"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ဟန်ဂေရီယန်း"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"အိုက်စလန်"</string> diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml index 9a0ac843cae2..6059b4c8a822 100644 --- a/packages/InputDevices/res/values-nb/strings.xml +++ b/packages/InputDevices/res/values-nb/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finsk"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisk"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tsjekkisk"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tsjekkisk QWERTY-stil"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estisk"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungarsk"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandsk"</string> diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml index 578b2aa4d137..9767f92d72e4 100644 --- a/packages/InputDevices/res/values-nl/strings.xml +++ b/packages/InputDevices/res/values-nl/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fins"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisch"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tsjechisch"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tsjechisch - QWERTY-stijl"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estlands"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongaars"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"IJslands"</string> diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml index 44f9e3f61c27..c3751197b7d2 100644 --- a/packages/InputDevices/res/values-pl/strings.xml +++ b/packages/InputDevices/res/values-pl/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fiński"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Chorwacki"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Czeski"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Styl czeskiej klawiatury QWERTY"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoński"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Węgierski"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandzki"</string> diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml index d282f2c64860..d6687c811a5b 100644 --- a/packages/InputDevices/res/values-pt-rBR/strings.xml +++ b/packages/InputDevices/res/values-pt-rBR/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandês"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tcheco"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY tcheco"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniano"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandês"</string> diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml index 052e2ad160bb..4f8a43279565 100644 --- a/packages/InputDevices/res/values-pt-rPT/strings.xml +++ b/packages/InputDevices/res/values-pt-rPT/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandês"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Checo"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY checo"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estónio"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandês"</string> diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml index d282f2c64860..d6687c811a5b 100644 --- a/packages/InputDevices/res/values-pt/strings.xml +++ b/packages/InputDevices/res/values-pt/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandês"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tcheco"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY tcheco"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniano"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandês"</string> diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml index 7d85df0ff637..1b38bdddd1a9 100644 --- a/packages/InputDevices/res/values-ro/strings.xml +++ b/packages/InputDevices/res/values-ro/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandeză"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croată"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Cehă"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Stil QWERTY cehă"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniană"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Maghiară"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandeză"</string> diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml index a1d3185a6397..501517d34176 100644 --- a/packages/InputDevices/res/values-ru/strings.xml +++ b/packages/InputDevices/res/values-ru/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"финский"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"хорватский"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"чешский"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешский (QWERTY)"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"эстонский"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"венгерский"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландский"</string> diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml index 49b990751d5d..c13661613bd5 100644 --- a/packages/InputDevices/res/values-si/strings.xml +++ b/packages/InputDevices/res/values-si/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ෆින්ලන්ත"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"ක්රොඒෂියානු"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"චෙක්"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"චෙක් QWERTY විලාසය"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"එස්තෝනියානු"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"හංගේරියානු"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"අයිස්ලන්ත"</string> diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml index 613dda057d4a..91ba472bde1e 100644 --- a/packages/InputDevices/res/values-sl/strings.xml +++ b/packages/InputDevices/res/values-sl/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finska"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvaška"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"češka"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Razpored QWERTY za češčino"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonska"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"madžarska"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandska"</string> diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml index 29e22786d2c4..3128025d6bd0 100644 --- a/packages/InputDevices/res/values-sq/strings.xml +++ b/packages/InputDevices/res/values-sq/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finlandisht"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"kroatisht"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"çekisht"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tastiera QWERTY çekisht"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonisht"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"hungarisht"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandisht"</string> diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml index 7b7076732ad1..a4c0fcaa86de 100644 --- a/packages/InputDevices/res/values-sv/strings.xml +++ b/packages/InputDevices/res/values-sv/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finskt"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatiskt"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tjeckiskt"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tjeckiskt QWERTY"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estniskt"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungerskt"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Isländskt"</string> diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml index d82959a60f78..1e5c8d7d30e1 100644 --- a/packages/InputDevices/res/values-sw/strings.xml +++ b/packages/InputDevices/res/values-sw/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Kifinlandi"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kikroeshia"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Kicheki"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Muundo wa QWERTY wa Kicheki"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Kiestonia"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Kihungari"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Kiaislandi"</string> diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml index e07b2b521614..b6caafd48790 100644 --- a/packages/InputDevices/res/values-te/strings.xml +++ b/packages/InputDevices/res/values-te/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ఫిన్నిష్"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"క్రొయేషియన్"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"చెక్"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"చెక్ QWERTY స్టయిల్"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"ఎస్టోనియన్"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"హంగేరియన్"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ఐస్లాండిక్"</string> diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml index 926c127aea38..1170f8620a39 100644 --- a/packages/InputDevices/res/values-th/strings.xml +++ b/packages/InputDevices/res/values-th/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"ฟินแลนด์"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"โครเอเชีย"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"เช็ก"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"แบบ Czech QWERTY"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"เอสโตเนีย"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ฮังการี"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ไอซ์แลนดิก"</string> diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml index 41d6a18ce6d2..97b878bde52c 100644 --- a/packages/InputDevices/res/values-tl/strings.xml +++ b/packages/InputDevices/res/values-tl/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string> diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml index 9249930ee978..7e9b5e50fbc6 100644 --- a/packages/InputDevices/res/values-tr/strings.xml +++ b/packages/InputDevices/res/values-tr/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fince"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Hırvatça"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Çekçe"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Çekçe QWERTY stili"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonca"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Macarca"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"İzlandaca"</string> diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml index 20f60461af1c..db5de4cb7a83 100644 --- a/packages/InputDevices/res/values-uk/strings.xml +++ b/packages/InputDevices/res/values-uk/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"фінська"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"хорватська"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"чеська"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чеська (QWERTY)"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"естонська"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"угорська"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ісландська"</string> diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml index 7e474302125b..bf9e5adca553 100644 --- a/packages/InputDevices/res/values-ur/strings.xml +++ b/packages/InputDevices/res/values-ur/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"فنش"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"کروشیائی"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"چیک"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY طرز"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"اسٹونیائی"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ہنگریائی"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"آئس لینڈک"</string> diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml index 62e59520e6eb..eabaa1961946 100644 --- a/packages/InputDevices/res/values-vi/strings.xml +++ b/packages/InputDevices/res/values-vi/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Tiếng Phần Lan"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Tiếng Croatia"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Tiếng Séc"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Kiểu QWERTY tiếng Séc"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Tiếng Estonia"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Tiếng Hungary"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Tiếng Ai-xơ-len"</string> diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml index 0b6ec0f6f7c4..3eb4b2c94a97 100644 --- a/packages/InputDevices/res/values-zh-rCN/strings.xml +++ b/packages/InputDevices/res/values-zh-rCN/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"芬兰语"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"克罗地亚语"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"捷克语"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"捷克语 QWERTY 样式"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"爱沙尼亚语"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"匈牙利语"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"冰岛语"</string> diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml index 22b46ca4a3cc..37cd533ca9b9 100644 --- a/packages/InputDevices/res/values-zh-rHK/strings.xml +++ b/packages/InputDevices/res/values-zh-rHK/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"芬蘭文"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"克羅地亞文"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"捷克文"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"捷克文 QWERTY 樣式"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"愛沙尼亞文"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"匈牙利文"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"冰島文"</string> diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml index abe16f360f1f..8d2de40b2d86 100644 --- a/packages/InputDevices/res/values-zh-rTW/strings.xml +++ b/packages/InputDevices/res/values-zh-rTW/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"芬蘭文"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"克羅埃西亞文"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"捷克文"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"捷克文 QWERTY 鍵盤"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"愛沙尼亞文"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"匈牙利文"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"冰島文"</string> diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml index c94225126580..ddb688ad92e9 100644 --- a/packages/InputDevices/res/values-zu/strings.xml +++ b/packages/InputDevices/res/values-zu/strings.xml @@ -26,8 +26,7 @@ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Isi-Finnish"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Isi-Croatian"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Isi-Czech"</string> - <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) --> - <skip /> + <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Isitayela se-Czech QWERTY"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Isi-Estonian"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Isi-Hungarian"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Isi-Icelandic"</string> diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java index 467a60e62e14..8e140ca27971 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java @@ -31,6 +31,7 @@ import android.location.LocationRequest; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.SystemClock; import android.print.PrintManager; @@ -250,9 +251,13 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>> Log.i(LOG_TAG, "onStartLoading() " + FusedPrintersProvider.this.hashCode()); } - mLocationManager.requestLocationUpdates(LocationRequest.create() - .setQuality(LocationRequest.POWER_LOW).setInterval(LOCATION_UPDATE_MS), this, - Looper.getMainLooper()); + mLocationManager.requestLocationUpdates( + LocationManager.FUSED_PROVIDER, + new LocationRequest.Builder(LOCATION_UPDATE_MS) + .setQuality(LocationRequest.POWER_LOW) + .build(), + new HandlerExecutor(new Handler(Looper.getMainLooper())), + this); Location lastLocation = mLocationManager.getLastLocation(); if (lastLocation != null) { diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 9acfa0da7747..9ca1814783b5 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -25,7 +25,7 @@ <string name="wifi_remembered" msgid="3266709779723179188">"تم الحفظ"</string> <string name="wifi_disconnected" msgid="7054450256284661757">"غير متصلة"</string> <string name="wifi_disabled_generic" msgid="2651916945380294607">"غير مفعّلة"</string> - <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"تعذّرت تهيئة عنوان IP"</string> + <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"تعذّر إعداد عنوان IP"</string> <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"الجهاز غير متصل بسبب انخفاض جودة الشبكة"</string> <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"تعذّر اتصال WiFi"</string> <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"حدثت مشكلة في المصادقة"</string> @@ -292,8 +292,8 @@ <string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"عندما نتوقف عن رصد أي أخطاء باستخدام المسجِّل الدائم مرة أخرى، يتعين علينا محو بيانات المسجِّل الموجودة على جهازك."</string> <string name="select_logpersist_title" msgid="447071974007104196">"تخزين بيانات المسجِّل باستمرار على الجهاز"</string> <string name="select_logpersist_dialog_title" msgid="7745193591195485594">"تحديد مخازن السجلات المؤقتة المراد تخزينها باستمرار على الجهاز"</string> - <string name="select_usb_configuration_title" msgid="6339801314922294586">"حدد تهيئة USB"</string> - <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"حدد تهيئة USB"</string> + <string name="select_usb_configuration_title" msgid="6339801314922294586">"حدد إعداد USB"</string> + <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"حدد إعداد USB"</string> <string name="allow_mock_location" msgid="2102650981552527884">"السماح بمواقع وهمية"</string> <string name="allow_mock_location_summary" msgid="179780881081354579">"السماح بمواقع وهمية"</string> <string name="debug_view_attributes" msgid="3539609843984208216">"تفعيل فحص سمة العرض"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java index 126f9b91b0d2..41d6afc9c234 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java @@ -46,6 +46,7 @@ import com.android.settingslib.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; /** * MediaDevice represents a media device(such like Bluetooth device, cast device and phone device). @@ -354,6 +355,13 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { } /** + * Gets the supported features of the route. + */ + public List<String> getFeatures() { + return mRouteInfo.getFeatures(); + } + + /** * Check if it is CarKit device * @return true if it is CarKit device */ diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 06a97b17a73c..57c15e3211e1 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -772,5 +772,12 @@ </intent-filter> </receiver> + <receiver android:name=".media.dialog.MediaOutDialogReceiver" + android:exported="true"> + <intent-filter> + <action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" /> + </intent-filter> + </receiver> + </application> </manifest> diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_background.xml new file mode 100644 index 000000000000..3ceb0f6ac06a --- /dev/null +++ b/packages/SystemUI/res/drawable/media_output_dialog_background.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> + +<inset xmlns:android="http://schemas.android.com/apk/res/android"> + <shape android:shape="rectangle"> + <corners android:radius="8dp" /> + <solid android:color="?android:attr/colorBackground" /> + </shape> +</inset> diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml new file mode 100644 index 000000000000..0229e6e9d4dd --- /dev/null +++ b/packages/SystemUI/res/layout/media_output_dialog.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/media_output_dialog" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="94dp" + android:gravity="start|center_vertical" + android:paddingStart="16dp" + android:orientation="horizontal"> + <ImageView + android:id="@+id/header_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingEnd="16dp"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginEnd="16dp" + android:orientation="vertical"> + <TextView + android:id="@+id/header_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textSize="20sp"/> + + <TextView + android:id="@+id/header_subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:fontFamily="roboto-regular" + android:textSize="14sp"/> + + </LinearLayout> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/listDivider"/> + + <LinearLayout + android:id="@+id/device_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="start|center_vertical" + android:orientation="vertical"> + + <View + android:layout_width="match_parent" + android:layout_height="12dp"/> + + <include + layout="@layout/media_output_list_item" + android:id="@+id/group_item_controller" + android:visibility="gone"/> + + <View + android:id="@+id/group_item_divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/listDivider" + android:visibility="gone"/> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/list_result" + android:scrollbars="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:overScrollMode="never"/> + + <View + android:id="@+id/list_bottom_padding" + android:layout_width="match_parent" + android:layout_height="12dp"/> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/listDivider"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/stop" + style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" + android:layout_width="wrap_content" + android:layout_height="64dp" + android:text="@string/keyboard_key_media_stop" + android:visibility="gone"/> + + <Space + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + <Button + android:id="@+id/done" + style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" + android:layout_width="wrap_content" + android:layout_height="64dp" + android:layout_marginEnd="0dp" + android:text="@string/inline_done_button"/> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml new file mode 100644 index 000000000000..92d0858a1a31 --- /dev/null +++ b/packages/SystemUI/res/layout/media_output_list_item.xml @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> + +<FrameLayout + android:id="@+id/device_container" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="64dp"> + + <FrameLayout + android:layout_width="36dp" + android:layout_height="36dp" + android:layout_gravity="center_vertical" + android:layout_marginStart="16dp"> + <ImageView + android:id="@+id/title_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center"/> + </FrameLayout> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginStart="68dp" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:attr/textColorPrimary" + android:textSize="14sp"/> + + <RelativeLayout + android:id="@+id/two_line_layout" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:layout_marginStart="52dp" + android:layout_marginEnd="69dp" + android:layout_marginTop="10dp"> + <TextView + android:id="@+id/two_line_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="15dp" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:attr/textColorPrimary" + android:textSize="14sp"/> + <TextView + android:id="@+id/subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="15dp" + android:layout_marginBottom="7dp" + android:layout_alignParentBottom="true" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:attr/textColorSecondary" + android:textSize="12sp" + android:fontFamily="roboto-regular" + android:visibility="gone"/> + <ProgressBar + android:id="@+id/volume_indeterminate_progress" + style="@*android:style/Widget.Material.ProgressBar.Horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="15dp" + android:layout_marginBottom="1dp" + android:layout_alignParentBottom="true" + android:indeterminate="true" + android:indeterminateOnly="true" + android:visibility="gone"/> + <SeekBar + android:id="@+id/volume_seekbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true"/> + </RelativeLayout> + + <View + android:layout_width="1dp" + android:layout_height="36dp" + android:layout_marginEnd="68dp" + android:layout_gravity="right|center_vertical" + android:background="?android:attr/listDivider" + android:visibility="gone"/> + + <ImageView + android:id="@+id/end_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="right|center_vertical" + android:layout_marginEnd="24dp" + android:visibility="gone"/> +</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 66304013da46..981a95312736 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -29,6 +29,7 @@ <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.volume.VolumeUI</item> <item>com.android.systemui.statusbar.tv.TvStatusBar</item> + <item>com.android.systemui.statusbar.tv.TvNotificationPanel</item> <item>com.android.systemui.usb.StorageNotification</item> <item>com.android.systemui.power.PowerUI</item> <item>com.android.systemui.media.RingtonePlayer</item> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 875fe1471b1c..98e8cde40275 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1366,4 +1366,11 @@ <dimen name="config_rounded_mask_size">@*android:dimen/rounded_corner_radius</dimen> <dimen name="config_rounded_mask_size_top">@*android:dimen/rounded_corner_radius_top</dimen> <dimen name="config_rounded_mask_size_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen> + + <!-- Output switcher panel related dimensions --> + <dimen name="media_output_dialog_padding_top">11dp</dimen> + <dimen name="media_output_dialog_list_max_height">364dp</dimen> + <dimen name="media_output_dialog_header_album_icon_size">52dp</dimen> + <dimen name="media_output_dialog_header_back_icon_size">36dp</dimen> + <dimen name="media_output_dialog_icon_corner_radius">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 2f018b9239b5..d815681c8736 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -170,6 +170,9 @@ <item type="id" name="accessibility_action_controls_move_before" /> <item type="id" name="accessibility_action_controls_move_after" /> + <item type="id" name="accessibility_action_qs_move_to_position" /> + <item type="id" name="accessibility_action_qs_add_to_position" /> + <!-- Accessibility actions for PIP --> <item type="id" name="action_pip_resize" /> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index cca70f9aa518..f69314977a21 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -107,7 +107,7 @@ <string name="status_bar_settings_notifications">Notifications</string> <!-- Separator for PLMN and SPN in network name. --> - <string name="status_bar_network_name_separator" translatable="false">|</string> + <string name="status_bar_network_name_separator" translatable="false"> - </string> <!-- Network connection string for Bluetooth Reverse Tethering --> <string name="bluetooth_tethered">Bluetooth tethered</string> @@ -2260,23 +2260,26 @@ <!-- SysUI Tuner: Other section --> <string name="other">Other</string> - <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string> + <!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_remove_tile_action">remove tile</string> - <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_add_tile_label"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g>. Double tap to add.</string> + <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to end" in screen readers [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_add_action">add tile to end</string> - <!-- Accessibility description of option to move QS tile [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_move_tile">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string> + <!-- Accessibility action for context menu to move QS tile [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_start_move">Move tile</string> - <!-- Accessibility description of option to remove QS tile [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_remove_tile">Remove <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string> + <!-- Accessibility action for context menu to add QS tile [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_start_add">Add tile</string> - <!-- Accessibility action when QS tile is to be added [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_tile_add">Add <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string> + <!-- Accessibility description when QS tile is to be moved, indicating the destination position [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_move_to_position">Move to <xliff:g id="position" example="5">%1$d</xliff:g></string> - <!-- Accessibility action when QS tile is to be moved [CHAR LIMIT=NONE] --> - <string name="accessibility_qs_edit_tile_move">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string> + <!-- Accessibility description when QS tile is to be added, indicating the destination position [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_add_to_position">Add to position <xliff:g id="position" example="5">%1$d</xliff:g></string> + + <!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string> <!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] --> <string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string> @@ -2799,4 +2802,19 @@ <string name="udfps_hbm_enable_command" translatable="false"></string> <!-- Device-specific payload for disabling the high-brightness mode --> <string name="udfps_hbm_disable_command" translatable="false"></string> + + <!-- Title for the media output group dialog with media related devices [CHAR LIMIT=50] --> + <string name="media_output_dialog_add_output">Add outputs</string> + <!-- Title for the media output slice with group devices [CHAR LIMIT=50] --> + <string name="media_output_dialog_group">Group</string> + <!-- Summary for media output group with only one device which is active [CHAR LIMIT=NONE] --> + <string name="media_output_dialog_single_device">1 device selected</string> + <!-- Summary for media output group with the active device count [CHAR LIMIT=NONE] --> + <string name="media_output_dialog_multiple_devices"><xliff:g id="count" example="2">%1$d</xliff:g> devices selected</string> + <!-- Summary for disconnected status [CHAR LIMIT=50] --> + <string name="media_output_dialog_disconnected"><xliff:g id="device_name" example="My device">%1$s</xliff:g> (disconnected)</string> + <!-- Summary for connecting error message [CHAR LIMIT=NONE] --> + <string name="media_output_dialog_connect_failed">Couldn\'t connect. Try again.</string> + <!-- Title for pairing item [CHAR LIMIT=60] --> + <string name="media_output_dialog_pairing_new">Pair new device</string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 58563f49dce4..2b0a963bff18 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -388,6 +388,10 @@ <item name="android:windowIsFloating">true</item> </style> + <style name="Theme.SystemUI.Dialog.MediaOutput"> + <item name="android:windowBackground">@drawable/media_output_dialog_background</item> + </style> + <style name="QSBorderlessButton"> <item name="android:padding">12dp</item> <item name="android:background">@drawable/qs_btn_borderless_rect</item> @@ -735,5 +739,4 @@ * Title: headline, medium 20sp * Message: body, 16 sp --> <style name="Theme.ControlsRequestDialog" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert"/> - </resources> diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index f663d1a0d77c..1ebe64860a98 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -105,6 +105,9 @@ public class ScreenDecorations extends SystemUI implements Tunable { public static final String SIZE = "sysui_rounded_size"; public static final String PADDING = "sysui_rounded_content_padding"; + // Provide a way for factory to disable ScreenDecorations to run the Display tests. + private static final boolean DEBUG_DISABLE_SCREEN_DECORATIONS = + SystemProperties.getBoolean("debug.disable_screen_decorations", false); private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS = SystemProperties.getBoolean("debug.screenshot_rounded_corners", false); private static final boolean VERBOSE = false; @@ -204,6 +207,10 @@ public class ScreenDecorations extends SystemUI implements Tunable { @Override public void start() { + if (DEBUG_DISABLE_SCREEN_DECORATIONS) { + Log.i(TAG, "ScreenDecorations is disabled"); + return; + } mHandler = startHandlerThread(); mHandler.post(this::startOnScreenDecorationsThread); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index ea18b11413ef..b29eff6c23ea 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -57,6 +57,7 @@ import com.android.systemui.statusbar.CommandQueue; import java.util.List; import javax.inject.Inject; +import javax.inject.Provider; /** * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the @@ -72,6 +73,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private final CommandQueue mCommandQueue; private final StatusBarStateController mStatusBarStateController; private final Injector mInjector; + private final Provider<UdfpsController> mUdfpsControllerFactory; // TODO: These should just be saved from onSaveState private SomeArgs mCurrentDialogArgs; @@ -237,6 +239,34 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } } + /** + * Requests fingerprint scan. + * + * @param screenX X position of long press + * @param screenY Y position of long press + */ + public void onAodInterrupt(int screenX, int screenY) { + if (mUdfpsController == null) { + return; + } + mUdfpsController.onAodInterrupt(screenX, screenY); + } + + /** + * Cancel a fingerprint scan. + * + * The sensor that triggers an AOD interrupt for fingerprint doesn't give + * ACTION_UP/ACTION_CANCEL events, so the scan needs to be cancelled manually. This should be + * called when authentication either succeeds or fails. Failing to cancel the scan will leave + * the screen in high brightness mode. + */ + private void onCancelAodInterrupt() { + if (mUdfpsController == null) { + return; + } + mUdfpsController.onCancelAodInterrupt(); + } + private void sendResultAndCleanUp(@DismissedReason int reason, @Nullable byte[] credentialAttestation) { if (mReceiver == null) { @@ -263,17 +293,21 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Inject public AuthController(Context context, CommandQueue commandQueue, - StatusBarStateController statusBarStateController) { - this(context, commandQueue, statusBarStateController, new Injector()); + StatusBarStateController statusBarStateController, + Provider<UdfpsController> udfpsControllerFactory) { + this(context, commandQueue, statusBarStateController, new Injector(), + udfpsControllerFactory); } @VisibleForTesting AuthController(Context context, CommandQueue commandQueue, - StatusBarStateController statusBarStateController, Injector injector) { + StatusBarStateController statusBarStateController, Injector injector, + Provider<UdfpsController> udfpsControllerFactory) { super(context); mCommandQueue = commandQueue; mStatusBarStateController = statusBarStateController; mInjector = injector; + mUdfpsControllerFactory = udfpsControllerFactory; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); @@ -294,7 +328,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, fpm.getSensorProperties(); for (FingerprintSensorProperties props : fingerprintSensorProperties) { if (props.sensorType == FingerprintSensorProperties.TYPE_UDFPS) { - mUdfpsController = new UdfpsController(mContext, mStatusBarStateController); + mUdfpsController = mUdfpsControllerFactory.get(); break; } } @@ -341,6 +375,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Override public void onBiometricAuthenticated() { mCurrentDialog.onAuthenticationSucceeded(); + onCancelAodInterrupt(); } @Override @@ -390,6 +425,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage); mCurrentDialog.onError(errorMessage); } + onCancelAodInterrupt(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 82fb80892ab1..06c190f1964c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -46,6 +46,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import java.io.FileWriter; import java.io.IOException; +import javax.inject.Inject; + /** * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events, * and coordinates triggering of the high-brightness mode (HBM). @@ -54,6 +56,7 @@ class UdfpsController implements DozeReceiver { private static final String TAG = "UdfpsController"; // Gamma approximation for the sRGB color space. private static final float DISPLAY_GAMMA = 2.2f; + private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000; private final FingerprintManager mFingerprintManager; private final WindowManager mWindowManager; @@ -80,6 +83,13 @@ class UdfpsController implements DozeReceiver { private final float mDefaultBrightness; private boolean mIsOverlayShowing; + // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when + // to turn off high brightness mode. To get around this limitation, the state of the AOD + // interrupt is being tracked and a timeout is used as a last resort to turn off high brightness + // mode. + private boolean mIsAodInterruptActive; + private final Runnable mAodInterruptTimeoutAction = this::onCancelAodInterrupt; + public class UdfpsOverlayController extends IUdfpsOverlayController.Stub { @Override public void showUdfpsOverlay() { @@ -126,6 +136,7 @@ class UdfpsController implements DozeReceiver { } }; + @Inject UdfpsController(@NonNull Context context, @NonNull StatusBarStateController statusBarStateController) { mFingerprintManager = context.getSystemService(FingerprintManager.class); @@ -240,6 +251,40 @@ class UdfpsController implements DozeReceiver { return BrightnessSynchronizer.brightnessFloatToInt(scrimOpacity); } + /** + * Request fingerprint scan. + * + * This is intented to be called in response to a sensor that triggers an AOD interrupt for the + * fingerprint sensor. + */ + void onAodInterrupt(int screenX, int screenY) { + if (mIsAodInterruptActive) { + return; + } + mIsAodInterruptActive = true; + // Since the sensor that triggers the AOD interrupt doesn't provide ACTION_UP/ACTION_CANCEL, + // we need to be careful about not letting the screen accidentally remain in high brightness + // mode. As a mitigation, queue a call to cancel the fingerprint scan. + mHandler.postDelayed(mAodInterruptTimeoutAction, AOD_INTERRUPT_TIMEOUT_MILLIS); + // using a hard-coded value for major and minor until it is available from the sensor + onFingerDown(screenX, screenY, 13.0f, 13.0f); + } + + /** + * Cancel fingerprint scan. + * + * This is intented to be called after the fingerprint scan triggered by the AOD interrupt + * either succeeds or fails. + */ + void onCancelAodInterrupt() { + if (!mIsAodInterruptActive) { + return; + } + mHandler.removeCallbacks(mAodInterruptTimeoutAction); + mIsAodInterruptActive = false; + onFingerUp(); + } + private void onFingerDown(int x, int y, float minor, float major) { mView.setScrimAlpha(computeScrimOpacity()); mView.showScrimAndDot(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 99875f8675d8..b6106025a17a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -458,6 +458,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mSavedBubbleKeysPerUser = new SparseSetArray<>(); mCurrentUserId = mNotifUserManager.getCurrentUserId(); + mBubbleData.setCurrentUserId(mCurrentUserId); + mNotifUserManager.addUserChangedListener( new NotificationLockscreenUserManager.UserChangedListener() { @Override @@ -466,6 +468,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleData.dismissAll(DISMISS_USER_CHANGED); BubbleController.this.restoreBubbles(newUserId); mCurrentUserId = newUserId; + mBubbleData.setCurrentUserId(newUserId); } }); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index a747db680b2e..bab18ec053ee 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -61,6 +62,8 @@ public class BubbleData { private BubbleLoggerImpl mLogger = new BubbleLoggerImpl(); + private int mCurrentUserId; + private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleData" : TAG_BUBBLES; private static final Comparator<Bubble> BUBBLES_BY_SORT_KEY_DESCENDING = @@ -617,6 +620,10 @@ public class BubbleData { mStateChange.selectionChanged = true; } + void setCurrentUserId(int uid) { + mCurrentUserId = uid; + } + /** * Logs the bubble UI event. * @@ -634,7 +641,9 @@ public class BubbleData { if (provider == null) { mLogger.logStackUiChanged(packageName, action, bubbleCount, normalX, normalY); } else if (provider.getKey().equals(BubbleOverflow.KEY)) { - mLogger.logShowOverflow(packageName, action, bubbleCount, normalX, normalY); + if (action == SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED) { + mLogger.logShowOverflow(packageName, mCurrentUserId); + } } else { mLogger.logBubbleUiChanged((Bubble) provider, packageName, action, bubbleCount, normalX, normalY, bubbleIndex); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java index 1e6eb8c5c89b..86ba8c5c7192 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java @@ -53,7 +53,10 @@ public interface BubbleLogger extends UiEventLogger { BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK(489), @UiEvent(doc = "User blocked notification from bubbling, remove bubble from overflow.") - BUBBLE_OVERFLOW_REMOVE_BLOCKED(490); + BUBBLE_OVERFLOW_REMOVE_BLOCKED(490), + + @UiEvent(doc = "User selected the overflow.") + BUBBLE_OVERFLOW_SELECTED(600); private final int mId; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java index d702cc4e0062..2d90c8626a0e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.bubbles; +import android.os.UserHandle; + import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.shared.system.SysUiStatsLog; @@ -31,11 +33,7 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger * @param e UI event */ public void log(Bubble b, UiEventEnum e) { - if (b.getInstanceId() == null) { - // Added from persistence -- TODO log this with specific event? - return; - } - logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId()); + super.log(e, b.getUser().getIdentifier(), b.getPackageName()); } /** @@ -82,20 +80,9 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger false /* isAppForeground (unused) */); } - void logShowOverflow(String packageName, int action, int bubbleCount, float normalX, - float normalY) { - SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, - packageName, - BubbleOverflow.KEY /* notification channel */, - 0 /* notification ID */, - 0 /* bubble position */, - bubbleCount, - action, - normalX, - normalY, - false /* unread bubble */, - false /* on-going bubble */, - false /* isAppForeground (unused) */); + void logShowOverflow(String packageName, int currentUserId) { + super.log(BubbleLogger.Event.BUBBLE_OVERFLOW_SELECTED, currentUserId, + packageName); } void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java index 6e8d63b2c516..307362fe790e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java @@ -18,6 +18,7 @@ package com.android.systemui.dagger; import android.content.BroadcastReceiver; +import com.android.systemui.media.dialog.MediaOutDialogReceiver; import com.android.systemui.screenshot.ActionProxyReceiver; import com.android.systemui.screenshot.DeleteScreenshotReceiver; import com.android.systemui.screenshot.SmartActionsReceiver; @@ -59,4 +60,13 @@ public abstract class DefaultBroadcastReceiverBinder { public abstract BroadcastReceiver bindSmartActionsReceiver( SmartActionsReceiver broadcastReceiver); + /** + * + */ + @Binds + @IntoMap + @ClassKey(MediaOutDialogReceiver.class) + public abstract BroadcastReceiver bindMediaOutDialogReceiver( + MediaOutDialogReceiver broadcastReceiver); + } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index 3a5ce4d82540..1f6288a94ad4 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -35,6 +35,7 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher; import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.InstantAppNotifier; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.tv.TvNotificationPanel; import com.android.systemui.statusbar.tv.TvStatusBar; import com.android.systemui.theme.ThemeOverlayController; import com.android.systemui.toast.ToastUI; @@ -156,6 +157,12 @@ public abstract class SystemUIBinder { @ClassKey(TvStatusBar.class) public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui); + /** Inject into TvNotificationPanel. */ + @Binds + @IntoMap + @ClassKey(TvNotificationPanel.class) + public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui); + /** Inject into VolumeUI. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 99d2651ae9ea..424a8246b278 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -339,6 +339,7 @@ public class DozeLog implements Dumpable { case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen"; case REASON_SENSOR_WAKE_UP: return "wakeup"; case REASON_SENSOR_TAP: return "tap"; + case REASON_SENSOR_UDFPS_LONG_PRESS: return "udfps"; default: throw new IllegalArgumentException("invalid reason: " + pulseReason); } } @@ -347,7 +348,8 @@ public class DozeLog implements Dumpable { @IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION, PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP, PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP, - PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP}) + PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP, + REASON_SENSOR_UDFPS_LONG_PRESS}) public @interface Reason {} public static final int PULSE_REASON_NONE = -1; public static final int PULSE_REASON_INTENT = 0; @@ -360,6 +362,7 @@ public class DozeLog implements Dumpable { public static final int REASON_SENSOR_WAKE_UP = 7; public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8; public static final int REASON_SENSOR_TAP = 9; + public static final int REASON_SENSOR_UDFPS_LONG_PRESS = 10; - public static final int TOTAL_REASONS = 10; + public static final int TOTAL_REASONS = 11; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 524d9c8536b8..028870f3815e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -150,6 +150,15 @@ public class DozeSensors { true /* reports touch coordinates */, true /* touchscreen */, dozeLog), + new TriggerSensor( + findSensorWithType(config.udfpsLongPressSensorType()), + Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, + false /* settingDef */, + true /* configured */, + DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, + true /* reports touch coordinates */, + true /* touchscreen */, + dozeLog), new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY), Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 8364b486c8d7..45e5c614ea58 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -39,6 +39,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.dagger.DozeScope; @@ -93,6 +94,7 @@ public class DozeTriggers implements DozeMachine.Part { private final DockManager mDockManager; private final ProximitySensor.ProximityCheck mProxCheck; private final BroadcastDispatcher mBroadcastDispatcher; + private final AuthController mAuthController; private long mNotificationPulseTime; private boolean mPulsePending; @@ -165,7 +167,7 @@ public class DozeTriggers implements DozeMachine.Part { WakeLock wakeLock, DockManager dockManager, ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck, DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher, - SecureSettings secureSettings) { + SecureSettings secureSettings, AuthController authController) { mContext = context; mDozeHost = dozeHost; mConfig = config; @@ -181,6 +183,7 @@ public class DozeTriggers implements DozeMachine.Part { mProxCheck = proxCheck; mDozeLog = dozeLog; mBroadcastDispatcher = broadcastDispatcher; + mAuthController = authController; } @Override @@ -256,6 +259,7 @@ public class DozeTriggers implements DozeMachine.Part { boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS; boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP; boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN; + boolean isUdfpsLongPress = pulseReason == DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0; if (isWakeDisplay) { @@ -281,6 +285,11 @@ public class DozeTriggers implements DozeMachine.Part { gentleWakeUp(pulseReason); } else if (isPickup) { gentleWakeUp(pulseReason); + } else if (isUdfpsLongPress) { + gentleWakeUp(pulseReason); + // Since the gesture won't be received by the UDFPS view, manually inject an + // event. + mAuthController.onAodInterrupt((int) screenX, (int) screenY); } else { mDozeHost.extendPulse(pulseReason); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt index f01713fb5f6c..f695622b943a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt @@ -52,9 +52,12 @@ class MediaSessionBasedFilter @Inject constructor( private val packageControllers: LinkedHashMap<String, MutableList<MediaController>> = LinkedHashMap() - // Keep track of the key used for the session tokens. This information is used to know when + // Keep track of the key used for the session tokens. This information is used to know when to // dispatch a removed event so that a media object for a local session will be removed. - private val keyedTokens: MutableMap<String, MutableList<MediaSession.Token>> = mutableMapOf() + private val keyedTokens: MutableMap<String, MutableSet<MediaSession.Token>> = mutableMapOf() + + // Keep track of which media session tokens have associated notifications. + private val tokensWithNotifications: MutableSet<MediaSession.Token> = mutableSetOf() private val sessionListener = object : MediaSessionManager.OnActiveSessionsChangedListener { override fun onActiveSessionsChanged(controllers: List<MediaController>) { @@ -90,6 +93,9 @@ class MediaSessionBasedFilter @Inject constructor( */ override fun onMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { backgroundExecutor.execute { + info.token?.let { + tokensWithNotifications.add(it) + } val isMigration = oldKey != null && key != oldKey if (isMigration) { keyedTokens.remove(oldKey)?.let { removed -> keyedTokens.put(key, removed) } @@ -99,7 +105,7 @@ class MediaSessionBasedFilter @Inject constructor( tokens -> tokens.add(info.token) } ?: run { - val tokens = mutableListOf(info.token) + val tokens = mutableSetOf(info.token) keyedTokens.put(key, tokens) } } @@ -110,7 +116,8 @@ class MediaSessionBasedFilter @Inject constructor( } // Limiting search to only apps with a single remote session. val remote = if (remoteControllers?.size == 1) remoteControllers.firstOrNull() else null - if (isMigration || remote == null || remote.sessionToken == info.token) { + if (isMigration || remote == null || remote.sessionToken == info.token || + !tokensWithNotifications.contains(remote.sessionToken)) { // Not filtering in this case. Passing the event along to listeners. dispatchMediaDataLoaded(key, oldKey, info) } else { @@ -159,5 +166,6 @@ class MediaSessionBasedFilter @Inject constructor( packageControllers.put(controller.packageName, tokens) } } + tokensWithNotifications.retainAll(controllers.map { it.sessionToken }) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutDialogReceiver.kt new file mode 100644 index 000000000000..d60771394ded --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutDialogReceiver.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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.media.dialog + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.text.TextUtils +import com.android.settingslib.media.MediaOutputSliceConstants +import javax.inject.Inject + +/** + * BroadcastReceiver for handling media output intent + */ +class MediaOutDialogReceiver @Inject constructor( + private var mediaOutputDialogFactory: MediaOutputDialogFactory +) : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (TextUtils.equals(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG, + intent.action)) { + mediaOutputDialogFactory.create( + intent.getStringExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME), false) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java new file mode 100644 index 000000000000..9fc64d51cdf7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2020 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.media.dialog; + +import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; + +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; + +import com.android.settingslib.Utils; +import com.android.settingslib.media.LocalMediaManager.MediaDeviceState; +import com.android.settingslib.media.MediaDevice; +import com.android.systemui.R; + +import java.util.List; + +/** + * Adapter for media output dialog. + */ +public class MediaOutputAdapter extends MediaOutputBaseAdapter { + + private static final String TAG = "MediaOutputAdapter"; + private static final int PAIR_NEW = 1; + + public MediaOutputAdapter(MediaOutputController controller) { + super(controller); + } + + @Override + public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, + int viewType) { + super.onCreateViewHolder(viewGroup, viewType); + + return new MediaDeviceViewHolder(mHolderView); + } + + @Override + public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) { + if (mController.isZeroMode() && position == (mController.getMediaDevices().size())) { + viewHolder.onBind(PAIR_NEW); + } else if (position < (mController.getMediaDevices().size())) { + viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position)); + } else { + Log.d(TAG, "Incorrect position: " + position); + } + } + + @Override + public int getItemCount() { + if (mController.isZeroMode()) { + // Add extra one for "pair new" + return mController.getMediaDevices().size() + 1; + } + return mController.getMediaDevices().size(); + } + + void onItemClick(MediaDevice device) { + mController.connectDevice(device); + device.setState(MediaDeviceState.STATE_CONNECTING); + notifyDataSetChanged(); + } + + void onItemClick(int customizedItem) { + if (customizedItem == PAIR_NEW) { + mController.launchBluetoothPairing(); + } + } + + @Override + CharSequence getItemTitle(MediaDevice device) { + if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE + && !device.isConnected()) { + final CharSequence deviceName = device.getName(); + // Append status to title only for the disconnected Bluetooth device. + final SpannableString spannableTitle = new SpannableString( + mContext.getString(R.string.media_output_dialog_disconnected, deviceName)); + spannableTitle.setSpan(new ForegroundColorSpan( + Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorSecondary)), + deviceName.length(), + spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE); + return spannableTitle; + } + return super.getItemTitle(device); + } + + class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder { + + MediaDeviceViewHolder(View view) { + super(view); + } + + @Override + void onBind(MediaDevice device) { + super.onBind(device); + if (mController.isTransferring()) { + if (device.getState() == MediaDeviceState.STATE_CONNECTING + && !mController.hasAdjustVolumeUserRestriction()) { + setTwoLineLayout(device, true); + mProgressBar.setVisibility(View.VISIBLE); + mSeekBar.setVisibility(View.GONE); + mSubTitleText.setVisibility(View.GONE); + } else { + setSingleLineLayout(getItemTitle(device), false); + } + } else { + // Set different layout for each device + if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { + setTwoLineLayout(device, false); + mSubTitleText.setVisibility(View.VISIBLE); + mSeekBar.setVisibility(View.GONE); + mProgressBar.setVisibility(View.GONE); + mSubTitleText.setText(R.string.media_output_dialog_connect_failed); + mFrameLayout.setOnClickListener(v -> onItemClick(device)); + } else if (!mController.hasAdjustVolumeUserRestriction() + && isCurrentConnected(device)) { + setTwoLineLayout(device, true); + mSeekBar.setVisibility(View.VISIBLE); + mProgressBar.setVisibility(View.GONE); + mSubTitleText.setVisibility(View.GONE); + initSeekbar(device); + } else { + setSingleLineLayout(getItemTitle(device), false); + mFrameLayout.setOnClickListener(v -> onItemClick(device)); + } + } + } + + @Override + void onBind(int customizedItem) { + if (customizedItem == PAIR_NEW) { + setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new), + false); + final Drawable d = mContext.getDrawable(R.drawable.ic_add); + d.setColorFilter(new PorterDuffColorFilter( + Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN)); + mTitleIcon.setImageDrawable(d); + mFrameLayout.setOnClickListener(v -> onItemClick(PAIR_NEW)); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java new file mode 100644 index 000000000000..7579c25b030a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 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.media.dialog; + +import android.content.Context; +import android.graphics.Typeface; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settingslib.media.MediaDevice; +import com.android.systemui.R; + +/** + * Base adapter for media output dialog. + */ +public abstract class MediaOutputBaseAdapter extends + RecyclerView.Adapter<MediaOutputBaseAdapter.MediaDeviceBaseViewHolder> { + + private static final String FONT_SELECTED_TITLE = "sans-serif-medium"; + private static final String FONT_TITLE = "sans-serif"; + + final MediaOutputController mController; + + private boolean mIsDragging; + + Context mContext; + View mHolderView; + + public MediaOutputBaseAdapter(MediaOutputController controller) { + mController = controller; + mIsDragging = false; + } + + @Override + public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, + int viewType) { + mContext = viewGroup.getContext(); + mHolderView = LayoutInflater.from(mContext).inflate(R.layout.media_output_list_item, + viewGroup, false); + + return null; + } + + CharSequence getItemTitle(MediaDevice device) { + return device.getName(); + } + + boolean isCurrentConnected(MediaDevice device) { + return TextUtils.equals(device.getId(), + mController.getCurrentConnectedMediaDevice().getId()); + } + + boolean isDragging() { + return mIsDragging; + } + + /** + * ViewHolder for binding device view. + */ + abstract class MediaDeviceBaseViewHolder extends RecyclerView.ViewHolder { + final FrameLayout mFrameLayout; + final TextView mTitleText; + final TextView mTwoLineTitleText; + final TextView mSubTitleText; + final ImageView mTitleIcon; + final ImageView mEndIcon; + final ProgressBar mProgressBar; + final SeekBar mSeekBar; + final RelativeLayout mTwoLineLayout; + + MediaDeviceBaseViewHolder(View view) { + super(view); + mFrameLayout = view.requireViewById(R.id.device_container); + mTitleText = view.requireViewById(R.id.title); + mSubTitleText = view.requireViewById(R.id.subtitle); + mTwoLineLayout = view.requireViewById(R.id.two_line_layout); + mTwoLineTitleText = view.requireViewById(R.id.two_line_title); + mTitleIcon = view.requireViewById(R.id.title_icon); + mEndIcon = view.requireViewById(R.id.end_icon); + mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress); + mSeekBar = view.requireViewById(R.id.volume_seekbar); + } + + void onBind(MediaDevice device) { + mTitleIcon.setImageIcon(mController.getDeviceIconCompat(device).toIcon(mContext)); + } + + void onBind(int customizedItem) { } + + void setSingleLineLayout(CharSequence title, boolean bFocused) { + mTitleText.setVisibility(View.VISIBLE); + mTwoLineLayout.setVisibility(View.GONE); + mTitleText.setText(title); + if (bFocused) { + mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL)); + } else { + mTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL)); + } + } + + void setTwoLineLayout(MediaDevice device, boolean bFocused) { + mTitleText.setVisibility(View.GONE); + mTwoLineLayout.setVisibility(View.VISIBLE); + mTwoLineTitleText.setText(getItemTitle(device)); + if (bFocused) { + mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, + Typeface.NORMAL)); + } else { + mTwoLineTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL)); + } + } + + void initSeekbar(MediaDevice device) { + mSeekBar.setMax(device.getMaxVolume()); + mSeekBar.setMin(0); + if (mSeekBar.getProgress() != device.getCurrentVolume()) { + mSeekBar.setProgress(device.getCurrentVolume()); + } + mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (device == null || !fromUser) { + return; + } + mController.adjustVolume(device, progress); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + mIsDragging = true; + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + mIsDragging = false; + } + }); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java new file mode 100644 index 000000000000..781bf8d74d88 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2020 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.media.dialog; + +import static android.view.WindowInsets.Type.navigationBars; +import static android.view.WindowInsets.Type.statusBars; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.Window; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.VisibleForTesting; +import androidx.core.graphics.drawable.IconCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settingslib.R; +import com.android.systemui.statusbar.phone.SystemUIDialog; + +/** + * Base dialog for media output UI + */ +public abstract class MediaOutputBaseDialog extends SystemUIDialog implements + MediaOutputController.Callback { + + private static final String TAG = "MediaOutputDialog"; + + private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); + private final RecyclerView.LayoutManager mLayoutManager; + + final Context mContext; + final MediaOutputController mMediaOutputController; + + @VisibleForTesting + View mDialogView; + private TextView mHeaderTitle; + private TextView mHeaderSubtitle; + private ImageView mHeaderIcon; + private RecyclerView mDevicesRecyclerView; + private LinearLayout mDeviceListLayout; + private Button mDoneButton; + private Button mStopButton; + private View mListBottomPadding; + private int mListMaxHeight; + + MediaOutputBaseAdapter mAdapter; + FrameLayout mGroupItemController; + View mGroupDivider; + + private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> { + // Set max height for list + if (mDeviceListLayout.getHeight() > mListMaxHeight) { + ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams(); + params.height = mListMaxHeight; + mDeviceListLayout.setLayoutParams(params); + } + }; + + public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) { + super(context, R.style.Theme_SystemUI_Dialog_MediaOutput); + mContext = context; + mMediaOutputController = mediaOutputController; + mLayoutManager = new LinearLayoutManager(mContext); + mListMaxHeight = context.getResources().getDimensionPixelSize( + R.dimen.media_output_dialog_list_max_height); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mDialogView = LayoutInflater.from(mContext).inflate(R.layout.media_output_dialog, null); + final Window window = getWindow(); + final WindowManager.LayoutParams lp = window.getAttributes(); + lp.gravity = Gravity.BOTTOM; + // Config insets to make sure the layout is above the navigation bar + lp.setFitInsetsTypes(statusBars() | navigationBars()); + lp.setFitInsetsSides(WindowInsets.Side.all()); + lp.setFitInsetsIgnoringVisibility(true); + window.setAttributes(lp); + window.setContentView(mDialogView); + window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + mHeaderTitle = mDialogView.requireViewById(R.id.header_title); + mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle); + mHeaderIcon = mDialogView.requireViewById(R.id.header_icon); + mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result); + mGroupItemController = mDialogView.requireViewById(R.id.group_item_controller); + mGroupDivider = mDialogView.requireViewById(R.id.group_item_divider); + mDeviceListLayout = mDialogView.requireViewById(R.id.device_list); + mDoneButton = mDialogView.requireViewById(R.id.done); + mStopButton = mDialogView.requireViewById(R.id.stop); + mListBottomPadding = mDialogView.requireViewById(R.id.list_bottom_padding); + + mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener( + mDeviceListLayoutListener); + // Init device list + mDevicesRecyclerView.setLayoutManager(mLayoutManager); + mDevicesRecyclerView.setAdapter(mAdapter); + // Init bottom buttons + mDoneButton.setOnClickListener(v -> dismiss()); + mStopButton.setOnClickListener(v -> { + mMediaOutputController.releaseSession(); + dismiss(); + }); + } + + @Override + public void onStart() { + super.onStart(); + mMediaOutputController.start(this); + } + + @Override + public void onStop() { + super.onStop(); + mMediaOutputController.stop(); + } + + @VisibleForTesting + void refresh() { + // Update header icon + final int iconRes = getHeaderIconRes(); + final IconCompat iconCompat = getHeaderIcon(); + if (iconRes != 0) { + mHeaderIcon.setVisibility(View.VISIBLE); + mHeaderIcon.setImageResource(iconRes); + } else if (iconCompat != null) { + mHeaderIcon.setVisibility(View.VISIBLE); + mHeaderIcon.setImageIcon(iconCompat.toIcon(mContext)); + } else { + mHeaderIcon.setVisibility(View.GONE); + } + if (mHeaderIcon.getVisibility() == View.VISIBLE) { + final int size = getHeaderIconSize(); + mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size)); + } + // Update title and subtitle + mHeaderTitle.setText(getHeaderText()); + final CharSequence subTitle = getHeaderSubtitle(); + if (TextUtils.isEmpty(subTitle)) { + mHeaderSubtitle.setVisibility(View.GONE); + mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL); + } else { + mHeaderSubtitle.setVisibility(View.VISIBLE); + mHeaderSubtitle.setText(subTitle); + mHeaderTitle.setGravity(Gravity.NO_GRAVITY); + } + if (!mAdapter.isDragging()) { + mAdapter.notifyDataSetChanged(); + } + // Add extra padding when device amount is less than 6 + if (mMediaOutputController.getMediaDevices().size() < 6) { + mListBottomPadding.setVisibility(View.VISIBLE); + } else { + mListBottomPadding.setVisibility(View.GONE); + } + } + + abstract int getHeaderIconRes(); + + abstract IconCompat getHeaderIcon(); + + abstract int getHeaderIconSize(); + + abstract CharSequence getHeaderText(); + + abstract CharSequence getHeaderSubtitle(); + + @Override + public void onMediaChanged() { + mMainThreadHandler.post(() -> refresh()); + } + + @Override + public void onMediaStoppedOrPaused() { + if (isShowing()) { + dismiss(); + } + } + + @Override + public void onRouteChanged() { + mMainThreadHandler.post(() -> refresh()); + } + + @Override + public void dismissDialog() { + dismiss(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java new file mode 100644 index 000000000000..64d20a273931 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2020 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.media.dialog; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.media.MediaMetadata; +import android.media.RoutingSessionInfo; +import android.media.session.MediaController; +import android.media.session.MediaSessionManager; +import android.media.session.PlaybackState; +import android.os.UserHandle; +import android.os.UserManager; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.core.graphics.drawable.IconCompat; + +import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.Utils; +import com.android.settingslib.bluetooth.BluetoothUtils; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.media.InfoMediaManager; +import com.android.settingslib.media.LocalMediaManager; +import com.android.settingslib.media.MediaDevice; +import com.android.settingslib.media.MediaOutputSliceConstants; +import com.android.settingslib.utils.ThreadUtils; +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.phone.ShadeController; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.inject.Inject; + +/** + * Controller for media output dialog + */ +public class MediaOutputController implements LocalMediaManager.DeviceCallback{ + + private static final String TAG = "MediaOutputController"; + private static final boolean DEBUG = false; + + private final String mPackageName; + private final Context mContext; + private final MediaSessionManager mMediaSessionManager; + private final ShadeController mShadeController; + private final ActivityStarter mActivityStarter; + @VisibleForTesting + final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); + + private MediaController mMediaController; + @VisibleForTesting + Callback mCallback; + @VisibleForTesting + LocalMediaManager mLocalMediaManager; + + @Inject + public MediaOutputController(@NonNull Context context, String packageName, + MediaSessionManager mediaSessionManager, LocalBluetoothManager + lbm, ShadeController shadeController, ActivityStarter starter) { + mContext = context; + mPackageName = packageName; + mMediaSessionManager = mediaSessionManager; + mShadeController = shadeController; + mActivityStarter = starter; + InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm); + mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName); + } + + void start(@NonNull Callback cb) { + mMediaDevices.clear(); + if (!TextUtils.isEmpty(mPackageName)) { + for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) { + if (TextUtils.equals(controller.getPackageName(), mPackageName)) { + mMediaController = controller; + mMediaController.unregisterCallback(mCb); + mMediaController.registerCallback(mCb); + break; + } + } + } + if (mMediaController == null) { + if (DEBUG) { + Log.d(TAG, "No media controller for " + mPackageName); + } + } + if (mLocalMediaManager == null) { + if (DEBUG) { + Log.d(TAG, "No local media manager " + mPackageName); + } + return; + } + mCallback = cb; + mLocalMediaManager.unregisterCallback(this); + mLocalMediaManager.stopScan(); + mLocalMediaManager.registerCallback(this); + mLocalMediaManager.startScan(); + } + + void stop() { + if (mMediaController != null) { + mMediaController.unregisterCallback(mCb); + } + if (mLocalMediaManager != null) { + mLocalMediaManager.unregisterCallback(this); + mLocalMediaManager.stopScan(); + } + mMediaDevices.clear(); + } + + @Override + public void onDeviceListUpdate(List<MediaDevice> devices) { + buildMediaDevices(devices); + mCallback.onRouteChanged(); + } + + @Override + public void onSelectedDeviceStateChanged(MediaDevice device, + @LocalMediaManager.MediaDeviceState int state) { + mCallback.onRouteChanged(); + } + + @Override + public void onDeviceAttributesChanged() { + mCallback.onRouteChanged(); + } + + @Override + public void onRequestFailed(int reason) { + mCallback.onRouteChanged(); + } + + CharSequence getHeaderTitle() { + if (mMediaController != null) { + final MediaMetadata metadata = mMediaController.getMetadata(); + if (metadata != null) { + return metadata.getDescription().getTitle(); + } + } + return mContext.getText(R.string.controls_media_title); + } + + CharSequence getHeaderSubTitle() { + if (mMediaController == null) { + return null; + } + final MediaMetadata metadata = mMediaController.getMetadata(); + if (metadata == null) { + return null; + } + return metadata.getDescription().getSubtitle(); + } + + IconCompat getHeaderIcon() { + if (mMediaController == null) { + return null; + } + final MediaMetadata metadata = mMediaController.getMetadata(); + if (metadata != null) { + final Bitmap bitmap = metadata.getDescription().getIconBitmap(); + if (bitmap != null) { + final Bitmap roundBitmap = Utils.convertCornerRadiusBitmap(mContext, bitmap, + (float) mContext.getResources().getDimensionPixelSize( + R.dimen.media_output_dialog_icon_corner_radius)); + return IconCompat.createWithBitmap(roundBitmap); + } + } + if (DEBUG) { + Log.d(TAG, "Media meta data does not contain icon information"); + } + return getPackageIcon(); + } + + IconCompat getDeviceIconCompat(MediaDevice device) { + Drawable drawable = device.getIcon(); + if (drawable == null) { + if (DEBUG) { + Log.d(TAG, "getDeviceIconCompat() device : " + device.getName() + + ", drawable is null"); + } + // Use default Bluetooth device icon to handle getIcon() is null case. + drawable = mContext.getDrawable(com.android.internal.R.drawable.ic_bt_headphones_a2dp); + } + return BluetoothUtils.createIconWithDrawable(drawable); + } + + private IconCompat getPackageIcon() { + if (TextUtils.isEmpty(mPackageName)) { + return null; + } + try { + final Drawable drawable = mContext.getPackageManager().getApplicationIcon(mPackageName); + if (drawable instanceof BitmapDrawable) { + return IconCompat.createWithBitmap(((BitmapDrawable) drawable).getBitmap()); + } + final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return IconCompat.createWithBitmap(bitmap); + } catch (PackageManager.NameNotFoundException e) { + if (DEBUG) { + Log.e(TAG, "Package is not found. Unable to get package icon."); + } + } + return null; + } + + private void buildMediaDevices(List<MediaDevice> devices) { + // For the first time building list, to make sure the top device is the connected device. + if (mMediaDevices.isEmpty()) { + final MediaDevice connectedMediaDevice = getCurrentConnectedMediaDevice(); + if (connectedMediaDevice == null) { + if (DEBUG) { + Log.d(TAG, "No connected media device."); + } + mMediaDevices.addAll(devices); + return; + } + for (MediaDevice device : devices) { + if (TextUtils.equals(device.getId(), connectedMediaDevice.getId())) { + mMediaDevices.add(0, device); + } else { + mMediaDevices.add(device); + } + } + return; + } + // To keep the same list order + final Collection<MediaDevice> targetMediaDevices = new ArrayList<>(); + for (MediaDevice originalDevice : mMediaDevices) { + for (MediaDevice newDevice : devices) { + if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) { + targetMediaDevices.add(newDevice); + break; + } + } + } + if (targetMediaDevices.size() != devices.size()) { + devices.removeAll(targetMediaDevices); + targetMediaDevices.addAll(devices); + } + mMediaDevices.clear(); + mMediaDevices.addAll(targetMediaDevices); + } + + void connectDevice(MediaDevice device) { + ThreadUtils.postOnBackgroundThread(() -> { + mLocalMediaManager.connectDevice(device); + }); + } + + Collection<MediaDevice> getMediaDevices() { + return mMediaDevices; + } + + MediaDevice getCurrentConnectedMediaDevice() { + return mLocalMediaManager.getCurrentConnectedDevice(); + } + + private MediaDevice getMediaDeviceById(String id) { + return mLocalMediaManager.getMediaDeviceById(new ArrayList<>(mMediaDevices), id); + } + + boolean addDeviceToPlayMedia(MediaDevice device) { + return mLocalMediaManager.addDeviceToPlayMedia(device); + } + + boolean removeDeviceFromPlayMedia(MediaDevice device) { + return mLocalMediaManager.removeDeviceFromPlayMedia(device); + } + + List<MediaDevice> getSelectableMediaDevice() { + return mLocalMediaManager.getSelectableMediaDevice(); + } + + List<MediaDevice> getSelectedMediaDevice() { + return mLocalMediaManager.getSelectedMediaDevice(); + } + + List<MediaDevice> getDeselectableMediaDevice() { + return mLocalMediaManager.getDeselectableMediaDevice(); + } + + boolean isDeviceIncluded(Collection<MediaDevice> deviceCollection, MediaDevice targetDevice) { + for (MediaDevice device : deviceCollection) { + if (TextUtils.equals(device.getId(), targetDevice.getId())) { + return true; + } + } + return false; + } + + void adjustSessionVolume(String sessionId, int volume) { + mLocalMediaManager.adjustSessionVolume(sessionId, volume); + } + + void adjustSessionVolume(int volume) { + mLocalMediaManager.adjustSessionVolume(volume); + } + + int getSessionVolumeMax() { + return mLocalMediaManager.getSessionVolumeMax(); + } + + int getSessionVolume() { + return mLocalMediaManager.getSessionVolume(); + } + + CharSequence getSessionName() { + return mLocalMediaManager.getSessionName(); + } + + void releaseSession() { + mLocalMediaManager.releaseSession(); + } + + List<RoutingSessionInfo> getActiveRemoteMediaDevices() { + final List<RoutingSessionInfo> sessionInfos = new ArrayList<>(); + for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) { + if (!info.isSystemSession()) { + sessionInfos.add(info); + } + } + return sessionInfos; + } + + void adjustVolume(MediaDevice device, int volume) { + ThreadUtils.postOnBackgroundThread(() -> { + device.requestSetVolume(volume); + }); + } + + String getPackageName() { + return mPackageName; + } + + boolean hasAdjustVolumeUserRestriction() { + if (RestrictedLockUtilsInternal.checkIfRestrictionEnforced( + mContext, UserManager.DISALLOW_ADJUST_VOLUME, UserHandle.myUserId()) != null) { + return true; + } + final UserManager um = mContext.getSystemService(UserManager.class); + return um.hasBaseUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, + UserHandle.of(UserHandle.myUserId())); + } + + boolean isTransferring() { + for (MediaDevice device : mMediaDevices) { + if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) { + return true; + } + } + return false; + } + + boolean isZeroMode() { + if (mMediaDevices.size() == 1) { + final MediaDevice device = mMediaDevices.iterator().next(); + // Add "pair new" only when local output device exists + final int type = device.getDeviceType(); + if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) { + return true; + } + } + return false; + } + + void launchBluetoothPairing() { + mCallback.dismissDialog(); + final ActivityStarter.OnDismissAction postKeyguardAction = () -> { + mContext.sendBroadcast(new Intent() + .setAction(MediaOutputSliceConstants.ACTION_LAUNCH_BLUETOOTH_PAIRING) + .setPackage(MediaOutputSliceConstants.SETTINGS_PACKAGE_NAME)); + mShadeController.animateCollapsePanels(); + return true; + }; + mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true); + } + + private final MediaController.Callback mCb = new MediaController.Callback() { + @Override + public void onMetadataChanged(MediaMetadata metadata) { + mCallback.onMediaChanged(); + } + + @Override + public void onPlaybackStateChanged(PlaybackState playbackState) { + final int state = playbackState.getState(); + if (state == PlaybackState.STATE_STOPPED || state == PlaybackState.STATE_PAUSED) { + mCallback.onMediaStoppedOrPaused(); + } + } + }; + + interface Callback { + /** + * Override to handle the media content updating. + */ + void onMediaChanged(); + + /** + * Override to handle the media state updating. + */ + void onMediaStoppedOrPaused(); + + /** + * Override to handle the device updating. + */ + void onRouteChanged(); + + /** + * Override to dismiss dialog. + */ + void dismissDialog(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java new file mode 100644 index 000000000000..ac9d8ce52d88 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 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.media.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.view.WindowManager; + +import androidx.core.graphics.drawable.IconCompat; + +import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; + +/** + * Dialog for media output transferring. + */ +@SysUISingleton +public class MediaOutputDialog extends MediaOutputBaseDialog { + + MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController + mediaOutputController) { + super(context, mediaOutputController); + mAdapter = new MediaOutputAdapter(mMediaOutputController); + if (!aboveStatusbar) { + getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); + } + show(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mGroupItemController.setVisibility(View.GONE); + mGroupDivider.setVisibility(View.GONE); + } + + @Override + int getHeaderIconRes() { + return 0; + } + + @Override + IconCompat getHeaderIcon() { + return mMediaOutputController.getHeaderIcon(); + } + + @Override + int getHeaderIconSize() { + return mContext.getResources().getDimensionPixelSize( + R.dimen.media_output_dialog_header_album_icon_size); + } + + @Override + CharSequence getHeaderText() { + return mMediaOutputController.getHeaderTitle(); + } + + @Override + CharSequence getHeaderSubtitle() { + return mMediaOutputController.getHeaderSubTitle(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt new file mode 100644 index 000000000000..bc1dca58990d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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.media.dialog + +import android.content.Context +import android.media.session.MediaSessionManager +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.phone.ShadeController +import javax.inject.Inject + +/** + * Factory to create [MediaOutputDialog] objects. + */ +class MediaOutputDialogFactory @Inject constructor( + private val context: Context, + private val mediaSessionManager: MediaSessionManager, + private val lbm: LocalBluetoothManager?, + private val shadeController: ShadeController, + private val starter: ActivityStarter +) { + /** Creates a [MediaOutputDialog] for the given package. */ + fun create(packageName: String, aboveStatusBar: Boolean) { + MediaOutputController(context, packageName, mediaSessionManager, lbm, shadeController, + starter).run { + MediaOutputDialog(context, aboveStatusBar, this) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index a5ee3a0cd4e0..9ca5f2a0dfd1 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -551,6 +551,11 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize return; } + if (mLeash == null) { + Log.e(TAG, "PiP Leash is not yet ready."); + return; + } + if (Looper.getMainLooper() != Looper.myLooper()) { throw new RuntimeException("PipMenuView needs to be attached on the main thread."); } @@ -580,6 +585,13 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize } } + /** + * Return whether the PiP Menu has been attached to the leash yet. + */ + public boolean isPipMenuViewHostAttached() { + return mPipViewHost != null; + } + /** * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}. diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java index 8bcaa8ab5404..22adbb77d70a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -17,6 +17,7 @@ package com.android.systemui.pip; import android.app.TaskInfo; +import android.content.pm.PackageManager; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -28,26 +29,48 @@ import com.android.systemui.dagger.SysUISingleton; @SysUISingleton public class PipUiEventLogger { + private static final int INVALID_PACKAGE_UID = -1; + private final UiEventLogger mUiEventLogger; + private final PackageManager mPackageManager; - private TaskInfo mTaskInfo; + private String mPackageName; + private int mPackageUid = INVALID_PACKAGE_UID; - public PipUiEventLogger(UiEventLogger uiEventLogger) { + public PipUiEventLogger(UiEventLogger uiEventLogger, PackageManager packageManager) { mUiEventLogger = uiEventLogger; + mPackageManager = packageManager; } public void setTaskInfo(TaskInfo taskInfo) { - mTaskInfo = taskInfo; + if (taskInfo == null) { + mPackageName = null; + mPackageUid = INVALID_PACKAGE_UID; + } else { + mPackageName = taskInfo.topActivity.getPackageName(); + mPackageUid = getUid(mPackageName, taskInfo.userId); + } } /** * Sends log via UiEvent, reference go/uievent for how to debug locally */ public void log(PipUiEventEnum event) { - if (mTaskInfo == null) { + if (mPackageName == null || mPackageUid == INVALID_PACKAGE_UID) { return; } - mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName()); + mUiEventLogger.log(event, mPackageUid, mPackageName); + } + + private int getUid(String packageName, int userId) { + int uid = INVALID_PACKAGE_UID; + try { + uid = mPackageManager.getApplicationInfoAsUser( + packageName, 0 /* ApplicationInfoFlags */, userId).uid; + } catch (PackageManager.NameNotFoundException e) { + // do nothing. + } + return uid; } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index d308172689db..5b07db6c91a1 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -118,12 +118,7 @@ public class PipMenuActivityController { } public void onActivityPinned() { - if (mPipMenuView == null) { - WindowManager.LayoutParams lp = - getPipMenuLayoutParams(0, 0); - mPipMenuView = new PipMenuView(mContext, this); - mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, lp); - } + attachPipMenuView(); mInputConsumerController.registerInputConsumer(true /* withSfVsync */); } @@ -140,6 +135,14 @@ public class PipMenuActivityController { } } + private void attachPipMenuView() { + if (mPipMenuView == null) { + mPipMenuView = new PipMenuView(mContext, this); + + } + mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, getPipMenuLayoutParams(0, 0)); + } + /** * Adds a new menu activity listener. */ @@ -197,10 +200,11 @@ public class PipMenuActivityController { + " callers=\n" + Debug.getCallers(5, " ")); } - if (mPipMenuView == null) { - Log.e(TAG, "PipMenu has not been attached yet."); - return; + if (!mPipTaskOrganizer.isPipMenuViewHostAttached()) { + Log.d(TAG, "PipMenu has not been attached yet. Attaching now at showMenuInternal()."); + attachPipMenuView(); } + mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, showResizeHandle); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index e738cec4962a..bffeb3ec3c70 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -14,11 +14,8 @@ package com.android.systemui.qs.customize; -import android.app.AlertDialog; -import android.app.AlertDialog.Builder; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; @@ -28,10 +25,11 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.core.view.AccessibilityDelegateCompat; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup; import androidx.recyclerview.widget.ItemTouchHelper; @@ -49,7 +47,6 @@ import com.android.systemui.qs.customize.TileQueryHelper.TileInfo; import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tileimpl.QSIconViewImpl; -import com.android.systemui.statusbar.phone.SystemUIDialog; import java.util.ArrayList; import java.util.List; @@ -78,10 +75,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private final List<TileInfo> mTiles = new ArrayList<>(); private final ItemTouchHelper mItemTouchHelper; private final ItemDecoration mDecoration; - private final AccessibilityManager mAccessibilityManager; private final int mMinNumTiles; private int mEditIndex; private int mTileDividerIndex; + private int mFocusIndex; private boolean mNeedsFocus; private List<String> mCurrentSpecs; private List<TileInfo> mOtherTiles; @@ -90,17 +87,28 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private Holder mCurrentDrag; private int mAccessibilityAction = ACTION_NONE; private int mAccessibilityFromIndex; - private CharSequence mAccessibilityFromLabel; private QSTileHost mHost; private final UiEventLogger mUiEventLogger; + private final AccessibilityDelegateCompat mAccessibilityDelegate; + private RecyclerView mRecyclerView; public TileAdapter(Context context, UiEventLogger uiEventLogger) { mContext = context; mUiEventLogger = uiEventLogger; - mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mItemTouchHelper = new ItemTouchHelper(mCallbacks); mDecoration = new TileItemDecoration(context); mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles); + mAccessibilityDelegate = new TileAdapterDelegate(); + } + + @Override + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = recyclerView; + } + + @Override + public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = null; } public void setHost(QSTileHost host) { @@ -130,7 +138,6 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta // Remove blank tile from last spot mTiles.remove(--mEditIndex); // Update the tile divider position - mTileDividerIndex--; notifyDataSetChanged(); } mAccessibilityAction = ACTION_NONE; @@ -241,14 +248,12 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta } private void setSelectableForHeaders(View view) { - if (mAccessibilityManager.isTouchExplorationEnabled()) { - final boolean selectable = mAccessibilityAction == ACTION_NONE; - view.setFocusable(selectable); - view.setImportantForAccessibility(selectable - ? View.IMPORTANT_FOR_ACCESSIBILITY_YES - : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - view.setFocusableInTouchMode(selectable); - } + final boolean selectable = mAccessibilityAction == ACTION_NONE; + view.setFocusable(selectable); + view.setImportantForAccessibility(selectable + ? View.IMPORTANT_FOR_ACCESSIBILITY_YES + : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + view.setFocusableInTouchMode(selectable); } @Override @@ -285,12 +290,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta holder.mTileView.setVisibility(View.VISIBLE); holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); holder.mTileView.setContentDescription(mContext.getString( - R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, - position)); + R.string.accessibility_qs_edit_tile_add_to_position, position)); holder.mTileView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - selectPosition(holder.getAdapterPosition(), v); + selectPosition(holder.getLayoutPosition()); } }); focusOnHolder(holder); @@ -299,54 +303,49 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta TileInfo info = mTiles.get(position); - if (position > mEditIndex) { - info.state.contentDescription = mContext.getString( - R.string.accessibility_qs_edit_add_tile_label, info.state.label); - } else if (mAccessibilityAction == ACTION_ADD) { + final boolean selectable = 0 < position && position < mEditIndex; + if (selectable && mAccessibilityAction == ACTION_ADD) { info.state.contentDescription = mContext.getString( - R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, position); - } else if (mAccessibilityAction == ACTION_MOVE) { + R.string.accessibility_qs_edit_tile_add_to_position, position); + } else if (selectable && mAccessibilityAction == ACTION_MOVE) { info.state.contentDescription = mContext.getString( - R.string.accessibility_qs_edit_tile_move, mAccessibilityFromLabel, position); + R.string.accessibility_qs_edit_tile_move_to_position, position); } else { - info.state.contentDescription = mContext.getString( - R.string.accessibility_qs_edit_tile_label, position, info.state.label); + info.state.contentDescription = info.state.label; } + info.state.expandedAccessibilityClassName = ""; + holder.mTileView.handleStateChanged(info.state); holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem); + holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + holder.mTileView.setClickable(true); + holder.mTileView.setOnClickListener(null); + holder.mTileView.setFocusable(true); + holder.mTileView.setFocusableInTouchMode(true); - if (mAccessibilityManager.isTouchExplorationEnabled()) { - final boolean selectable = mAccessibilityAction == ACTION_NONE || position < mEditIndex; + if (mAccessibilityAction != ACTION_NONE) { holder.mTileView.setClickable(selectable); holder.mTileView.setFocusable(selectable); + holder.mTileView.setFocusableInTouchMode(selectable); holder.mTileView.setImportantForAccessibility(selectable ? View.IMPORTANT_FOR_ACCESSIBILITY_YES : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - holder.mTileView.setFocusableInTouchMode(selectable); if (selectable) { holder.mTileView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - int position = holder.getAdapterPosition(); + int position = holder.getLayoutPosition(); if (position == RecyclerView.NO_POSITION) return; if (mAccessibilityAction != ACTION_NONE) { - selectPosition(position, v); - } else { - if (position < mEditIndex && canRemoveTiles()) { - showAccessibilityDialog(position, v); - } else if (position < mEditIndex && !canRemoveTiles()) { - startAccessibleMove(position); - } else { - startAccessibleAdd(position); - } + selectPosition(position); } } }); - if (position == mAccessibilityFromIndex) { - focusOnHolder(holder); - } } } + if (position == mFocusIndex) { + focusOnHolder(holder); + } } private void focusOnHolder(Holder holder) { @@ -360,9 +359,13 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta int oldLeft, int oldTop, int oldRight, int oldBottom) { holder.mTileView.removeOnLayoutChangeListener(this); holder.mTileView.requestFocus(); + if (mAccessibilityAction == ACTION_NONE) { + holder.mTileView.clearFocus(); + } } }); mNeedsFocus = false; + mFocusIndex = RecyclerView.NO_POSITION; } } @@ -370,72 +373,77 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta return mCurrentSpecs.size() > mMinNumTiles; } - private void selectPosition(int position, View v) { + private void selectPosition(int position) { if (mAccessibilityAction == ACTION_ADD) { // Remove the placeholder. mTiles.remove(mEditIndex--); - notifyItemRemoved(mEditIndex); } mAccessibilityAction = ACTION_NONE; - move(mAccessibilityFromIndex, position, v); + move(mAccessibilityFromIndex, position, false); + mFocusIndex = position; + mNeedsFocus = true; notifyDataSetChanged(); } - private void showAccessibilityDialog(final int position, final View v) { - final TileInfo info = mTiles.get(position); - CharSequence[] options = new CharSequence[] { - mContext.getString(R.string.accessibility_qs_edit_move_tile, info.state.label), - mContext.getString(R.string.accessibility_qs_edit_remove_tile, info.state.label), - }; - AlertDialog dialog = new Builder(mContext) - .setItems(options, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == 0) { - startAccessibleMove(position); - } else { - move(position, info.isSystem ? mEditIndex : mTileDividerIndex, v); - notifyItemChanged(mTileDividerIndex); - notifyDataSetChanged(); - } - } - }).setNegativeButton(android.R.string.cancel, null) - .create(); - SystemUIDialog.setShowForAllUsers(dialog, true); - SystemUIDialog.applyFlags(dialog); - dialog.show(); - } - private void startAccessibleAdd(int position) { mAccessibilityFromIndex = position; - mAccessibilityFromLabel = mTiles.get(position).state.label; mAccessibilityAction = ACTION_ADD; // Add placeholder for last slot. mTiles.add(mEditIndex++, null); // Update the tile divider position mTileDividerIndex++; + mFocusIndex = mEditIndex - 1; mNeedsFocus = true; + if (mRecyclerView != null) { + mRecyclerView.post(() -> mRecyclerView.smoothScrollToPosition(mFocusIndex)); + } notifyDataSetChanged(); } private void startAccessibleMove(int position) { mAccessibilityFromIndex = position; - mAccessibilityFromLabel = mTiles.get(position).state.label; mAccessibilityAction = ACTION_MOVE; + mFocusIndex = position; mNeedsFocus = true; notifyDataSetChanged(); } + private boolean canRemoveFromPosition(int position) { + return canRemoveTiles() && isCurrentTile(position); + } + + private boolean isCurrentTile(int position) { + return position < mEditIndex; + } + + private boolean canAddFromPosition(int position) { + return position > mEditIndex; + } + + private void addFromPosition(int position) { + if (!canAddFromPosition(position)) return; + move(position, mEditIndex); + } + + private void removeFromPosition(int position) { + if (!canRemoveFromPosition(position)) return; + TileInfo info = mTiles.get(position); + move(position, info.isSystem ? mEditIndex : mTileDividerIndex); + } + public SpanSizeLookup getSizeLookup() { return mSizeLookup; } - private boolean move(int from, int to, View v) { + private boolean move(int from, int to) { + return move(from, to, true); + } + + private boolean move(int from, int to, boolean notify) { if (to == from) { return true; } - CharSequence fromLabel = mTiles.get(from).state.label; - move(from, to, mTiles); + move(from, to, mTiles, notify); updateDividerLocations(); if (to >= mEditIndex) { mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to))); @@ -477,9 +485,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta return spec; } - private <T> void move(int from, int to, List<T> list) { + private <T> void move(int from, int to, List<T> list, boolean notify) { list.add(to, list.remove(from)); - notifyItemMoved(from, to); + if (notify) { + notifyItemMoved(from, to); + } } public class Holder extends ViewHolder { @@ -491,6 +501,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mTileView = (CustomizeTileView) ((FrameLayout) itemView).getChildAt(0); mTileView.setBackground(null); mTileView.getIcon().disableAnimation(); + mTileView.setTag(this); + ViewCompat.setAccessibilityDelegate(mTileView, mAccessibilityDelegate); } } @@ -527,6 +539,46 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta .setDuration(DRAG_LENGTH) .alpha(.6f); } + + boolean canRemove() { + return canRemoveFromPosition(getLayoutPosition()); + } + + boolean canAdd() { + return canAddFromPosition(getLayoutPosition()); + } + + void toggleState() { + if (canAdd()) { + add(); + } else { + remove(); + } + } + + private void add() { + addFromPosition(getLayoutPosition()); + } + + private void remove() { + removeFromPosition(getLayoutPosition()); + } + + boolean isCurrentTile() { + return TileAdapter.this.isCurrentTile(getLayoutPosition()); + } + + void startAccessibleAdd() { + TileAdapter.this.startAccessibleAdd(getLayoutPosition()); + } + + void startAccessibleMove() { + TileAdapter.this.startAccessibleMove(getLayoutPosition()); + } + + boolean canTakeAccessibleAction() { + return mAccessibilityAction == ACTION_NONE; + } } private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() { @@ -648,7 +700,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta to == 0 || to == RecyclerView.NO_POSITION) { return false; } - return move(from, to, target.itemView); + return move(from, to); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java new file mode 100644 index 000000000000..1e426adac0b8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2020 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.qs.customize; + +import android.os.Bundle; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +import androidx.core.view.AccessibilityDelegateCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; + +import com.android.systemui.R; + +import java.util.List; + +/** + * Accessibility delegate for {@link TileAdapter} views. + * + * This delegate will populate the accessibility info with the proper actions that can be taken for + * the different tiles: + * <ul> + * <li>Add to end if the tile is not a current tile (by double tap).</li> + * <li>Add to a given position (by context menu). This will let the user select a position.</li> + * <li>Remove, if the tile is a current tile (by double tap).</li> + * <li>Move to a given position (by context menu). This will let the user select a position.</li> + * </ul> + * + * This only handles generating the associated actions. The logic for selecting positions is handled + * by {@link TileAdapter}. + * + * In order for the delegate to work properly, the asociated {@link TileAdapter.Holder} should be + * passed along with the view using {@link View#setTag}. + */ +class TileAdapterDelegate extends AccessibilityDelegateCompat { + + private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position; + private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position; + + private TileAdapter.Holder getHolder(View view) { + return (TileAdapter.Holder) view.getTag(); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + TileAdapter.Holder holder = getHolder(host); + info.setCollectionItemInfo(null); + info.setStateDescription(""); + if (holder == null || !holder.canTakeAccessibleAction()) { + // If there's not a holder (not a regular Tile) or an action cannot be taken + // because we are in the middle of an accessibility action, don't create a special node. + return; + } + + addClickAction(host, info, holder); + maybeAddActionAddToPosition(host, info, holder); + maybeAddActionMoveToPosition(host, info, holder); + + if (holder.isCurrentTile()) { + info.setStateDescription(host.getContext().getString( + R.string.accessibility_qs_edit_position, holder.getLayoutPosition())); + } + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + TileAdapter.Holder holder = getHolder(host); + + if (holder == null || !holder.canTakeAccessibleAction()) { + // If there's not a holder (not a regular Tile) or an action cannot be taken + // because we are in the middle of an accessibility action, perform the default action. + return super.performAccessibilityAction(host, action, args); + } + if (action == AccessibilityNodeInfo.ACTION_CLICK) { + holder.toggleState(); + return true; + } else if (action == MOVE_TO_POSITION_ID) { + holder.startAccessibleMove(); + return true; + } else if (action == ADD_TO_POSITION_ID) { + holder.startAccessibleAdd(); + return true; + } else { + return super.performAccessibilityAction(host, action, args); + } + } + + private void addClickAction( + View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) { + String clickActionString; + if (holder.canAdd()) { + clickActionString = host.getContext().getString( + R.string.accessibility_qs_edit_tile_add_action); + } else if (holder.canRemove()) { + clickActionString = host.getContext().getString( + R.string.accessibility_qs_edit_remove_tile_action); + } else { + // Remove the default click action if tile can't either be added or removed (for example + // if there's the minimum number of tiles) + List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> listOfActions = + info.getActionList(); // This is a copy + int numActions = listOfActions.size(); + for (int i = 0; i < numActions; i++) { + if (listOfActions.get(i).getId() == AccessibilityNodeInfo.ACTION_CLICK) { + info.removeAction(listOfActions.get(i)); + } + } + return; + } + + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat( + AccessibilityNodeInfo.ACTION_CLICK, clickActionString); + info.addAction(action); + } + + private void maybeAddActionMoveToPosition( + View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) { + if (holder.isCurrentTile()) { + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat(MOVE_TO_POSITION_ID, + host.getContext().getString( + R.string.accessibility_qs_edit_tile_start_move)); + info.addAction(action); + } + } + + private void maybeAddActionAddToPosition( + View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) { + if (holder.canAdd()) { + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat(ADD_TO_POSITION_ID, + host.getContext().getString( + R.string.accessibility_qs_edit_tile_start_add)); + info.addAction(action); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 4673ec73c25a..37ae791bf172 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -44,6 +44,8 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.view.InsetsState.InternalInsetsType; @@ -59,7 +61,9 @@ import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.tracing.ProtoTracer; +import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; /** * This class takes the functions from IStatusBar that come in on @@ -70,6 +74,8 @@ import java.util.ArrayList; */ public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks>, DisplayManager.DisplayListener { + private static final String TAG = CommandQueue.class.getSimpleName(); + private static final int INDEX_MASK = 0xffff; private static final int MSG_SHIFT = 16; private static final int MSG_MASK = 0xffff << MSG_SHIFT; @@ -131,6 +137,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_TRACING_STATE_CHANGED = 55 << MSG_SHIFT; private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 56 << MSG_SHIFT; private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 57 << MSG_SHIFT; + private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 58 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -353,6 +360,11 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< * @param connect {@code true} if needs connection, otherwise set the connection to null. */ default void requestWindowMagnificationConnection(boolean connect) { } + + /** + * Handles a window manager shell logging command. + */ + default void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {} } public CommandQueue(Context context) { @@ -984,6 +996,17 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override + public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) { + synchronized (mLock) { + SomeArgs internalArgs = SomeArgs.obtain(); + internalArgs.arg1 = args; + internalArgs.arg2 = outFd; + mHandler.obtainMessage(MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND, internalArgs) + .sendToTarget(); + } + } + + @Override public void suppressAmbientDisplay(boolean suppress) { synchronized (mLock) { mHandler.obtainMessage(MSG_SUPPRESS_AMBIENT_DISPLAY, suppress).sendToTarget(); @@ -1334,6 +1357,18 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mCallbacks.get(i).requestWindowMagnificationConnection((Boolean) msg.obj); } break; + case MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND: + args = (SomeArgs) msg.obj; + try (ParcelFileDescriptor pfd = (ParcelFileDescriptor) args.arg2) { + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).handleWindowManagerLoggingCommand( + (String[]) args.arg1, pfd); + } + } catch (IOException e) { + Log.e(TAG, "Failed to handle logging command", e); + } + args.recycle(); + break; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 7c7bb5c83762..b19997d15664 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -42,6 +42,8 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -538,12 +540,21 @@ public class NotificationConversationInfo extends LinearLayout implements && Settings.Global.getInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0) == 1; + Drawable person = mIconFactory.getBaseIconDrawable(mShortcutInfo); + if (person == null) { + person = mContext.getDrawable(R.drawable.ic_person).mutate(); + TypedArray ta = mContext.obtainStyledAttributes(new int[]{android.R.attr.colorAccent}); + int colorAccent = ta.getColor(0, 0); + ta.recycle(); + person.setTint(colorAccent); + } + PriorityOnboardingDialogController controller = mBuilderProvider.get() .setContext(mUserContext) .setView(onboardingView) .setIgnoresDnd(ignoreDnd) .setShowsAsBubble(showAsBubble) - .setIcon(mIconFactory.getBaseIconDrawable(mShortcutInfo)) + .setIcon(person) .setBadge(mIconFactory.getAppBadge( mPackageName, UserHandle.getUserId(mSbn.getUid()))) .setOnSettingsClick(mOnConversationSettingsClickListener) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index fe6666943e5c..f9d7c1f056ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -24,6 +24,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import java.util.HashSet; @@ -34,7 +35,7 @@ import javax.inject.Inject; * A class that manages the roundness for notification views */ @SysUISingleton -public class NotificationRoundnessManager implements OnHeadsUpChangedListener { +public class NotificationRoundnessManager { private final ExpandableView[] mFirstInSectionViews; private final ExpandableView[] mLastInSectionViews; @@ -59,27 +60,7 @@ public class NotificationRoundnessManager implements OnHeadsUpChangedListener { mBypassController = keyguardBypassController; } - @Override - public void onHeadsUpPinned(NotificationEntry headsUp) { - updateView(headsUp.getRow(), false /* animate */); - } - - @Override - public void onHeadsUpUnPinned(NotificationEntry headsUp) { - updateView(headsUp.getRow(), true /* animate */); - } - - public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row, - boolean isAnimatingAway) { - updateView(row, false /* animate */); - } - - @Override - public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - updateView(entry.getRow(), false /* animate */); - } - - private void updateView(ExpandableView view, boolean animate) { + public void updateView(ExpandableView view, boolean animate) { boolean changed = updateViewWithoutCallback(view, animate); if (changed) { mRoundingChangedCallback.run(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index e0e3013c13dd..7a12464bd4ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -73,9 +73,6 @@ import android.widget.ScrollView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEvent; -import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.keyguard.KeyguardSliceView; import com.android.settingslib.Utils; @@ -95,7 +92,6 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.FakeShadowView; -import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.ShadeViewRefactor; @@ -264,6 +260,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable protected EmptyShadeView mEmptyShadeView; private boolean mDismissAllInProgress; private boolean mFadeNotificationsOnDismiss; + private FooterDismissListener mFooterDismissListener; /** * Was the scroller scrolled to the top when the down motion was observed? @@ -318,7 +315,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>(); private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations = new HashSet<>(); - private final NotificationRoundnessManager mRoundnessManager; private boolean mTrackingHeadsUp; private boolean mForceNoOverlappingRendering; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); @@ -450,12 +446,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private int mHeadsUpInset; private HeadsUpAppearanceController mHeadsUpAppearanceController; private final Rect mTmpRect = new Rect(); + private DismissListener mDismissListener; private DismissAllAnimationListener mDismissAllAnimationListener; - @VisibleForTesting - protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); - protected final UiEventLogger mUiEventLogger; - private final NotificationRemoteInputManager mRemoteInputManager = - Dependency.get(NotificationRemoteInputManager.class); + private NotificationRemoteInputManager mRemoteInputManager; private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class); private final LockscreenGestureLogger mLockscreenGestureLogger = @@ -468,7 +461,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationPanelViewController mNotificationPanelController; private final NotificationSectionsManager mSectionsManager; - private final ForegroundServiceSectionController mFgsSectionController; private ForegroundServiceDungeonView mFgsSectionView; private boolean mAnimateBottomOnLayout; private float mLastSentAppear; @@ -529,22 +521,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable public NotificationStackScrollLayout( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - NotificationRoundnessManager notificationRoundnessManager, - SysuiStatusBarStateController statusbarStateController, NotificationSectionsManager notificationSectionsManager, - ForegroundServiceSectionController fgsSectionController, - ForegroundServiceDismissalFeatureController fgsFeatureController, GroupMembershipManager groupMembershipManager, GroupExpansionManager groupExpansionManager, - UiEventLogger uiEventLogger + SysuiStatusBarStateController statusbarStateController ) { super(context, attrs, 0, 0); Resources res = getResources(); - - mRoundnessManager = notificationRoundnessManager; - mFgsSectionController = fgsSectionController; - mSectionsManager = notificationSectionsManager; + mSectionsManager.initialize(this, LayoutInflater.from(context)); mSectionsManager.setOnClearSilentNotifsClickListener(v -> { // Leave the shade open if there will be other notifs left over to clear @@ -589,18 +574,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mGroupMembershipManager = groupMembershipManager; mGroupExpansionManager = groupExpansionManager; mStatusbarStateController = statusbarStateController; - initializeForegroundServiceSection(fgsFeatureController); - mUiEventLogger = uiEventLogger; } - private void initializeForegroundServiceSection( - ForegroundServiceDismissalFeatureController featureController) { - if (featureController.isForegroundServiceDismissalEnabled()) { - LayoutInflater li = LayoutInflater.from(mContext); - mFgsSectionView = - (ForegroundServiceDungeonView) mFgsSectionController.createView(li); - addView(mFgsSectionView, -1); + void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) { + if (mFgsSectionView != null) { + return; } + + mFgsSectionView = fgsSectionView; + + addView(mFgsSectionView, -1); } void updateDismissRtlSetting(boolean dismissRtl) { @@ -3222,7 +3205,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mAnimateNextSectionBoundsChange = false; } mAmbientState.setLastVisibleBackgroundChild(lastChild); - mRoundnessManager.updateRoundedChildren(mSections); + // TODO: Refactor SectionManager and put the RoundnessManager there. + mController.getNoticationRoundessManager().updateRoundedChildren(mSections); mAnimateBottomOnLayout = false; invalidate(); } @@ -3297,14 +3281,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable requestChildrenUpdate(); } - @ShadeViewRefactor(RefactorComponent.ADAPTER) - void bindRow(ExpandableNotificationRow row) { - row.setHeadsUpAnimatingAwayListener(animatingAway -> { - mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway); - mHeadsUpAppearanceController.updateHeader(row.getEntry()); - }); - } - public boolean containsView(View v) { return v.getParent() == this; } @@ -5022,7 +4998,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable public void setTrackingHeadsUp(ExpandableNotificationRow row) { mAmbientState.setTrackedHeadsUpRow(row); mTrackingHeadsUp = row != null; - mRoundnessManager.setTrackingHeadsUp(row); } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -5343,16 +5318,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setHeadsUpAppearanceController( + void setHeadsUpAppearanceController( HeadsUpAppearanceController headsUpAppearanceController) { mHeadsUpAppearanceController = headsUpAppearanceController; } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @VisibleForTesting - void clearNotifications( - @SelectedRows int selection, - boolean closeShade) { + void clearNotifications(@SelectedRows int selection, boolean closeShade) { // animate-swipe all dismissable notifications, then animate the shade closed int numChildren = getChildCount(); @@ -5393,8 +5366,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - // Log dismiss event even if there's nothing to dismiss - mUiEventLogger.log(NotificationPanelEvent.fromSelection(selection)); + if (mDismissListener != null) { + mDismissListener.onDismiss(selection); + } if (viewsToRemove.isEmpty()) { if (closeShade) { @@ -5484,7 +5458,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_footer, this, false); footerView.setDismissButtonClickListener(v -> { - mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES); + if (mFooterDismissListener != null) { + mFooterDismissListener.onDismiss(); + } clearNotifications(ROWS_ALL, true /* closeShade */); }); footerView.setManageButtonClickListener(v -> { @@ -5701,6 +5677,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return mCheckForLeavebehind; } + void setDismissListener (DismissListener listener) { + mDismissListener = listener; + } + void setDismissAllAnimationListener(DismissAllAnimationListener dismissAllAnimationListener) { mDismissAllAnimationListener = dismissAllAnimationListener; } @@ -5709,6 +5689,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump; } + void setFooterDismissListener(FooterDismissListener listener) { + mFooterDismissListener = listener; + } + + public void setRemoteInputManager(NotificationRemoteInputManager remoteInputManager) { + mRemoteInputManager = remoteInputManager; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -6312,39 +6300,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable /** Only rows where entry.isHighPriority() is false. */ public static final int ROWS_GENTLE = 2; - /** - * Enum for UiEvent logged from this class - */ - enum NotificationPanelEvent implements UiEventLogger.UiEventEnum { - INVALID(0), - @UiEvent(doc = "User dismissed all notifications from notification panel.") - DISMISS_ALL_NOTIFICATIONS_PANEL(312), - @UiEvent(doc = "User dismissed all silent notifications from notification panel.") - DISMISS_SILENT_NOTIFICATIONS_PANEL(314); - private final int mId; - NotificationPanelEvent(int id) { - mId = id; - } - @Override public int getId() { - return mId; - } + interface KeyguardBypassEnabledProvider { + boolean getBypassEnabled(); + } - public static UiEventLogger.UiEventEnum fromSelection(@SelectedRows int selection) { - if (selection == ROWS_ALL) { - return DISMISS_ALL_NOTIFICATIONS_PANEL; - } - if (selection == ROWS_GENTLE) { - return DISMISS_SILENT_NOTIFICATIONS_PANEL; - } - if (NotificationStackScrollLayout.DEBUG) { - throw new IllegalArgumentException("Unexpected selection" + selection); - } - return INVALID; - } + interface DismissListener { + void onDismiss(@SelectedRows int selectedRows); } - interface KeyguardBypassEnabledProvider { - boolean getBypassEnabled(); + interface FooterDismissListener { + void onDismiss(); } interface DismissAllAnimationListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 88b7fab02b3e..231ff2cec8f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -39,6 +39,7 @@ import android.service.notification.StatusBarNotification; import android.util.Log; import android.util.Pair; import android.view.Display; +import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -47,6 +48,8 @@ import android.widget.FrameLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; @@ -65,16 +68,17 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -90,6 +94,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView; import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; @@ -122,6 +127,7 @@ import kotlin.Unit; @StatusBarComponent.StatusBarScope public class NotificationStackScrollLayoutController { private static final String TAG = "StackScrollerController"; + private static final boolean DEBUG = false; private final boolean mAllowLongPress; private final NotificationGutsManager mNotificationGutsManager; @@ -142,6 +148,11 @@ public class NotificationStackScrollLayoutController { private final NotifCollection mNotifCollection; private final NotificationEntryManager mNotificationEntryManager; private final IStatusBarService mIStatusBarService; + private final UiEventLogger mUiEventLogger; + private final ForegroundServiceDismissalFeatureController mFgFeatureController; + private final ForegroundServiceSectionController mFgServicesSectionController; + private final LayoutInflater mLayoutInflater; + private final NotificationRemoteInputManager mRemoteInputManager; private final KeyguardMediaController mKeyguardMediaController; private final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardBypassController mKeyguardBypassController; @@ -156,6 +167,7 @@ public class NotificationStackScrollLayoutController { private NotificationSwipeHelper mSwipeHelper; private boolean mShowEmptyShadeView; private int mBarState; + private HeadsUpAppearanceController mHeadsUpAppearanceController; private final NotificationListContainerImpl mNotificationListContainer = new NotificationListContainerImpl(); @@ -497,29 +509,30 @@ public class NotificationStackScrollLayoutController { private final OnHeadsUpChangedListener mOnHeadsUpChangedListener = new OnHeadsUpChangedListener() { - @Override - public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { - mView.setInHeadsUpPinnedMode(inPinnedMode); - } - - @Override - public void onHeadsUpPinned(NotificationEntry entry) { - - } + @Override + public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { + mView.setInHeadsUpPinnedMode(inPinnedMode); + } - @Override - public void onHeadsUpUnPinned(NotificationEntry entry) { + @Override + public void onHeadsUpPinned(NotificationEntry entry) { + mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */); + } - } + @Override + public void onHeadsUpUnPinned(NotificationEntry entry) { + mNotificationRoundnessManager.updateView(entry.getRow(), true /* animate */); + } - @Override - public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - long numEntries = mHeadsUpManager.getAllEntries().count(); - NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); - mView.setNumHeadsUp(numEntries); - mView.setTopHeadsUpEntry(topEntry); - } - }; + @Override + public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { + long numEntries = mHeadsUpManager.getAllEntries().count(); + NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); + mView.setNumHeadsUp(numEntries); + mView.setTopHeadsUpEntry(topEntry); + mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */); + } + }; private final ZenModeController.Callback mZenModeControllerCallback = new ZenModeController.Callback() { @@ -558,7 +571,12 @@ public class NotificationStackScrollLayoutController { NotifPipeline notifPipeline, NotifCollection notifCollection, NotificationEntryManager notificationEntryManager, - IStatusBarService iStatusBarService) { + IStatusBarService iStatusBarService, + UiEventLogger uiEventLogger, + ForegroundServiceDismissalFeatureController fgFeatureController, + ForegroundServiceSectionController fgServicesSectionController, + LayoutInflater layoutInflater, + NotificationRemoteInputManager remoteInputManager) { mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; @@ -599,6 +617,11 @@ public class NotificationStackScrollLayoutController { mNotifCollection = notifCollection; mNotificationEntryManager = notificationEntryManager; mIStatusBarService = iStatusBarService; + mUiEventLogger = uiEventLogger; + mFgFeatureController = fgFeatureController; + mFgServicesSectionController = fgServicesSectionController; + mLayoutInflater = layoutInflater; + mRemoteInputManager = remoteInputManager; } public void attach(NotificationStackScrollLayout view) { @@ -607,6 +630,17 @@ public class NotificationStackScrollLayoutController { mView.setTouchHandler(new TouchHandler()); mView.setStatusBar(mStatusBar); mView.setDismissAllAnimationListener(this::onAnimationEnd); + mView.setDismissListener((selection) -> mUiEventLogger.log( + NotificationPanelEvent.fromSelection(selection))); + mView.setFooterDismissListener(() -> + mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES)); + mView.setRemoteInputManager(mRemoteInputManager); + + if (mFgFeatureController.isForegroundServiceDismissalEnabled()) { + mView.initializeForegroundServiceSection( + (ForegroundServiceDungeonView) mFgServicesSectionController.createView( + mLayoutInflater)); + } mSwipeHelper = mNotificationSwipeHelperBuilder .setSwipeDirection(SwipeHelper.X) @@ -633,7 +667,6 @@ public class NotificationStackScrollLayoutController { mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled, mSwipeHelper); - mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here? mHeadsUpManager.addListener(mOnHeadsUpChangedListener); mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed); mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); @@ -707,6 +740,7 @@ public class NotificationStackScrollLayoutController { } public void setHeadsUpAppearanceController(HeadsUpAppearanceController controller) { + mHeadsUpAppearanceController = controller; mView.setHeadsUpAppearanceController(controller); } @@ -755,6 +789,7 @@ public class NotificationStackScrollLayoutController { public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) { mView.setTrackingHeadsUp(expandableNotificationRow); + mNotificationRoundnessManager.setTrackingHeadsUp(expandableNotificationRow); } public void wakeUpFromPulse() { @@ -1293,6 +1328,37 @@ public class NotificationStackScrollLayoutController { return mDynamicPrivacyController.isInLockedDownShade(); } + /** + * Enum for UiEvent logged from this class + */ + enum NotificationPanelEvent implements UiEventLogger.UiEventEnum { + INVALID(0), + @UiEvent(doc = "User dismissed all notifications from notification panel.") + DISMISS_ALL_NOTIFICATIONS_PANEL(312), + @UiEvent(doc = "User dismissed all silent notifications from notification panel.") + DISMISS_SILENT_NOTIFICATIONS_PANEL(314); + private final int mId; + NotificationPanelEvent(int id) { + mId = id; + } + @Override public int getId() { + return mId; + } + + public static UiEventLogger.UiEventEnum fromSelection(@SelectedRows int selection) { + if (selection == ROWS_ALL) { + return DISMISS_ALL_NOTIFICATIONS_PANEL; + } + if (selection == NotificationStackScrollLayout.ROWS_GENTLE) { + return DISMISS_SILENT_NOTIFICATIONS_PANEL; + } + if (NotificationStackScrollLayoutController.DEBUG) { + throw new IllegalArgumentException("Unexpected selection" + selection); + } + return INVALID; + } + } + private class NotificationListContainerImpl implements NotificationListContainer { @Override @@ -1408,7 +1474,10 @@ public class NotificationStackScrollLayoutController { @Override public void bindRow(ExpandableNotificationRow row) { - mView.bindRow(row); + row.setHeadsUpAnimatingAwayListener(animatingAway -> { + mNotificationRoundnessManager.updateView(row, false); + mHeadsUpAppearanceController.updateHeader(row.getEntry()); + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 11ceedf79227..77ae059a0cb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -502,7 +502,7 @@ public class LockscreenLockIconController { * @return true if the visibility changed */ private boolean updateIconVisibility() { - boolean onAodOrDocked = mStatusBarStateController.isDozing() && mDocked; + boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked; boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; boolean fingerprintOrBypass = mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled(); 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 31c1a5e5a9aa..8254b7f5b32a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1848,7 +1848,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController.setPanelExpanded(isExpanded); if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { if (DEBUG) { - Log.v(TAG, "clearing notification effects from setExpandedHeight"); + Log.v(TAG, "clearing notification effects from Height"); } clearNotificationEffects(); } @@ -2809,6 +2809,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + Trace.beginSection("StatusBar#onReceive"); if (DEBUG) Log.v(TAG, "onReceive: " + intent); String action = intent.getAction(); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { @@ -2833,7 +2834,8 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationShadeWindowController.setNotTouchable(false); } if (mBubbleController.isStackExpanded()) { - mBubbleController.collapseStack(); + // Post to main thread handler, since updating the UI. + mMainThreadHandler.post(() -> mBubbleController.collapseStack()); } finishBarAnimations(); resetUserExpandedStates(); @@ -2841,6 +2843,7 @@ public class StatusBar extends SystemUI implements DemoMode, else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) { mQSPanel.showDeviceMonitoringDialog(); } + Trace.endSection(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java new file mode 100644 index 000000000000..7a78c157e5b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv; + +import android.Manifest; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.UserHandle; +import android.util.Log; + +import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.CommandQueue; + +import javax.inject.Inject; + +/** + * Offers control methods for the notification panel handler on TV devices. + */ +@SysUISingleton +public class TvNotificationPanel extends SystemUI implements CommandQueue.Callbacks { + private static final String TAG = "TvNotificationPanel"; + private final CommandQueue mCommandQueue; + private final String mNotificationHandlerPackage; + + @Inject + public TvNotificationPanel(Context context, CommandQueue commandQueue) { + super(context); + mCommandQueue = commandQueue; + mNotificationHandlerPackage = mContext.getResources().getString( + com.android.internal.R.string.config_notificationHandlerPackage); + } + + @Override + public void start() { + mCommandQueue.addCallback(this); + } + + @Override + public void togglePanel() { + if (!mNotificationHandlerPackage.isEmpty()) { + startNotificationHandlerActivity( + new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL)); + } + } + + @Override + public void animateExpandNotificationsPanel() { + if (!mNotificationHandlerPackage.isEmpty()) { + startNotificationHandlerActivity( + new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL)); + } + } + + @Override + public void animateCollapsePanels(int flags, boolean force) { + if (!mNotificationHandlerPackage.isEmpty() + && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) { + Intent closeNotificationIntent = new Intent( + NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL); + closeNotificationIntent.setPackage(mNotificationHandlerPackage); + mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT); + } + } + + /** + * Starts the activity intent if all of the following are true + * <ul> + * <li> the notification handler package is a system component </li> + * <li> the provided intent is handled by the notification handler package </li> + * <li> the notification handler requests the + * {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission for the given intent</li> + * </ul> + * + * @param intent The intent for starting the desired activity + */ + private void startNotificationHandlerActivity(Intent intent) { + intent.setPackage(mNotificationHandlerPackage); + PackageManager pm = mContext.getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY); + if (ri != null && ri.activityInfo != null) { + if (ri.activityInfo.permission != null && ri.activityInfo.permission.equals( + Manifest.permission.STATUS_BAR_SERVICE)) { + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } else { + Log.e(TAG, + "Not launching notification handler activity: Notification handler does " + + "not require the STATUS_BAR_SERVICE permission for intent " + + intent.getAction()); + } + } else { + Log.e(TAG, + "Not launching notification handler activity: Could not resolve activityInfo " + + "for intent " + + intent.getAction()); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index bcfff60dadf3..2795857383e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -17,13 +17,9 @@ package com.android.systemui.statusbar.tv; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.UserHandle; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; @@ -49,9 +45,6 @@ import dagger.Lazy; @SysUISingleton public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { - private static final String ACTION_OPEN_TV_NOTIFICATIONS_PANEL = - "com.android.tv.action.OPEN_NOTIFICATIONS_PANEL"; - private final CommandQueue mCommandQueue; private final Lazy<AssistManager> mAssistManagerLazy; @@ -74,24 +67,11 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { // If the system process isn't there we're doomed anyway. } - if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) { + if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) { // Creating AudioRecordingDisclosureBar and just letting it run new AudioRecordingDisclosureBar(mContext); } - } - - @Override - public void animateExpandNotificationsPanel() { - startSystemActivity(new Intent(ACTION_OPEN_TV_NOTIFICATIONS_PANEL)); - } - private void startSystemActivity(Intent intent) { - PackageManager pm = mContext.getPackageManager(); - ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY); - if (ri != null && ri.activityInfo != null) { - intent.setPackage(ri.activityInfo.packageName); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index da7713acfc32..ce125f3fdce0 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -28,6 +28,7 @@ import android.content.Context; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.view.KeyEvent; import com.android.internal.annotations.VisibleForTesting; @@ -56,6 +57,7 @@ import com.android.wm.shell.protolog.ShellProtoLogImpl; import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; +import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.Arrays; import java.util.Optional; @@ -66,7 +68,8 @@ import javax.inject.Inject; * Proxy in SysUiScope to delegate events to controllers in WM Shell library. */ @SysUISingleton -public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTraceProto> { +public final class WMShell extends SystemUI + implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> { private final CommandQueue mCommandQueue; private final DisplayImeController mDisplayImeController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -100,6 +103,7 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr ProtoTracer protoTracer) { super(context); mCommandQueue = commandQueue; + mCommandQueue.addCallback(this); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mActivityManagerWrapper = activityManagerWrapper; mDisplayImeController = displayImeController; @@ -293,31 +297,43 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { // Handle commands if provided + if (handleLoggingCommand(args, pw)) { + return; + } + + // Dump WMShell stuff here if no commands were handled + } + + @Override + public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) { + PrintWriter pw = new PrintWriter(new ParcelFileDescriptor.AutoCloseOutputStream(outFd)); + handleLoggingCommand(args, pw); + pw.flush(); + pw.close(); + } + + private boolean handleLoggingCommand(String[] args, PrintWriter pw) { + ShellProtoLogImpl protoLogImpl = ShellProtoLogImpl.getSingleInstance(); for (int i = 0; i < args.length; i++) { switch (args[i]) { - case "enable-text-logging": { + case "enable-text": { String[] groups = Arrays.copyOfRange(args, i + 1, args.length); - startTextLogging(groups); - pw.println("Starting logging on groups: " + Arrays.toString(groups)); - return; + int result = protoLogImpl.startTextLogging(mContext, groups, pw); + if (result == 0) { + pw.println("Starting logging on groups: " + Arrays.toString(groups)); + } + return true; } - case "disable-text-logging": { + case "disable-text": { String[] groups = Arrays.copyOfRange(args, i + 1, args.length); - stopTextLogging(groups); - pw.println("Stopping logging on groups: " + Arrays.toString(groups)); - return; + int result = protoLogImpl.stopTextLogging(groups, pw); + if (result == 0) { + pw.println("Stopping logging on groups: " + Arrays.toString(groups)); + } + return true; } } } - - // Dump WMShell stuff here if no commands were handled - } - - private void startTextLogging(String... groups) { - ShellProtoLogImpl.getSingleInstance().startTextLogging(mContext, groups); - } - - private void stopTextLogging(String... groups) { - ShellProtoLogImpl.getSingleInstance().stopTextLogging(groups); + return false; } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 3faa8a7996b7..7c129ac92fe3 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -17,6 +17,7 @@ package com.android.systemui.wmshell; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Handler; import android.util.DisplayMetrics; import android.view.IWindowManager; @@ -76,8 +77,9 @@ public abstract class WMShellBaseModule { @SysUISingleton @Provides - static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger) { - return new PipUiEventLogger(uiEventLogger); + static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger, + PackageManager packageManager) { + return new PipUiEventLogger(uiEventLogger, packageManager); } @SysUISingleton diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index c8566c599108..7cebc9ff6345 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -46,6 +46,7 @@ import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintSensorProperties; import android.os.Bundle; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -69,6 +70,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import javax.inject.Provider; + @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest @@ -82,6 +85,14 @@ public class AuthControllerTest extends SysuiTestCase { private AuthDialog mDialog1; @Mock private AuthDialog mDialog2; + @Mock + private CommandQueue mCommandQueue; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private FingerprintManager mFingerprintManager; + @Mock + private UdfpsController mUdfpsController; private TestableAuthController mAuthController; @@ -104,8 +115,16 @@ public class AuthControllerTest extends SysuiTestCase { when(mDialog1.isAllowDeviceCredentials()).thenReturn(false); when(mDialog2.isAllowDeviceCredentials()).thenReturn(false); - mAuthController = new TestableAuthController(context, mock(CommandQueue.class), - mock(StatusBarStateController.class), new MockInjector()); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + FingerprintSensorProperties prop = new FingerprintSensorProperties( + 1, FingerprintSensorProperties.TYPE_UDFPS, true, 1); + List<FingerprintSensorProperties> props = new ArrayList<>(); + props.add(prop); + when(mFingerprintManager.getSensorProperties()).thenReturn(props); + + mAuthController = new TestableAuthController(context, mCommandQueue, + mStatusBarStateController, new MockInjector(), + () -> mUdfpsController); mAuthController.start(); } @@ -463,6 +482,27 @@ public class AuthControllerTest extends SysuiTestCase { eq(null) /* credentialAttestation */); } + @Test + public void testOnAodInterrupt() { + final int pos = 10; + mAuthController.onAodInterrupt(pos, pos); + verify(mUdfpsController).onAodInterrupt(eq(pos), eq(pos)); + } + + @Test + public void testOnBiometricAuthenticated_OnCancelAodInterrupt() { + showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FINGERPRINT); + mAuthController.onBiometricAuthenticated(); + verify(mUdfpsController).onCancelAodInterrupt(); + } + + @Test + public void testOnBiometricError_OnCancelAodInterrupt() { + showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FINGERPRINT); + mAuthController.onBiometricError(0, 0, 0); + verify(mUdfpsController).onCancelAodInterrupt(); + } + // Helpers private void showDialog(int authenticators, int biometricModality) { @@ -504,8 +544,10 @@ public class AuthControllerTest extends SysuiTestCase { private PromptInfo mLastBiometricPromptInfo; TestableAuthController(Context context, CommandQueue commandQueue, - StatusBarStateController statusBarStateController, Injector injector) { - super(context, commandQueue, statusBarStateController, injector); + StatusBarStateController statusBarStateController, Injector injector, + Provider<UdfpsController> udfpsControllerFactory) { + super(context, commandQueue, statusBarStateController, injector, + udfpsControllerFactory); } @Override @@ -536,7 +578,7 @@ public class AuthControllerTest extends SysuiTestCase { @Override FingerprintManager getFingerprintManager(Context context) { - return mock(FingerprintManager.class); + return mFingerprintManager; } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java index c591c1bd42bc..9fd9b470a83b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java @@ -59,6 +59,7 @@ public class DozeConfigurationUtil { when(config.doubleTapSensorType()).thenReturn(null); when(config.tapSensorType()).thenReturn(null); when(config.longPressSensorType()).thenReturn(null); + when(config.udfpsLongPressSensorType()).thenReturn(null); when(config.tapGestureEnabled(anyInt())).thenReturn(true); when(config.tapSensorAvailable()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 1ed58714fb9f..3ae02a469095 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -37,6 +37,7 @@ import android.view.Display; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.phone.DozeParameters; @@ -76,6 +77,8 @@ public class DozeTriggersTest extends SysuiTestCase { private DockManager mDockManager; @Mock private ProximitySensor.ProximityCheck mProximityCheck; + @Mock + private AuthController mAuthController; private DozeTriggers mTriggers; private FakeSensorManager mSensors; @@ -100,7 +103,8 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters, asyncSensorManager, wakeLock, mDockManager, mProximitySensor, - mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings()); + mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(), + mAuthController); mTriggers.setDozeMachine(mMachine); waitForSensorManager(); } @@ -186,6 +190,15 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers.onSensor(DozeLog.REASON_SENSOR_TAP, 100, 100, null); } + @Test + public void testOnSensor_Fingerprint() { + final int screenX = 100; + final int screenY = 100; + final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; + mTriggers.onSensor(reason, screenX, screenY, null); + verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY)); + } + private void waitForSensorManager() { mExecutor.runAllReady(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt index 887cc777d4fe..2d90cc4f6712 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt @@ -225,7 +225,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { @Test fun remoteSession_loadedEventNotFiltered() { - // GIVEN a remove session + // GIVEN a remote session whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) val controllers = listOf(controller1) whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) @@ -260,6 +260,22 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { } @Test + fun remoteAndLocalSessions_remoteSessionWithoutNotification() { + // GIVEN remote and local sessions + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered because there isn't a notification for the remote + // session. + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test fun remoteAndLocalHaveDifferentKeys_localLoadedEventFiltered() { // GIVEN remote and local sessions val key1 = "KEY_1" @@ -285,6 +301,29 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { } @Test + fun remoteAndLocalHaveDifferentKeys_remoteSessionWithoutNotification() { + // GIVEN remote and local sessions + val key1 = "KEY_1" + val key2 = "KEY_2" + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key1, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + } + + @Test fun multipleRemoteSessions_loadedEventNotFiltered() { // GIVEN two remote sessions whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java new file mode 100644 index 000000000000..0e376bd356a2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2020 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.media.dialog; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.graphics.drawable.Icon; +import android.testing.AndroidTestingRunner; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.core.graphics.drawable.IconCompat; +import androidx.test.filters.SmallTest; + +import com.android.settingslib.media.LocalMediaManager; +import com.android.settingslib.media.MediaDevice; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class MediaOutputAdapterTest extends SysuiTestCase { + + private static final String TEST_DEVICE_NAME_1 = "test_device_name_1"; + private static final String TEST_DEVICE_NAME_2 = "test_device_name_2"; + private static final String TEST_DEVICE_ID_1 = "test_device_id_1"; + private static final String TEST_DEVICE_ID_2 = "test_device_id_2"; + + // Mock + private MediaOutputController mMediaOutputController = mock(MediaOutputController.class); + private MediaDevice mMediaDevice1 = mock(MediaDevice.class); + private MediaDevice mMediaDevice2 = mock(MediaDevice.class); + private Icon mIcon = mock(Icon.class); + private IconCompat mIconCompat = mock(IconCompat.class); + + private MediaOutputAdapter mMediaOutputAdapter; + private MediaOutputAdapter.MediaDeviceViewHolder mViewHolder; + private List<MediaDevice> mMediaDevices = new ArrayList<>(); + + @Before + public void setUp() { + mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new FrameLayout(mContext), 0); + + when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); + when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); + when(mMediaOutputController.isZeroMode()).thenReturn(false); + when(mMediaOutputController.isTransferring()).thenReturn(false); + when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat); + when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat); + when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1); + when(mIconCompat.toIcon(mContext)).thenReturn(mIcon); + when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1); + when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1); + when(mMediaDevice2.getName()).thenReturn(TEST_DEVICE_NAME_2); + when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_ID_2); + when(mMediaDevice1.getState()).thenReturn( + LocalMediaManager.MediaDeviceState.STATE_CONNECTED); + when(mMediaDevice2.getState()).thenReturn( + LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); + mMediaDevices.add(mMediaDevice1); + mMediaDevices.add(mMediaDevice2); + } + + @Test + public void getItemCount_nonZeroMode_isDeviceSize() { + assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size()); + } + + @Test + public void getItemCount_zeroMode_containExtraOneForPairNew() { + when(mMediaOutputController.isZeroMode()).thenReturn(true); + + assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size() + 1); + } + + @Test + public void onBindViewHolder_zeroMode_bindPairNew_verifyView() { + when(mMediaOutputController.isZeroMode()).thenReturn(true); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText( + R.string.media_output_dialog_pairing_new)); + } + + @Test + public void onBindViewHolder_bindConnectedDevice_verifyView() { + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); + } + + @Test + public void onBindViewHolder_bindDisconnectedBluetoothDevice_verifyView() { + when(mMediaDevice2.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE); + when(mMediaDevice2.isConnected()).thenReturn(false); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); + + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo( + mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2)); + } + + @Test + public void onBindViewHolder_bindFailedStateDevice_verifyView() { + when(mMediaDevice2.getState()).thenReturn( + LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText( + R.string.media_output_dialog_connect_failed)); + assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); + } + + @Test + public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() { + when(mMediaOutputController.isTransferring()).thenReturn(true); + when(mMediaDevice1.getState()).thenReturn( + LocalMediaManager.MediaDeviceState.STATE_CONNECTING); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); + } + + @Test + public void onBindViewHolder_inTransferring_bindNonTransferringDevice_verifyView() { + when(mMediaOutputController.isTransferring()).thenReturn(true); + when(mMediaDevice2.getState()).thenReturn( + LocalMediaManager.MediaDeviceState.STATE_CONNECTING); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); + } + + @Test + public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() { + when(mMediaOutputController.isZeroMode()).thenReturn(true); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); + mViewHolder.mFrameLayout.performClick(); + + verify(mMediaOutputController).launchBluetoothPairing(); + } + + @Test + public void onItemClick_clickDevice_verifyConnectDevice() { + assertThat(mMediaDevice2.getState()).isEqualTo( + LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); + mViewHolder.mFrameLayout.performClick(); + + verify(mMediaOutputController).connectDevice(mMediaDevice2); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java new file mode 100644 index 000000000000..42b21c61510a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2020 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.media.dialog; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.media.session.MediaSessionManager; +import android.os.Bundle; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.core.graphics.drawable.IconCompat; +import androidx.test.filters.SmallTest; + +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.media.MediaDevice; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.phone.ShadeController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class MediaOutputBaseDialogTest extends SysuiTestCase { + + private static final String TEST_PACKAGE = "test_package"; + + // Mock + private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class); + + private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class); + private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class); + private ShadeController mShadeController = mock(ShadeController.class); + private ActivityStarter mStarter = mock(ActivityStarter.class); + + private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl; + private MediaOutputController mMediaOutputController; + private int mHeaderIconRes; + private IconCompat mIconCompat; + private CharSequence mHeaderTitle; + private CharSequence mHeaderSubtitle; + + @Before + public void setUp() { + mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, + mMediaOutputController); + mMediaOutputBaseDialogImpl.onCreate(new Bundle()); + } + + @Test + public void refresh_withIconRes_iconIsVisible() { + mHeaderIconRes = 1; + mMediaOutputBaseDialogImpl.refresh(); + final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.header_icon); + + assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void refresh_withIconCompat_iconIsVisible() { + mIconCompat = mock(IconCompat.class); + mMediaOutputBaseDialogImpl.refresh(); + final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.header_icon); + + assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void refresh_noIcon_iconLayoutNotVisible() { + mHeaderIconRes = 0; + mIconCompat = null; + mMediaOutputBaseDialogImpl.refresh(); + final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.header_icon); + + assertThat(view.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void refresh_checkTitle() { + mHeaderTitle = "test_string"; + + mMediaOutputBaseDialogImpl.refresh(); + final TextView titleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.header_title); + + assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(titleView.getText()).isEqualTo(mHeaderTitle); + } + + @Test + public void refresh_withSubtitle_checkSubtitle() { + mHeaderSubtitle = "test_string"; + + mMediaOutputBaseDialogImpl.refresh(); + final TextView subtitleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.header_subtitle); + + assertThat(subtitleView.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(subtitleView.getText()).isEqualTo(mHeaderSubtitle); + } + + @Test + public void refresh_noSubtitle_checkSubtitle() { + mMediaOutputBaseDialogImpl.refresh(); + final TextView subtitleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.header_subtitle); + + assertThat(subtitleView.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void refresh_inDragging_notUpdateAdapter() { + when(mMediaOutputBaseAdapter.isDragging()).thenReturn(true); + mMediaOutputBaseDialogImpl.refresh(); + + verify(mMediaOutputBaseAdapter, never()).notifyDataSetChanged(); + } + + @Test + public void refresh_notInDragging_verifyUpdateAdapter() { + when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false); + mMediaOutputBaseDialogImpl.refresh(); + + verify(mMediaOutputBaseAdapter).notifyDataSetChanged(); + } + + @Test + public void refresh_with6Devices_checkBottomPaddingVisibility() { + for (int i = 0; i < 6; i++) { + mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class)); + } + mMediaOutputBaseDialogImpl.refresh(); + final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.list_bottom_padding); + + assertThat(view.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void refresh_with5Devices_checkBottomPaddingVisibility() { + for (int i = 0; i < 5; i++) { + mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class)); + } + mMediaOutputBaseDialogImpl.refresh(); + final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.list_bottom_padding); + + assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); + } + + class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog { + + MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) { + super(context, mediaOutputController); + + mAdapter = mMediaOutputBaseAdapter; + } + + int getHeaderIconRes() { + return mHeaderIconRes; + } + + IconCompat getHeaderIcon() { + return mIconCompat; + } + + int getHeaderIconSize() { + return 10; + } + + CharSequence getHeaderText() { + return mHeaderTitle; + } + + CharSequence getHeaderSubtitle() { + return mHeaderSubtitle; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java new file mode 100644 index 000000000000..0dcdecfdaadb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2020 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.media.dialog; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.media.MediaDescription; +import android.media.MediaMetadata; +import android.media.RoutingSessionInfo; +import android.media.session.MediaController; +import android.media.session.MediaSessionManager; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.media.LocalMediaManager; +import com.android.settingslib.media.MediaDevice; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.phone.ShadeController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class MediaOutputControllerTest extends SysuiTestCase { + + private static final String TEST_PACKAGE_NAME = "com.test.package.name"; + private static final String TEST_DEVICE_1_ID = "test_device_1_id"; + private static final String TEST_DEVICE_2_ID = "test_device_2_id"; + private static final String TEST_ARTIST = "test_artist"; + private static final String TEST_SONG = "test_song"; + private static final String TEST_SESSION_ID = "test_session_id"; + private static final String TEST_SESSION_NAME = "test_session_name"; + // Mock + private MediaController mMediaController = mock(MediaController.class); + private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class); + private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager = + mock(CachedBluetoothDeviceManager.class); + private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class); + private MediaOutputController.Callback mCb = mock(MediaOutputController.Callback.class); + private MediaDevice mMediaDevice1 = mock(MediaDevice.class); + private MediaDevice mMediaDevice2 = mock(MediaDevice.class); + private MediaMetadata mMediaMetadata = mock(MediaMetadata.class); + private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class); + private ShadeController mShadeController = mock(ShadeController.class); + private ActivityStarter mStarter = mock(ActivityStarter.class); + + private Context mSpyContext; + private MediaOutputController mMediaOutputController; + private LocalMediaManager mLocalMediaManager; + private List<MediaController> mMediaControllers = new ArrayList<>(); + private List<MediaDevice> mMediaDevices = new ArrayList<>(); + private MediaDescription mMediaDescription; + private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>(); + + @Before + public void setUp() { + mSpyContext = spy(mContext); + when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE_NAME); + mMediaControllers.add(mMediaController); + when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers); + doReturn(mMediaSessionManager).when(mSpyContext).getSystemService( + MediaSessionManager.class); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn( + mCachedBluetoothDeviceManager); + mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager); + mMediaOutputController.mLocalMediaManager = mLocalMediaManager; + MediaDescription.Builder builder = new MediaDescription.Builder(); + builder.setTitle(TEST_SONG); + builder.setSubtitle(TEST_ARTIST); + mMediaDescription = builder.build(); + when(mMediaMetadata.getDescription()).thenReturn(mMediaDescription); + when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID); + when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); + mMediaDevices.add(mMediaDevice1); + mMediaDevices.add(mMediaDevice2); + } + + @Test + public void start_verifyLocalMediaManagerInit() { + mMediaOutputController.start(mCb); + + verify(mLocalMediaManager).registerCallback(mMediaOutputController); + verify(mLocalMediaManager).startScan(); + } + + @Test + public void stop_verifyLocalMediaManagerDeinit() { + mMediaOutputController.start(mCb); + reset(mLocalMediaManager); + + mMediaOutputController.stop(); + + verify(mLocalMediaManager).unregisterCallback(mMediaOutputController); + verify(mLocalMediaManager).stopScan(); + } + + @Test + public void start_withPackageName_verifyMediaControllerInit() { + mMediaOutputController.start(mCb); + + verify(mMediaController).registerCallback(any()); + } + + @Test + public void start_withoutPackageName_verifyMediaControllerInit() { + mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager, + mLocalBluetoothManager, mShadeController, mStarter); + + mMediaOutputController.start(mCb); + + verify(mMediaController, never()).registerCallback(any()); + } + + @Test + public void stop_withPackageName_verifyMediaControllerDeinit() { + mMediaOutputController.start(mCb); + reset(mMediaController); + + mMediaOutputController.stop(); + + verify(mMediaController).unregisterCallback(any()); + } + + @Test + public void stop_withoutPackageName_verifyMediaControllerDeinit() { + mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager, + mLocalBluetoothManager, mShadeController, mStarter); + mMediaOutputController.start(mCb); + + mMediaOutputController.stop(); + + verify(mMediaController, never()).unregisterCallback(any()); + } + + @Test + public void onDeviceListUpdate_verifyDeviceListCallback() { + mMediaOutputController.start(mCb); + reset(mCb); + + mMediaOutputController.onDeviceListUpdate(mMediaDevices); + final List<MediaDevice> devices = new ArrayList<>(mMediaOutputController.getMediaDevices()); + + assertThat(devices.containsAll(mMediaDevices)).isTrue(); + assertThat(devices.size()).isEqualTo(mMediaDevices.size()); + verify(mCb).onRouteChanged(); + } + + @Test + public void onSelectedDeviceStateChanged_verifyCallback() { + mMediaOutputController.start(mCb); + reset(mCb); + + mMediaOutputController.onSelectedDeviceStateChanged(mMediaDevice1, + LocalMediaManager.MediaDeviceState.STATE_CONNECTED); + + verify(mCb).onRouteChanged(); + } + + @Test + public void onDeviceAttributesChanged_verifyCallback() { + mMediaOutputController.start(mCb); + reset(mCb); + + mMediaOutputController.onDeviceAttributesChanged(); + + verify(mCb).onRouteChanged(); + } + + @Test + public void onRequestFailed_verifyCallback() { + mMediaOutputController.start(mCb); + reset(mCb); + + mMediaOutputController.onRequestFailed(0 /* reason */); + + verify(mCb).onRouteChanged(); + } + + @Test + public void getHeaderTitle_withoutMetadata_returnDefaultString() { + when(mMediaController.getMetadata()).thenReturn(null); + + mMediaOutputController.start(mCb); + + assertThat(mMediaOutputController.getHeaderTitle()).isEqualTo( + mContext.getText(R.string.controls_media_title)); + } + + @Test + public void getHeaderTitle_withMetadata_returnSongName() { + when(mMediaController.getMetadata()).thenReturn(mMediaMetadata); + + mMediaOutputController.start(mCb); + + assertThat(mMediaOutputController.getHeaderTitle()).isEqualTo(TEST_SONG); + } + + @Test + public void getHeaderSubTitle_withoutMetadata_returnNull() { + when(mMediaController.getMetadata()).thenReturn(null); + + mMediaOutputController.start(mCb); + + assertThat(mMediaOutputController.getHeaderSubTitle()).isNull(); + } + + @Test + public void getHeaderSubTitle_withMetadata_returnArtistName() { + when(mMediaController.getMetadata()).thenReturn(mMediaMetadata); + + mMediaOutputController.start(mCb); + + assertThat(mMediaOutputController.getHeaderSubTitle()).isEqualTo(TEST_ARTIST); + } + + @Test + public void connectDevice_verifyConnect() { + mMediaOutputController.connectDevice(mMediaDevice1); + + // Wait for background thread execution + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + verify(mLocalMediaManager).connectDevice(mMediaDevice1); + } + + @Test + public void getActiveRemoteMediaDevice_isSystemSession_returnSession() { + when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID); + when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME); + when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100); + when(mRemoteSessionInfo.getVolume()).thenReturn(10); + when(mRemoteSessionInfo.isSystemSession()).thenReturn(false); + mRoutingSessionInfos.add(mRemoteSessionInfo); + when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos); + + assertThat(mMediaOutputController.getActiveRemoteMediaDevices()).containsExactly( + mRemoteSessionInfo); + } + + @Test + public void getActiveRemoteMediaDevice_notSystemSession_returnEmpty() { + when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID); + when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME); + when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100); + when(mRemoteSessionInfo.getVolume()).thenReturn(10); + when(mRemoteSessionInfo.isSystemSession()).thenReturn(true); + mRoutingSessionInfos.add(mRemoteSessionInfo); + when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos); + + assertThat(mMediaOutputController.getActiveRemoteMediaDevices()).isEmpty(); + } + + @Test + public void isZeroMode_onlyFromPhoneOutput_returnTrue() { + // Multiple available devices + assertThat(mMediaOutputController.isZeroMode()).isFalse(); + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); + mMediaDevices.clear(); + mMediaDevices.add(mMediaDevice1); + mMediaOutputController.start(mCb); + mMediaOutputController.onDeviceListUpdate(mMediaDevices); + + assertThat(mMediaOutputController.isZeroMode()).isTrue(); + + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE); + + assertThat(mMediaOutputController.isZeroMode()).isTrue(); + + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE); + + assertThat(mMediaOutputController.isZeroMode()).isTrue(); + } + + @Test + public void isZeroMode_notFromPhoneOutput_returnFalse() { + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_UNKNOWN); + mMediaDevices.clear(); + mMediaDevices.add(mMediaDevice1); + mMediaOutputController.start(mCb); + mMediaOutputController.onDeviceListUpdate(mMediaDevices); + + assertThat(mMediaOutputController.isZeroMode()).isFalse(); + + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE); + + assertThat(mMediaOutputController.isZeroMode()).isFalse(); + + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE); + + assertThat(mMediaOutputController.isZeroMode()).isFalse(); + + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); + + assertThat(mMediaOutputController.isZeroMode()).isFalse(); + + when(mMediaDevice1.getDeviceType()).thenReturn( + MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE); + + assertThat(mMediaOutputController.isZeroMode()).isFalse(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java new file mode 100644 index 000000000000..a5dead0f3258 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2020 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.qs.customize; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.Bundle; +import android.testing.AndroidTestingRunner; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class TileAdapterDelegateTest extends SysuiTestCase { + + private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position; + private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position; + private static final int POSITION_STRING_ID = R.string.accessibility_qs_edit_position; + + @Mock + private TileAdapter.Holder mHolder; + + private AccessibilityNodeInfoCompat mInfo; + private TileAdapterDelegate mDelegate; + private View mView; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mView = new View(mContext); + mDelegate = new TileAdapterDelegate(); + mInfo = AccessibilityNodeInfoCompat.obtain(); + } + + @Test + public void testInfoNoSpecialActionsWhenNoHolder() { + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + for (AccessibilityNodeInfoCompat.AccessibilityActionCompat action : mInfo.getActionList()) { + if (action.getId() == MOVE_TO_POSITION_ID || action.getId() == ADD_TO_POSITION_ID + || action.getId() == AccessibilityNodeInfo.ACTION_CLICK) { + fail("It should not have special action " + action.getId()); + } + } + } + + @Test + public void testInfoNoSpecialActionsWhenCannotStartAccessibleAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(false); + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + for (AccessibilityNodeInfoCompat.AccessibilityActionCompat action : mInfo.getActionList()) { + if (action.getId() == MOVE_TO_POSITION_ID || action.getId() == ADD_TO_POSITION_ID + || action.getId() == AccessibilityNodeInfo.ACTION_CLICK) { + fail("It should not have special action " + action.getId()); + } + } + } + + @Test + public void testNoCollectionItemInfo() { + mInfo.setCollectionItemInfo( + AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(0, 1, 0, 1, false)); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(mInfo.getCollectionItemInfo()).isNull(); + } + + @Test + public void testStateDescriptionHasPositionForCurrentTile() { + mView.setTag(mHolder); + int position = 3; + when(mHolder.getLayoutPosition()).thenReturn(position); + when(mHolder.isCurrentTile()).thenReturn(true); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + String expectedString = mContext.getString(POSITION_STRING_ID, position); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(mInfo.getStateDescription()).isEqualTo(expectedString); + } + + @Test + public void testStateDescriptionEmptyForNotCurrentTile() { + mView.setTag(mHolder); + int position = 3; + when(mHolder.getLayoutPosition()).thenReturn(position); + when(mHolder.isCurrentTile()).thenReturn(false); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(mInfo.getStateDescription()).isEqualTo(""); + } + + @Test + public void testClickAddAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(true); + when(mHolder.canRemove()).thenReturn(false); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + + String expectedString = mContext.getString(R.string.accessibility_qs_edit_tile_add_action); + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK); + assertThat(action.getLabel().toString()).contains(expectedString); + } + + @Test + public void testClickRemoveAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(false); + when(mHolder.canRemove()).thenReturn(true); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + + String expectedString = mContext.getString( + R.string.accessibility_qs_edit_remove_tile_action); + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK); + assertThat(action.getLabel().toString()).contains(expectedString); + } + + @Test + public void testNoClickAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(false); + when(mHolder.canRemove()).thenReturn(false); + mInfo.addAction(AccessibilityNodeInfo.ACTION_CLICK); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + + AccessibilityNodeInfoCompat.AccessibilityActionCompat action = + getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK); + assertThat(action).isNull(); + } + + @Test + public void testAddToPositionAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(true); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(getActionForId(mInfo, ADD_TO_POSITION_ID)).isNotNull(); + } + + @Test + public void testNoAddToPositionAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.canAdd()).thenReturn(false); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(getActionForId(mInfo, ADD_TO_POSITION_ID)).isNull(); + } + + @Test + public void testMoveToPositionAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.isCurrentTile()).thenReturn(true); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(getActionForId(mInfo, MOVE_TO_POSITION_ID)).isNotNull(); + } + + @Test + public void testNoMoveToPositionAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + when(mHolder.isCurrentTile()).thenReturn(false); + + mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo); + assertThat(getActionForId(mInfo, MOVE_TO_POSITION_ID)).isNull(); + } + + @Test + public void testNoInteractionsWhenCannotTakeAccessibleAction() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(false); + + mDelegate.performAccessibilityAction(mView, AccessibilityNodeInfo.ACTION_CLICK, null); + mDelegate.performAccessibilityAction(mView, MOVE_TO_POSITION_ID, new Bundle()); + mDelegate.performAccessibilityAction(mView, ADD_TO_POSITION_ID, new Bundle()); + + verify(mHolder, never()).toggleState(); + verify(mHolder, never()).startAccessibleAdd(); + verify(mHolder, never()).startAccessibleMove(); + } + + @Test + public void testClickActionTogglesState() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + mDelegate.performAccessibilityAction(mView, AccessibilityNodeInfo.ACTION_CLICK, null); + + verify(mHolder).toggleState(); + } + + @Test + public void testAddToPositionActionStartsAccessibleAdd() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + mDelegate.performAccessibilityAction(mView, ADD_TO_POSITION_ID, null); + + verify(mHolder).startAccessibleAdd(); + } + + @Test + public void testMoveToPositionActionStartsAccessibleMove() { + mView.setTag(mHolder); + when(mHolder.canTakeAccessibleAction()).thenReturn(true); + + mDelegate.performAccessibilityAction(mView, MOVE_TO_POSITION_ID, null); + + verify(mHolder).startAccessibleMove(); + } + + private AccessibilityNodeInfoCompat.AccessibilityActionCompat getActionForId( + AccessibilityNodeInfoCompat info, int action) { + for (AccessibilityNodeInfoCompat.AccessibilityActionCompat a : info.getActionList()) { + if (a.getId() == action) { + return a; + } + } + return null; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java index d795cbac700f..cb3e5e262df1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java @@ -73,10 +73,10 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { TestableLooper.get(this)); mFirst = testHelper.createRow(); mFirst.setHeadsUpAnimatingAwayListener(animatingAway - -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway)); + -> mRoundnessManager.updateView(mFirst, false)); mSecond = testHelper.createRow(); mSecond.setHeadsUpAnimatingAwayListener(animatingAway - -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mSecond, animatingAway)); + -> mRoundnessManager.updateView(mSecond, false)); mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback); mRoundnessManager.setAnimatedChildren(mAnimatedChildren); mRoundnessManager.updateRoundedChildren(new NotificationSection[]{ @@ -160,12 +160,12 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { when(testHelper.getStatusBarStateController().isDozing()).thenReturn(true); row.setHeadsUp(true); - mRoundnessManager.onHeadsUpStateChanged(entry, true); + mRoundnessManager.updateView(entry.getRow(), false); Assert.assertEquals(1f, row.getCurrentBottomRoundness(), 0.0f); Assert.assertEquals(1f, row.getCurrentTopRoundness(), 0.0f); row.setHeadsUp(false); - mRoundnessManager.onHeadsUpStateChanged(entry, false); + mRoundnessManager.updateView(entry.getRow(), false); Assert.assertEquals(0f, row.getCurrentBottomRoundness(), 0.0f); Assert.assertEquals(0f, row.getCurrentTopRoundness(), 0.0f); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 81cbef7ff486..0d283ce03960 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -17,6 +17,7 @@ import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED; import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; +import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; @@ -43,8 +44,6 @@ import android.testing.TestableLooper; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; @@ -55,7 +54,6 @@ import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; @@ -97,7 +95,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private EmptyShadeView mEmptyShadeView; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; - @Mock private MetricsLogger mMetricsLogger; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; @Mock private NotificationSectionsManager mNotificationSectionsManager; @@ -106,8 +103,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationSwipeHelper mNotificationSwipeHelper; @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController; private int mOriginalInterruptionModelSetting; - private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake(); - @Before @UiThreadTest @@ -127,11 +122,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { NotificationBlockingHelperManager.class, mBlockingHelperManager); mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState); - mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); - mDependency.injectTestDependency(NotificationRemoteInputManager.class, - mRemoteInputManager); mDependency.injectMockDependency(ShadeController.class); - when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); + NotificationShelfController notificationShelfController = mock(NotificationShelfController.class); NotificationShelf notificationShelf = mock(NotificationShelf.class); @@ -140,6 +132,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { new NotificationSection[]{ mNotificationSection }); + when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); + // The actual class under test. You may need to work with this class directly when // testing anonymous class members of mStackScroller, like mMenuEventListener, // which refer to members of NotificationStackScrollLayout. The spy @@ -148,14 +142,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScrollerInternal = new NotificationStackScrollLayout( getContext(), null, - mNotificationRoundnessManager, - mStatusBarStateController, mNotificationSectionsManager, - mock(ForegroundServiceSectionController.class), - mock(ForegroundServiceDismissalFeatureController.class), mGroupMembershipManger, mGroupExpansionManager, - mUiEventLoggerFake + mStatusBarStateController ); mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider, mNotificationSwipeHelper); @@ -166,7 +156,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { when(mStackScrollLayoutController.getNoticationRoundessManager()) .thenReturn(mNotificationRoundnessManager); mStackScroller.setController(mStackScrollLayoutController); - + mStackScroller.setRemoteInputManager(mRemoteInputManager); + // Stub out functionality that isn't necessary to test. doNothing().when(mBar) .executeRunnableDismissingKeyguard(any(Runnable.class), @@ -380,18 +371,30 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testClearNotifications_All() { + final int[] numCalls = {0}; + final int[] selected = {-1}; + mStackScroller.setDismissListener(selectedRows -> { + numCalls[0]++; + selected[0] = selectedRows; + }); + mStackScroller.clearNotifications(ROWS_ALL, true); - assertEquals(1, mUiEventLoggerFake.numLogs()); - assertEquals(NotificationStackScrollLayout.NotificationPanelEvent - .DISMISS_ALL_NOTIFICATIONS_PANEL.getId(), mUiEventLoggerFake.eventId(0)); + assertEquals(1, numCalls[0]); + assertEquals(ROWS_ALL, selected[0]); } @Test public void testClearNotifications_Gentle() { + final int[] numCalls = {0}; + final int[] selected = {-1}; + mStackScroller.setDismissListener(selectedRows -> { + numCalls[0]++; + selected[0] = selectedRows; + }); + mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_GENTLE, false); - assertEquals(1, mUiEventLoggerFake.numLogs()); - assertEquals(NotificationStackScrollLayout.NotificationPanelEvent - .DISMISS_SILENT_NOTIFICATIONS_PANEL.getId(), mUiEventLoggerFake.eventId(0)); + assertEquals(1, numCalls[0]); + assertEquals(ROWS_GENTLE, selected[0]); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java index 60d6c530998a..d83417638cc5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; +import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -32,10 +33,12 @@ import static org.mockito.Mockito.when; import android.content.res.Resources; import android.metrics.LogMaker; import android.testing.AndroidTestingRunner; +import android.view.LayoutInflater; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SysuiTestCase; @@ -48,15 +51,20 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScrimController; @@ -111,6 +119,13 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { @Mock private NotifCollection mNotifCollection; @Mock private NotificationEntryManager mEntryManager; @Mock private IStatusBarService mIStatusBarService; + @Mock private UiEventLogger mUiEventLogger; + @Mock private ForegroundServiceDismissalFeatureController mFgFeatureController; + @Mock private ForegroundServiceSectionController mFgServicesSectionController; + @Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView; + @Mock private LayoutInflater mLayoutInflater; + @Mock private NotificationRemoteInputManager mRemoteInputManager; + @Mock private RemoteInputController mRemoteInputController; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; @@ -123,6 +138,9 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper); when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false); + when(mFgServicesSectionController.createView(mLayoutInflater)) + .thenReturn(mForegroundServiceDungeonView); + when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); mController = new NotificationStackScrollLayoutController( true, @@ -152,13 +170,17 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mNotifPipeline, mNotifCollection, mEntryManager, - mIStatusBarService + mIStatusBarService, + mUiEventLogger, + mFgFeatureController, + mFgServicesSectionController, + mLayoutInflater, + mRemoteInputManager ); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); } - @Test public void testAttach_viewAlreadyAttached() { mController.attach(mNotificationStackScrollLayout); @@ -270,15 +292,12 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); } - @Test - public void testOnMenuShownLogging() { ; - + public void testOnMenuShownLogging() { ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor = ArgumentCaptor.forClass(OnMenuEventListener.class); @@ -300,7 +319,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor = ArgumentCaptor.forClass(OnMenuEventListener.class); @@ -317,6 +335,39 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { MetricsProto.MetricsEvent.TYPE_ACTION)); } + @Test + public void testDismissListener() { + ArgumentCaptor<NotificationStackScrollLayout.DismissListener> + dismissListenerArgumentCaptor = ArgumentCaptor.forClass( + NotificationStackScrollLayout.DismissListener.class); + + mController.attach(mNotificationStackScrollLayout); + + verify(mNotificationStackScrollLayout).setDismissListener( + dismissListenerArgumentCaptor.capture()); + NotificationStackScrollLayout.DismissListener dismissListener = + dismissListenerArgumentCaptor.getValue(); + + dismissListener.onDismiss(ROWS_ALL); + verify(mUiEventLogger).log(NotificationPanelEvent.fromSelection(ROWS_ALL)); + } + + @Test + public void testForegroundDismissEnabled() { + when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(true); + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).initializeForegroundServiceSection( + mForegroundServiceDungeonView); + } + + @Test + public void testForegroundDismissaDisabled() { + when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(false); + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout, never()).initializeForegroundServiceSection( + any(ForegroundServiceDungeonView.class)); + } + private LogMaker logMatcher(int category, int type) { return argThat(new LogMatcher(category, type)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index 37ccac0b23b3..23c093033ae0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -179,7 +179,8 @@ public class DozeServiceHostTest extends SysuiTestCase { HashSet<Integer> reasonsThatDontPulse = new HashSet<>( Arrays.asList(DozeLog.REASON_SENSOR_PICKUP, DozeLog.REASON_SENSOR_DOUBLE_TAP, - DozeLog.REASON_SENSOR_TAP)); + DozeLog.REASON_SENSOR_TAP, + DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS)); doAnswer(invocation -> { DozeHost.PulseCallback callback = invocation.getArgument(0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java index 85b5d70c883c..06d0331543da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java @@ -18,11 +18,13 @@ package com.android.systemui.statusbar.phone; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.res.Resources; import android.view.View; +import android.view.View.OnAttachStateChangeListener; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -48,7 +50,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class LockscreenIconControllerTest extends SysuiTestCase { - private LockscreenLockIconController mLockIconController; @Mock private LockscreenGestureLogger mLockscreenGestureLogger; @Mock @@ -80,11 +81,15 @@ public class LockscreenIconControllerTest extends SysuiTestCase { @Mock private HeadsUpManagerPhone mHeadsUpManagerPhone; + private LockscreenLockIconController mLockIconController; + private OnAttachStateChangeListener mOnAttachStateChangeListener; + @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mLockIcon.getContext()).thenReturn(mContext); mLockIconController = new LockscreenLockIconController( mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils, mShadeController, mAccessibilityController, mKeyguardIndicationController, @@ -92,7 +97,15 @@ public class LockscreenIconControllerTest extends SysuiTestCase { mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources, mHeadsUpManagerPhone); + ArgumentCaptor<OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = + ArgumentCaptor.forClass(OnAttachStateChangeListener.class); + + doNothing().when(mLockIcon) + .addOnAttachStateChangeListener( + onAttachStateChangeListenerArgumentCaptor.capture()); mLockIconController.attach(mLockIcon); + + mOnAttachStateChangeListener = onAttachStateChangeListenerArgumentCaptor.getValue(); } @Test @@ -114,4 +127,19 @@ public class LockscreenIconControllerTest extends SysuiTestCase { verify(mLockPatternUtils).requireCredentialEntry(anyInt()); verify(mKeyguardUpdateMonitor).onLockIconPressed(); } + + @Test + public void testVisibility_Dozing() { + ArgumentCaptor<StatusBarStateController.StateListener> sBStateListenerCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + + mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon); + verify(mStatusBarStateController).addCallback(sBStateListenerCaptor.capture()); + + when(mStatusBarStateController.isDozing()).thenReturn(true); + sBStateListenerCaptor.getValue().onDozingChanged(true); + + verify(mLockIcon).updateIconVisibility(false); + + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 94d5458e0704..ef4d1078b8b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -17,6 +17,7 @@ package com.android.systemui.wmshell; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -88,7 +89,8 @@ public class WMShellTest extends SysuiTestCase { public void initPip_registersCommandQueueCallback() { mWMShell.initPip(mPip); - verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class)); + // Once for the shell, once for pip + verify(mCommandQueue, times(2)).addCallback(any(CommandQueue.Callbacks.class)); } @Test @@ -106,7 +108,8 @@ public class WMShellTest extends SysuiTestCase { mWMShell.initOneHanded(mOneHanded); verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); - verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class)); + // Once for the shell, once for the one handed mode + verify(mCommandQueue, times(2)).addCallback(any(CommandQueue.Callbacks.class)); verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class)); verify(mNavigationModeController).addListener( any(NavigationModeController.ModeChangedListener.class)); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index a75b64ce08f5..ee03b150b519 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -1352,7 +1352,7 @@ public class AccessibilityWindowManager { mTouchInteractionInProgress = false; // We want to set the active window to be current immediately // after the user has stopped touching the screen since if the - // user types with the IME he should get a feedback for the + // user types with the IME they should get a feedback for the // letter typed in the text view which is in the input focused // window. Note that we always deliver hover accessibility events // (they are a result of user touching the screen) so change of diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java index 8604fe7a7359..a860db389d1e 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java @@ -207,7 +207,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener { boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (mState.isClear()) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - // Sanity safeguard: if touch state is clear, then matchers should always be clear + // Validity safeguard: if touch state is clear, then matchers should always be clear // before processing the next down event. clear(); } else { diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 8305be393ab1..d8c692b88a0f 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -720,7 +720,7 @@ public class TouchExplorer extends BaseEventStreamTransformation } // If the user is touch exploring the second pointer may be // performing a double tap to activate an item without need - // for the user to lift his exploring finger. + // for the user to lift their exploring finger. // It is *important* to use the distance traveled by the pointers // on the screen which may or may not be magnified. final float deltaX = diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index d3d56d7f857d..9cbd78bf4482 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -68,6 +68,7 @@ public class WindowMagnificationManager implements private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>(); private int mUserId; + private boolean mReceiverRegistered = false; @VisibleForTesting protected final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() { @Override @@ -150,10 +151,16 @@ public class WindowMagnificationManager implements } if (connect) { final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); - mContext.registerReceiver(mScreenStateReceiver, intentFilter); + if (!mReceiverRegistered) { + mContext.registerReceiver(mScreenStateReceiver, intentFilter); + mReceiverRegistered = true; + } } else { disableAllWindowMagnifiers(); - mContext.unregisterReceiver(mScreenStateReceiver); + if (mReceiverRegistered) { + mContext.unregisterReceiver(mScreenStateReceiver); + mReceiverRegistered = false; + } } } @@ -240,6 +247,9 @@ public class WindowMagnificationManager implements void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable Runnable endCallback) { synchronized (mLock) { + if (mConnectionWrapper == null) { + return; + } WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { magnifier = createWindowMagnifier(displayId); @@ -269,7 +279,7 @@ public class WindowMagnificationManager implements void disableWindowMagnification(int displayId, boolean clear, Runnable endCallback) { synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); - if (magnifier == null) { + if (magnifier == null || mConnectionWrapper == null) { return; } magnifier.disableWindowMagnificationInternal(endCallback); diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index d7a3a32f102a..060d0971e391 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -1001,7 +1001,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku Slog.i(TAG, "hasBindAppWidgetPermission() " + UserHandle.getCallingUserId()); } - // A special permission is required for managing white listing. + // A special permission is required for managing allowlisting. mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName); synchronized (mLock) { @@ -1025,7 +1025,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku Slog.i(TAG, "setBindAppWidgetPermission() " + UserHandle.getCallingUserId()); } - // A special permission is required for managing white listing. + // A special permission is required for managing allowlisting. mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName); synchronized (mLock) { @@ -1117,7 +1117,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } // If the provider is not under the calling user, make sure this - // provider is white listed for access from the parent. + // provider is allowlisted for access from the parent. if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed( providerComponent.getPackageName(), providerProfileId)) { return false; @@ -1126,7 +1126,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku synchronized (mLock) { ensureGroupStateLoadedLocked(userId); - // A special permission or white listing is required to bind widgets. + // A special permission or allowlisting is required to bind widgets. if (!mSecurityPolicy.hasCallerBindPermissionOrBindWhiteListedLocked( callingPackage)) { return false; @@ -1741,7 +1741,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku continue; } - // Add providers only for the requested profile that are white-listed. + // Add providers only for the requested profile that are allowlisted. final int providerProfileId = info.getProfile().getIdentifier(); if (providerProfileId == profileId && mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed( @@ -3576,7 +3576,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku @Override public void onCrossProfileWidgetProvidersChanged(int userId, List<String> packages) { final int parentId = mSecurityPolicy.getProfileParent(userId); - // We care only if the white-listed package is in a profile of + // We care only if the allowlisted package is in a profile of // the group parent as only the parent can add widgets from the // profile and not the other way around. if (parentId != userId) { @@ -3600,7 +3600,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku userId, null); } - // Remove widgets from hosts in parent user for packages not in the whitelist + // Remove widgets from hosts in parent user for packages not in the allowlist final int removedCount = previousPackages.size(); for (int i = 0; i < removedCount; ++i) { removeWidgetsForPackageLocked(previousPackages.valueAt(i), diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 57ffe0498a88..d59c955de889 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -313,7 +313,7 @@ final class AutofillManagerServiceImpl @NonNull ComponentName componentName, boolean compatMode, boolean bindInstantServiceAllowed, int flags) { // FLAG_AUGMENTED_AUTOFILL_REQUEST is set in the flags when standard autofill is disabled - // but the package is whitelisted for augmented autofill + // but the package is allowlisted for augmented autofill boolean forAugmentedAutofillOnly = (flags & FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY) != 0; if (!isEnabledLocked() && !forAugmentedAutofillOnly) { @@ -322,7 +322,7 @@ final class AutofillManagerServiceImpl if (!forAugmentedAutofillOnly && isAutofillDisabledLocked(componentName)) { // Standard autofill is enabled, but service disabled autofill for this activity; that - // means no session, unless the activity is whitelisted for augmented autofill + // means no session, unless the activity is allowlisted for augmented autofill if (isWhitelistedForAugmentedAutofillLocked(componentName)) { if (sDebug) { Slog.d(TAG, "startSession(" + componentName + "): disabled by service but " @@ -1379,7 +1379,7 @@ final class AutofillManagerServiceImpl } /** - * Resets the augmented autofill whitelist. + * Resets the augmented autofill allowlist. */ @GuardedBy("mLock") void resetAugmentedAutofillWhitelistLocked() { diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java index b1c356a57f50..fd573d5e0665 100644 --- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java +++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java @@ -174,7 +174,7 @@ public class TransportManager { } } - /** Returns a set with the whitelisted transports. */ + /** Returns a set with the allowlisted transports. */ Set<ComponentName> getTransportWhitelist() { return mTransportWhitelist; } @@ -591,7 +591,7 @@ public class TransportManager { } } - /** Transport has to be whitelisted and privileged. */ + /** Transport has to be allowlisted and privileged. */ private boolean isTransportTrusted(ComponentName transport) { if (!mTransportWhitelist.contains(transport)) { Slog.w( diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java index ca89f7f69fbc..0eb3ea3e1bc8 100644 --- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java +++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java @@ -664,7 +664,7 @@ public class TransportClient { return; } // TODO (b/147705255): Remove when binder calls to IBackupTransport are not blocking - // In short-term, blocking calls are OK as the transports come from the whitelist at + // In short-term, blocking calls are OK as the transports come from the allowlist at // {@link SystemConfig#getBackupTransportWhitelist()} Binder.allowBlocking(binder); transportClient.onServiceConnected(binder); diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 7b9728cb4f08..45cbad181e04 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -85,7 +85,6 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.wm.ActivityTaskManagerInternal; import org.xmlpull.v1.XmlPullParser; @@ -170,7 +169,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind public void onPackageRemoved(String packageName, int uid) { updateAssociations( as -> CollectionUtils.filter(as, - a -> !Objects.equals(a.companionAppPackage, packageName)), + a -> !Objects.equals(a.getPackageName(), packageName)), getChangingUserId()); } @@ -199,7 +198,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } Set<String> companionAppPackages = new HashSet<>(); for (Association association : associations) { - companionAppPackages.add(association.companionAppPackage); + companionAppPackages.add(association.getPackageName()); } ActivityTaskManagerInternal atmInternal = LocalServices.getService( ActivityTaskManagerInternal.class); @@ -229,10 +228,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } for (Association a : associations) { try { - int uid = pm.getPackageUidAsUser(a.companionAppPackage, userId); - exemptFromAutoRevoke(a.companionAppPackage, uid); + int uid = pm.getPackageUidAsUser(a.getPackageName(), userId); + exemptFromAutoRevoke(a.getPackageName(), uid); } catch (PackageManager.NameNotFoundException e) { - Log.w(LOG_TAG, "Unknown companion package: " + a.companionAppPackage, e); + Log.w(LOG_TAG, "Unknown companion package: " + a.getPackageName(), e); } } } finally { @@ -344,7 +343,17 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } return new ArrayList<>(CollectionUtils.map( readAllAssociations(userId, callingPackage), - a -> a.deviceAddress)); + a -> a.getDeviceMacAddress())); + } + + @Override + public List<Association> getAssociationsForUser(int userId) { + if (!callerCanManageCompanionDevices()) { + throw new SecurityException("Caller must hold " + + android.Manifest.permission.MANAGE_COMPANION_DEVICES); + } + + return new ArrayList<>(readAllAssociations(userId, null /* packageFilter */)); } //TODO also revoke notification access @@ -429,7 +438,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return CollectionUtils.any( readAllAssociations(userId, packageName), - a -> Objects.equals(a.deviceAddress, macAddress)); + a -> Objects.equals(a.getDeviceMacAddress(), macAddress)); } private void checkCanCallNotificationApi(String callingPackage) throws RemoteException { @@ -479,7 +488,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind void addAssociation(Association association) { updateSpecialAccessPermissionForAssociatedPackage( - association.companionAppPackage, association.userId); + association.getPackageName(), association.getUserId()); recordAssociation(association); } @@ -582,7 +591,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind Set<Association> finalAssociations = associations; Set<String> companionAppPackages = new HashSet<>(); for (Association association : finalAssociations) { - companionAppPackages.add(association.companionAppPackage); + companionAppPackages.add(association.getPackageName()); } file.write((out) -> { @@ -595,8 +604,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind CollectionUtils.forEach(finalAssociations, association -> { xml.startTag(null, XML_TAG_ASSOCIATION) - .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage) - .attribute(null, XML_ATTR_DEVICE, association.deviceAddress) + .attribute(null, XML_ATTR_PACKAGE, association.getPackageName()) + .attribute(null, XML_ATTR_DEVICE, association.getDeviceMacAddress()) .endTag(null, XML_TAG_ASSOCIATION); }); @@ -678,7 +687,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind CollectionUtils.forEach( readAllAssociations(getNextArgInt()), a -> getOutPrintWriter() - .println(a.companionAppPackage + " " + a.deviceAddress)); + .println(a.getPackageName() + " " + a.getDeviceMacAddress())); } break; case "associate": { diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index e742015916e7..0be485b747e5 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -885,11 +885,11 @@ public final class ContentCaptureManagerService extends synchronized (mGlobalWhitelistStateLock) { packageWhitelisted = isWhitelisted(userId, packageName); if (!packageWhitelisted) { - // Full package is not whitelisted: check individual components first + // Full package is not allowlisted: check individual components first whitelistedComponents = getWhitelistedComponents(userId, packageName); if (whitelistedComponents == null && packageName.equals(mServicePackages.get(userId))) { - // No components whitelisted either, but let it go because it's the + // No components allowlisted either, but let it go because it's the // service's own package if (verbose) Slog.v(TAG, "getOptionsForPackage() lite for " + packageName); return new ContentCaptureOptions(mDevCfgLoggingLevel); @@ -897,7 +897,7 @@ public final class ContentCaptureManagerService extends } } // synchronized - // Restrict what temporary services can whitelist + // Restrict what temporary services can allowlist if (Build.IS_USER && mServiceNameResolver.isTemporary(userId)) { if (!packageName.equals(mServicePackages.get(userId))) { Slog.w(TAG, "Ignoring package " + packageName + " while using temporary " diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 9486b0d39bc2..ea68e190ff1f 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -93,7 +93,7 @@ final class ContentCapturePerUserService * Reference to the remote service. * * <p>It's set in the constructor, but it's also updated when the service's updated in the - * master's cache (for example, because a temporary service was set). + * main service's cache (for example, because a temporary service was set). */ @GuardedBy("mLock") @Nullable @@ -198,7 +198,7 @@ final class ContentCapturePerUserService void onConnected() { synchronized (mLock) { if (mZombie) { - // Sanity check - shouldn't happen + // Validity check - shouldn't happen if (mRemoteService == null) { Slog.w(TAG, "Cannot ressurect sessions because remote service is null"); return; @@ -571,7 +571,7 @@ final class ContentCapturePerUserService } /** - * Resets the content capture whitelist. + * Resets the content capture allowlist. */ @GuardedBy("mLock") private void resetContentCaptureWhitelistLocked() { @@ -598,7 +598,7 @@ final class ContentCapturePerUserService mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities); writeSetWhitelistEvent(getServiceComponentName(), packages, activities); - // Must disable session that are not the whitelist anymore... + // Must disable session that are not the allowlist anymore... final int numSessions = mSessions.size(); if (numSessions <= 0) return; diff --git a/services/core/java/com/android/server/CertBlacklister.java b/services/core/java/com/android/server/CertBlacklister.java index 8b167d7b0c90..c16378be7342 100644 --- a/services/core/java/com/android/server/CertBlacklister.java +++ b/services/core/java/com/android/server/CertBlacklister.java @@ -125,14 +125,14 @@ public class CertBlacklister extends Binder { } private void registerObservers(ContentResolver cr) { - // set up the public key blacklist observer + // set up the public key denylist observer cr.registerContentObserver( Settings.Secure.getUriFor(PUBKEY_BLACKLIST_KEY), true, buildPubkeyObserver(cr) ); - // set up the serial number blacklist observer + // set up the serial number denylist observer cr.registerContentObserver( Settings.Secure.getUriFor(SERIAL_BLACKLIST_KEY), true, diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index bd590d317910..aeb8fbb1b3e6 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK; @@ -1136,6 +1137,12 @@ public class ConnectivityService extends IConnectivityManager.Stub null /* broadcastPermission */, mHandler); + // Listen to lockdown VPN reset. + intentFilter = new IntentFilter(); + intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET); + mContext.registerReceiverAsUser( + mIntentReceiver, UserHandle.ALL, intentFilter, NETWORK_STACK, mHandler); + try { mNMS.registerObserver(mDataActivityObserver); } catch (RemoteException e) { @@ -5204,6 +5211,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void onVpnLockdownReset() { + synchronized (mVpns) { + if (mLockdownTracker != null) mLockdownTracker.reset(); + } + } + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -5214,6 +5227,12 @@ public class ConnectivityService extends IConnectivityManager.Stub final Uri packageData = intent.getData(); final String packageName = packageData != null ? packageData.getSchemeSpecificPart() : null; + + if (LockdownVpnTracker.ACTION_LOCKDOWN_RESET.equals(action)) { + onVpnLockdownReset(); + } + + // UserId should be filled for below intents, check the existence. if (userId == UserHandle.USER_NULL) return; if (Intent.ACTION_USER_STARTED.equals(action)) { @@ -5232,6 +5251,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean isReplacing = intent.getBooleanExtra( Intent.EXTRA_REPLACING, false); onPackageRemoved(packageName, uid, isReplacing); + } else { + Log.wtf(TAG, "received unexpected intent: " + action); } } }; @@ -6219,7 +6240,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final int vpnAppUid = nai.networkCapabilities.getOwnerUid(); // TODO: this create a window of opportunity for apps to receive traffic between the time // when the old rules are removed and the time when new rules are added. To fix this, - // make eBPF support two whitelisted interfaces so here new rules can be added before the + // make eBPF support two allowlisted interfaces so here new rules can be added before the // old rules are being removed. if (wasFiltering) { mPermissionMonitor.onVpnUidRangesRemoved(oldIface, ranges, vpnAppUid); diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java index 5e6e9d34dc25..c56cef2d58dc 100644 --- a/services/core/java/com/android/server/EntropyMixer.java +++ b/services/core/java/com/android/server/EntropyMixer.java @@ -52,9 +52,9 @@ import android.util.Slog; * entropy estimate is not increased. This is to avoid having to trust/verify * the quality and authenticity of the "randomness" of the HW RNG. * - * <p>This class was modeled after the script in - * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man - * 4 random</a>. + * <p>This class was modeled after the script in the + * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html"> + * random(4) manual page</a>. */ public class EntropyMixer extends Binder { private static final String TAG = "EntropyMixer"; diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index ee794badad88..ea14fadff433 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -373,7 +373,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { */ private void notifyInterfaceRemoved(String iface) { // netd already clears out quota and alerts for removed ifaces; update - // our sanity-checking state. + // our validity-checking state. mActiveAlerts.remove(iface); mActiveQuotas.remove(iface); invokeForAllObservers(o -> o.interfaceRemoved(iface)); @@ -1256,7 +1256,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { public void setInterfaceAlert(String iface, long alertBytes) { NetworkStack.checkNetworkStackPermission(mContext); - // quick sanity check + // quick validity check if (!mActiveQuotas.containsKey(iface)) { throw new IllegalStateException("setting alert requires existing quota on iface"); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index eb18da2d2e13..e342c1fcf114 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1076,7 +1076,7 @@ class StorageManagerService extends IStorageManager.Stub } try { - // TODO(b/135341433): Remove paranoid logging when FUSE is stable + // TODO(b/135341433): Remove cautious logging when FUSE is stable Slog.i(TAG, "Resetting vold..."); mVold.reset(); Slog.i(TAG, "Reset vold"); @@ -2173,7 +2173,7 @@ class StorageManagerService extends IStorageManager.Stub private void mount(VolumeInfo vol) { try { - // TODO(b/135341433): Remove paranoid logging when FUSE is stable + // TODO(b/135341433): Remove cautious logging when FUSE is stable Slog.i(TAG, "Mounting volume " + vol); mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() { @Override @@ -2904,7 +2904,7 @@ class StorageManagerService extends IStorageManager.Stub return 0; } - /** Set the password for encrypting the master key. + /** Set the password for encrypting the main key. * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager. * @param password The password to set. */ @@ -2968,7 +2968,7 @@ class StorageManagerService extends IStorageManager.Stub } /** - * Get the type of encryption used to encrypt the master key. + * Get the type of encryption used to encrypt the main key. * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager. */ @Override diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index e433fbd94d4f..2522e937025b 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1799,20 +1799,16 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void notifyCellLocationForSubscriber(int subId, CellIdentity cellLocation) { + public void notifyCellLocationForSubscriber(int subId, CellIdentity cellIdentity) { log("notifyCellLocationForSubscriber: subId=" + subId - + " cellLocation=" + cellLocation); + + " cellIdentity=" + cellIdentity); if (!checkNotifyPermission("notifyCellLocation()")) { return; } - if (VDBG) { - log("notifyCellLocationForSubscriber: subId=" + subId - + " cellLocation=" + cellLocation); - } int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { - if (validatePhoneId(phoneId)) { - mCellIdentity[phoneId] = cellLocation; + if (validatePhoneId(phoneId) && !Objects.equals(cellIdentity, mCellIdentity[phoneId])) { + mCellIdentity[phoneId] = cellIdentity; for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId) && @@ -1820,10 +1816,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { - log("notifyCellLocation: cellLocation=" + cellLocation + log("notifyCellLocation: cellIdentity=" + cellIdentity + " r=" + r); } - r.callback.onCellLocationChanged(cellLocation); + r.callback.onCellLocationChanged(cellIdentity); } catch (RemoteException ex) { mRemoveList.add(r.binder); } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 7d81d412e369..5a34283ed0cc 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -2326,7 +2326,7 @@ public class AccountManagerService List<String> accountRemovedReceivers = getAccountRemovedReceivers(account, accounts); accounts.accountsDb.beginTransaction(); - // Set to a dummy value, this will only be used if the database + // Set to a placeholder value, this will only be used if the database // transaction succeeds. long accountId = -1; try { @@ -6284,7 +6284,7 @@ public class AccountManagerService PRE_N_DATABASE_NAME); if (userId == 0) { // Migrate old file, if it exists, to the new location. - // Make sure the new file doesn't already exist. A dummy file could have been + // Make sure the new file doesn't already exist. A placeholder file could have been // accidentally created in the old location, // causing the new one to become corrupted as well. File oldFile = new File(systemDir, PRE_N_DATABASE_NAME); diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index fede1d2832b8..48055b51722f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -26,6 +26,7 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Build; import android.os.Handler; +import android.os.Message; import android.provider.DeviceConfig; import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.provider.DeviceConfig.Properties; @@ -124,6 +125,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000; private static final int DEFAULT_MIN_CRASH_INTERVAL = 2 * 60 * 1000; + private static final int DEFAULT_MAX_PHANTOM_PROCESSES = 32; // Flag stored in the DeviceConfig API. @@ -133,6 +135,11 @@ final class ActivityManagerConstants extends ContentObserver { private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes"; /** + * Maximum number of cached processes. + */ + private static final String KEY_MAX_PHANTOM_PROCESSES = "max_phantom_processes"; + + /** * Default value for mFlagBackgroundActivityStartsEnabled if not explicitly set in * Settings.Global. This allows it to be set experimentally unless it has been * enabled/disabled in developer options. Defaults to false. @@ -364,6 +371,11 @@ final class ActivityManagerConstants extends ContentObserver { */ public final ArraySet<ComponentName> KEEP_WARMING_SERVICES = new ArraySet<ComponentName>(); + /** + * Maximum number of phantom processes. + */ + public int MAX_PHANTOM_PROCESSES = DEFAULT_MAX_PHANTOM_PROCESSES; + private List<String> mDefaultImperceptibleKillExemptPackages; private List<Integer> mDefaultImperceptibleKillExemptProcStates; @@ -481,6 +493,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD: updateBinderHeavyHitterWatcher(); break; + case KEY_MAX_PHANTOM_PROCESSES: + updateMaxPhantomProcesses(); + break; default: break; } @@ -599,6 +614,8 @@ final class ActivityManagerConstants extends ContentObserver { // with defaults. Slog.e("ActivityManagerConstants", "Bad activity manager config settings", e); } + final long currentPowerCheckInterval = POWER_CHECK_INTERVAL; + BACKGROUND_SETTLE_TIME = mParser.getLong(KEY_BACKGROUND_SETTLE_TIME, DEFAULT_BACKGROUND_SETTLE_TIME); FGSERVICE_MIN_SHOWN_TIME = mParser.getLong(KEY_FGSERVICE_MIN_SHOWN_TIME, @@ -664,6 +681,13 @@ final class ActivityManagerConstants extends ContentObserver { PENDINGINTENT_WARNING_THRESHOLD = mParser.getInt(KEY_PENDINGINTENT_WARNING_THRESHOLD, DEFAULT_PENDINGINTENT_WARNING_THRESHOLD); + if (POWER_CHECK_INTERVAL != currentPowerCheckInterval) { + mService.mHandler.removeMessages( + ActivityManagerService.CHECK_EXCESSIVE_POWER_USE_MSG); + final Message msg = mService.mHandler.obtainMessage( + ActivityManagerService.CHECK_EXCESSIVE_POWER_USE_MSG); + mService.mHandler.sendMessageDelayed(msg, POWER_CHECK_INTERVAL); + } // For new flags that are intended for server-side experiments, please use the new // DeviceConfig package. } @@ -811,6 +835,16 @@ final class ActivityManagerConstants extends ContentObserver { mService.scheduleUpdateBinderHeavyHitterWatcherConfig(); } + private void updateMaxPhantomProcesses() { + final int oldVal = MAX_PHANTOM_PROCESSES; + MAX_PHANTOM_PROCESSES = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MAX_PHANTOM_PROCESSES, + DEFAULT_MAX_PHANTOM_PROCESSES); + if (oldVal > MAX_PHANTOM_PROCESSES) { + mService.mHandler.post(mService.mPhantomProcessList::trimPhantomProcessesIfNecessary); + } + } + void dump(PrintWriter pw) { pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) " + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":"); @@ -897,6 +931,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE); pw.print(" "); pw.print(KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD); pw.print("="); pw.println(BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD); + pw.print(" "); pw.print(KEY_MAX_PHANTOM_PROCESSES); pw.print("="); + pw.println(MAX_PHANTOM_PROCESSES); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b55d55562110..b1b4018036c0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -635,6 +635,12 @@ public class ActivityManagerService extends IActivityManager.Stub final ProcessList mProcessList; /** + * The list of phantom processes. + * @see PhantomProcessRecord + */ + final PhantomProcessList mPhantomProcessList; + + /** * Tracking long-term execution of processes to look for abuse and other * bad app behavior. */ @@ -1996,6 +2002,7 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessList = injector.getProcessList(this); mProcessList.init(this, activeUids, mPlatformCompat); mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null); + mPhantomProcessList = new PhantomProcessList(this); mOomAdjuster = hasHandlerThread ? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null; @@ -2053,6 +2060,7 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessList.init(this, activeUids, mPlatformCompat); mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), new LowMemDetector(this)); + mPhantomProcessList = new PhantomProcessList(this); mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); // Broadcast policy parameters @@ -9209,6 +9217,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } + if (dumpAll) { + mPhantomProcessList.dump(pw, " "); + } + if (mImportantProcesses.size() > 0) { synchronized (mPidsSelfLocked) { boolean printed = false; @@ -14832,44 +14844,24 @@ public class ActivityManagerService extends IActivityManager.Stub int i = mProcessList.mLruProcesses.size(); while (i > 0) { i--; - ProcessRecord app = mProcessList.mLruProcesses.get(i); + final ProcessRecord app = mProcessList.mLruProcesses.get(i); if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { - if (app.lastCpuTime <= 0) { - continue; - } - long cputimeUsed = app.curCpuTime - app.lastCpuTime; - if (DEBUG_POWER) { - StringBuilder sb = new StringBuilder(128); - sb.append("CPU for "); - app.toShortString(sb); - sb.append(": over "); - TimeUtils.formatDuration(uptimeSince, sb); - sb.append(" used "); - TimeUtils.formatDuration(cputimeUsed, sb); - sb.append(" ("); - sb.append((cputimeUsed * 100) / uptimeSince); - sb.append("%)"); - Slog.i(TAG_POWER, sb.toString()); + int cpuLimit; + long checkDur = curUptime - app.getWhenUnimportant(); + if (checkDur <= mConstants.POWER_CHECK_INTERVAL) { + cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1; + } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2) + || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) { + cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2; + } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) { + cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3; + } else { + cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4; } - // If the process has used too much CPU over the last duration, the - // user probably doesn't want this, so kill! - if (doCpuKills && uptimeSince > 0) { - // What is the limit for this process? - int cpuLimit; - long checkDur = curUptime - app.getWhenUnimportant(); - if (checkDur <= mConstants.POWER_CHECK_INTERVAL) { - cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1; - } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2) - || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) { - cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2; - } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) { - cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3; - } else { - cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4; - } - if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) { - mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName, - uptimeSince, cputimeUsed); + if (app.lastCpuTime > 0) { + final long cputimeUsed = app.curCpuTime - app.lastCpuTime; + if (checkExcessivePowerUsageLocked(uptimeSince, doCpuKills, cputimeUsed, + app.processName, app.toShortString(), cpuLimit, app)) { app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince + " dur=" + checkDur + " limit=" + cpuLimit, ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE, @@ -14878,21 +14870,71 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (mProcessStats.mLock) { app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList); } - for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg); - FrameworkStatsLog.write( - FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED, - app.info.uid, - holder.state.getName(), - holder.state.getPackage(), - holder.appVersion); - } } } + app.lastCpuTime = app.curCpuTime; + + // Also check the phantom processes if there is any + final long chkDur = checkDur; + final int cpuLmt = cpuLimit; + final boolean doKill = doCpuKills; + mPhantomProcessList.forEachPhantomProcessOfApp(app, r -> { + if (r.mLastCputime > 0) { + final long cputimeUsed = r.mCurrentCputime - r.mLastCputime; + if (checkExcessivePowerUsageLocked(uptimeSince, doKill, cputimeUsed, + app.processName, r.toString(), cpuLimit, app)) { + mPhantomProcessList.killPhantomProcessGroupLocked(app, r, + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE, + ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU, + "excessive cpu " + cputimeUsed + " during " + + uptimeSince + " dur=" + chkDur + " limit=" + cpuLmt); + return false; + } + } + r.mLastCputime = r.mCurrentCputime; + return true; + }); + } + } + } + } + + private boolean checkExcessivePowerUsageLocked(final long uptimeSince, boolean doCpuKills, + final long cputimeUsed, final String processName, final String description, + final int cpuLimit, final ProcessRecord app) { + if (DEBUG_POWER) { + StringBuilder sb = new StringBuilder(128); + sb.append("CPU for "); + sb.append(description); + sb.append(": over "); + TimeUtils.formatDuration(uptimeSince, sb); + sb.append(" used "); + TimeUtils.formatDuration(cputimeUsed, sb); + sb.append(" ("); + sb.append((cputimeUsed * 100.0) / uptimeSince); + sb.append("%)"); + Slog.i(TAG_POWER, sb.toString()); + } + // If the process has used too much CPU over the last duration, the + // user probably doesn't want this, so kill! + if (doCpuKills && uptimeSince > 0) { + if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) { + mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName, + uptimeSince, cputimeUsed); + for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) { + ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg); + FrameworkStatsLog.write( + FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED, + app.info.uid, + processName, + holder.state.getPackage(), + holder.appVersion); } + return true; } } + return false; } final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) { diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index 0b5d5859e86e..31ffb35f24b3 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -1257,6 +1257,10 @@ public class AppProfiler { } } + if (haveNewCpuStats) { + mService.mPhantomProcessList.updateProcessCpuStatesLocked(mProcessCpuTracker); + } + final BatteryStatsImpl bstats = mService.mBatteryStatsService.getActiveStatistics(); synchronized (bstats) { if (haveNewCpuStats) { diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java index 0a0d8d85f5c3..2142ebc7ee8e 100644 --- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java +++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java @@ -63,7 +63,7 @@ public final class BugReportHandlerUtil { } /** - * Launches a bugreport-whitelisted app to handle a bugreport. + * Launches a bugreport-allowlisted app to handle a bugreport. * * <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can * be predefined in the config, meant to be launched with the primary user. The user can @@ -71,7 +71,7 @@ public final class BugReportHandlerUtil { * useful for capturing bug reports from work profile, for instance. * * @param context Context - * @return true if there is a bugreport-whitelisted app to handle a bugreport, or false + * @return true if there is a bugreport-allowlisted app to handle a bugreport, or false * otherwise */ static boolean launchBugReportHandlerApp(Context context) { @@ -92,7 +92,7 @@ public final class BugReportHandlerUtil { // It looks like the settings are outdated, reset outdated settings. // // i.e. - // If user chooses which profile and which bugreport-whitelisted app in that + // If user chooses which profile and which bugreport-allowlisted app in that // profile to handle a bugreport, then user remove the profile. // === RESULT === // The chosen bugreport handler app is outdated because the profile is removed, @@ -184,7 +184,7 @@ public final class BugReportHandlerUtil { } private static boolean isBugreportWhitelistedApp(String app) { - // Verify the app is bugreport-whitelisted + // Verify the app is bugreport-allowlisted final ArraySet<String> whitelistedApps = SystemConfig.getInstance() .getBugreportWhitelistedPackages(); return whitelistedApps.contains(app); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index c313eb5760eb..1bf62a0c7b7e 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1113,8 +1113,8 @@ public final class OomAdjuster { + " to " + uidRec.curWhitelist); if (ActivityManager.isProcStateBackground(uidRec.getCurProcState()) && !uidRec.curWhitelist) { - // UID is now in the background (and not on the temp whitelist). Was it - // previously in the foreground (or on the temp whitelist)? + // UID is now in the background (and not on the temp allowlist). Was it + // previously in the foreground (or on the temp allowlist)? if (!ActivityManager.isProcStateBackground(uidRec.setProcState) || uidRec.setWhitelist) { uidRec.lastBackgroundTime = nowElapsed; diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java new file mode 100644 index 000000000000..e2fcf08ce82b --- /dev/null +++ b/services/core/java/com/android/server/am/PhantomProcessList.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2020 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.am; + +import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; +import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; + +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.app.ApplicationExitInfo.Reason; +import android.app.ApplicationExitInfo.SubReason; +import android.os.Handler; +import android.os.Process; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.ProcessCpuTracker; + +import libcore.io.IoUtils; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.function.Function; + +/** + * Activity manager code dealing with phantom processes. + */ +public final class PhantomProcessList { + static final String TAG = TAG_WITH_CLASS_NAME ? "PhantomProcessList" : TAG_AM; + + final Object mLock = new Object(); + + /** + * All of the phantom process record we track, key is the pid of the process. + */ + @GuardedBy("mLock") + final SparseArray<PhantomProcessRecord> mPhantomProcesses = new SparseArray<>(); + + /** + * The mapping between app processes and their phantom processess, outer key is the pid of + * the app process, while the inner key is the pid of the phantom process. + */ + @GuardedBy("mLock") + final SparseArray<SparseArray<PhantomProcessRecord>> mAppPhantomProcessMap = + new SparseArray<>(); + + /** + * The mapping of the pidfd to PhantomProcessRecord. + */ + @GuardedBy("mLock") + final SparseArray<PhantomProcessRecord> mPhantomProcessesPidFds = new SparseArray<>(); + + /** + * The list of phantom processes tha's being signaled to be killed but still undead yet. + */ + @GuardedBy("mLock") + final SparseArray<PhantomProcessRecord> mZombiePhantomProcesses = new SparseArray<>(); + + @GuardedBy("mLock") + private final ArrayList<PhantomProcessRecord> mTempPhantomProcesses = new ArrayList<>(); + + @GuardedBy("mLock") + private boolean mTrimPhantomProcessScheduled = false; + + @GuardedBy("mLock") + int mUpdateSeq; + + private final ActivityManagerService mService; + private final Handler mKillHandler; + + PhantomProcessList(final ActivityManagerService service) { + mService = service; + mKillHandler = service.mProcessList.sKillHandler; + } + + /** + * Get the existing phantom process record, or create if it's not existing yet; + * however, before creating it, we'll check if this is really a phantom process + * and we'll return null if it's not. + */ + @GuardedBy("mLock") + PhantomProcessRecord getOrCreatePhantomProcessIfNeededLocked(final String processName, + final int uid, final int pid) { + // First check if it's actually an app process we know + if (isAppProcess(pid)) { + return null; + } + + // Have we already been aware of this? + final int index = mPhantomProcesses.indexOfKey(pid); + if (index >= 0) { + final PhantomProcessRecord proc = mPhantomProcesses.valueAt(index); + if (proc.equals(processName, uid, pid)) { + return proc; + } + // Somehow our record doesn't match, remove it anyway + Slog.w(TAG, "Stale " + proc + ", removing"); + mPhantomProcesses.removeAt(index); + } else { + // Is this one of the zombie processes we've known? + final int idx = mZombiePhantomProcesses.indexOfKey(pid); + if (idx >= 0) { + final PhantomProcessRecord proc = mZombiePhantomProcesses.valueAt(idx); + if (proc.equals(processName, uid, pid)) { + return proc; + } + // Our zombie process information is outdated, let's remove this one, it shoud + // have been gone. + mZombiePhantomProcesses.removeAt(idx); + } + } + + int ppid = getParentPid(pid); + + // Walk through its parents and see if it could be traced back to an app process. + while (ppid > 1) { + if (isAppProcess(ppid)) { + // It's a phantom process, bookkeep it + try { + final PhantomProcessRecord proc = new PhantomProcessRecord( + processName, uid, pid, ppid, mService, + this::onPhantomProcessKilledLocked); + proc.mUpdateSeq = mUpdateSeq; + mPhantomProcesses.put(pid, proc); + SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(ppid); + if (array == null) { + array = new SparseArray<>(); + mAppPhantomProcessMap.put(ppid, array); + } + array.put(pid, proc); + if (proc.mPidFd != null) { + mKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener( + proc.mPidFd, EVENT_INPUT | EVENT_ERROR, + this::onPhantomProcessFdEvent); + mPhantomProcessesPidFds.put(proc.mPidFd.getInt$(), proc); + } + scheduleTrimPhantomProcessesLocked(); + return proc; + } catch (IllegalStateException e) { + return null; + } + } + + ppid = getParentPid(ppid); + } + return null; + } + + private static int getParentPid(int pid) { + try { + return Process.getParentPid(pid); + } catch (Exception e) { + } + return -1; + } + + private boolean isAppProcess(int pid) { + synchronized (mService.mPidsSelfLocked) { + return mService.mPidsSelfLocked.get(pid) != null; + } + } + + private int onPhantomProcessFdEvent(FileDescriptor fd, int events) { + synchronized (mLock) { + final PhantomProcessRecord proc = mPhantomProcessesPidFds.get(fd.getInt$()); + if ((events & EVENT_INPUT) != 0) { + proc.onProcDied(true); + } else { + // EVENT_ERROR, kill the process + proc.killLocked("Process error", true); + } + } + return 0; + } + + @GuardedBy("mLock") + private void onPhantomProcessKilledLocked(final PhantomProcessRecord proc) { + if (proc.mPidFd != null && proc.mPidFd.valid()) { + mKillHandler.getLooper().getQueue() + .removeOnFileDescriptorEventListener(proc.mPidFd); + mPhantomProcessesPidFds.remove(proc.mPidFd.getInt$()); + IoUtils.closeQuietly(proc.mPidFd); + } + mPhantomProcesses.remove(proc.mPid); + final int index = mAppPhantomProcessMap.indexOfKey(proc.mPpid); + if (index < 0) { + return; + } + SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.valueAt(index); + array.remove(proc.mPid); + if (array.size() == 0) { + mAppPhantomProcessMap.removeAt(index); + } + if (proc.mZombie) { + // If it's not really dead, bookkeep it + mZombiePhantomProcesses.put(proc.mPid, proc); + } else { + // In case of race condition, let's try to remove it from zombie list + mZombiePhantomProcesses.remove(proc.mPid); + } + } + + @GuardedBy("mLock") + private void scheduleTrimPhantomProcessesLocked() { + if (!mTrimPhantomProcessScheduled) { + mTrimPhantomProcessScheduled = true; + mService.mHandler.post(this::trimPhantomProcessesIfNecessary); + } + } + + /** + * Clamp the number of phantom processes to + * {@link ActivityManagerConstants#MAX_PHANTOM_PROCESSE}, kills those surpluses in the + * order of the oom adjs of their parent process. + */ + void trimPhantomProcessesIfNecessary() { + synchronized (mLock) { + mTrimPhantomProcessScheduled = false; + if (mService.mConstants.MAX_PHANTOM_PROCESSES < mPhantomProcesses.size()) { + for (int i = mPhantomProcesses.size() - 1; i >= 0; i--) { + mTempPhantomProcesses.add(mPhantomProcesses.valueAt(i)); + } + synchronized (mService.mPidsSelfLocked) { + Collections.sort(mTempPhantomProcesses, (a, b) -> { + final ProcessRecord ra = mService.mPidsSelfLocked.get(a.mPpid); + if (ra == null) { + // parent is gone, this process should have been killed too + return 1; + } + final ProcessRecord rb = mService.mPidsSelfLocked.get(b.mPpid); + if (rb == null) { + // parent is gone, this process should have been killed too + return -1; + } + if (ra.curAdj != rb.curAdj) { + return ra.curAdj - rb.curAdj; + } + if (a.mKnownSince != b.mKnownSince) { + // In case of identical oom adj, younger one first + return a.mKnownSince < b.mKnownSince ? 1 : -1; + } + return 0; + }); + } + for (int i = mTempPhantomProcesses.size() - 1; + i >= mService.mConstants.MAX_PHANTOM_PROCESSES; i--) { + final PhantomProcessRecord proc = mTempPhantomProcesses.get(i); + proc.killLocked("Trimming phantom processes", true); + } + mTempPhantomProcesses.clear(); + } + } + } + + /** + * Remove all entries with outdated seq num. + */ + @GuardedBy("mLock") + void pruneStaleProcessesLocked() { + for (int i = mPhantomProcesses.size() - 1; i >= 0; i--) { + final PhantomProcessRecord proc = mPhantomProcesses.valueAt(i); + if (proc.mUpdateSeq < mUpdateSeq) { + if (DEBUG_PROCESSES) { + Slog.v(TAG, "Pruning " + proc + " as it should have been dead."); + } + proc.killLocked("Stale process", true); + } + } + for (int i = mZombiePhantomProcesses.size() - 1; i >= 0; i--) { + final PhantomProcessRecord proc = mZombiePhantomProcesses.valueAt(i); + if (proc.mUpdateSeq < mUpdateSeq) { + if (DEBUG_PROCESSES) { + Slog.v(TAG, "Pruning " + proc + " as it should have been dead."); + } + } + } + } + + /** + * Kill the given phantom process, all its siblings (if any) and their parent process + */ + @GuardedBy("mService") + void killPhantomProcessGroupLocked(ProcessRecord app, PhantomProcessRecord proc, + @Reason int reasonCode, @SubReason int subReason, String msg) { + synchronized (mLock) { + int index = mAppPhantomProcessMap.indexOfKey(proc.mPpid); + if (index >= 0) { + final SparseArray<PhantomProcessRecord> array = + mAppPhantomProcessMap.valueAt(index); + for (int i = array.size() - 1; i >= 0; i--) { + final PhantomProcessRecord r = array.valueAt(i); + if (r == proc) { + r.killLocked(msg, true); + } else { + r.killLocked("Caused by siling process: " + msg, false); + } + } + } + } + // Lastly, kill the parent process too + app.kill("Caused by child process: " + msg, reasonCode, subReason, true); + } + + /** + * Iterate all phantom process belonging to the given app, and invokve callback + * for each of them. + */ + void forEachPhantomProcessOfApp(final ProcessRecord app, + final Function<PhantomProcessRecord, Boolean> callback) { + synchronized (mLock) { + int index = mAppPhantomProcessMap.indexOfKey(app.pid); + if (index >= 0) { + final SparseArray<PhantomProcessRecord> array = + mAppPhantomProcessMap.valueAt(index); + for (int i = array.size() - 1; i >= 0; i--) { + final PhantomProcessRecord r = array.valueAt(i); + if (!callback.apply(r)) { + break; + } + } + } + } + } + + @GuardedBy("tracker") + void updateProcessCpuStatesLocked(ProcessCpuTracker tracker) { + synchronized (mLock) { + // refresh the phantom process list with the latest cpu stats results. + mUpdateSeq++; + for (int i = tracker.countStats() - 1; i >= 0; i--) { + final ProcessCpuTracker.Stats st = tracker.getStats(i); + final PhantomProcessRecord r = + getOrCreatePhantomProcessIfNeededLocked(st.name, st.uid, st.pid); + if (r != null) { + r.mUpdateSeq = mUpdateSeq; + r.mCurrentCputime += st.rel_utime + st.rel_stime; + if (r.mLastCputime == 0) { + r.mLastCputime = r.mCurrentCputime; + } + r.updateAdjLocked(); + } + } + // remove the stale ones + pruneStaleProcessesLocked(); + } + } + + void dump(PrintWriter pw, String prefix) { + synchronized (mLock) { + dumpPhantomeProcessLocked(pw, prefix, "All Active App Child Processes:", + mPhantomProcesses); + dumpPhantomeProcessLocked(pw, prefix, "All Zombie App Child Processes:", + mZombiePhantomProcesses); + } + } + + void dumpPhantomeProcessLocked(PrintWriter pw, String prefix, String headline, + SparseArray<PhantomProcessRecord> list) { + final int size = list.size(); + if (size == 0) { + return; + } + pw.println(); + pw.print(prefix); + pw.println(headline); + for (int i = 0; i < size; i++) { + final PhantomProcessRecord proc = list.valueAt(i); + pw.print(prefix); + pw.print(" proc #"); + pw.print(i); + pw.print(": "); + pw.println(proc.toString()); + proc.dump(pw, prefix + " "); + } + } +} diff --git a/services/core/java/com/android/server/am/PhantomProcessRecord.java b/services/core/java/com/android/server/am/PhantomProcessRecord.java new file mode 100644 index 000000000000..0156ee5bb018 --- /dev/null +++ b/services/core/java/com/android/server/am/PhantomProcessRecord.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2020 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.am; + +import static android.os.Process.PROC_NEWLINE_TERM; +import static android.os.Process.PROC_OUT_LONG; + +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.os.Handler; +import android.os.Process; +import android.os.StrictMode; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.EventLog; +import android.util.Slog; +import android.util.TimeUtils; + +import com.android.internal.annotations.GuardedBy; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.function.Consumer; + +/** + * The "phantom" app processes, which are forked by app processes so we are not aware of + * them until we walk through the process list in /proc. + */ +public final class PhantomProcessRecord { + static final String TAG = TAG_WITH_CLASS_NAME ? "PhantomProcessRecord" : TAG_AM; + + static final long[] LONG_OUT = new long[1]; + static final int[] LONG_FORMAT = new int[] {PROC_NEWLINE_TERM | PROC_OUT_LONG}; + + final String mProcessName; // name of the process + final int mUid; // uid of the process + final int mPid; // The id of the process + final int mPpid; // Ancestor (managed app process) pid of the process + final long mKnownSince; // The timestamp when we're aware of the process + final FileDescriptor mPidFd; // The fd to monitor the termination of this process + + long mLastCputime; // How long proc has run CPU at last check + long mCurrentCputime; // How long proc has run CPU most recently + int mUpdateSeq; // Seq no, indicating the last check on this process + int mAdj; // The last known oom adj score + boolean mKilled; // Whether it has been killed by us or not + boolean mZombie; // Whether it was signaled to be killed but timed out + String mStringName; // Caching of the toString() result + + final ActivityManagerService mService; + final Object mLock; + final Consumer<PhantomProcessRecord> mOnKillListener; + final Handler mKillHandler; + + PhantomProcessRecord(final String processName, final int uid, final int pid, + final int ppid, final ActivityManagerService service, + final Consumer<PhantomProcessRecord> onKillListener) throws IllegalStateException { + mProcessName = processName; + mUid = uid; + mPid = pid; + mPpid = ppid; + mKilled = false; + mAdj = ProcessList.NATIVE_ADJ; + mKnownSince = SystemClock.elapsedRealtime(); + mService = service; + mLock = service.mPhantomProcessList.mLock; + mOnKillListener = onKillListener; + mKillHandler = service.mProcessList.sKillHandler; + if (Process.supportsPidFd()) { + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + try { + mPidFd = Process.openPidFd(pid, 0); + if (mPidFd == null) { + throw new IllegalStateException(); + } + } catch (IOException e) { + // Maybe a race condition, the process is gone. + Slog.w(TAG, "Unable to open process " + pid + ", it might be gone"); + IllegalStateException ex = new IllegalStateException(); + ex.initCause(e); + throw ex; + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } + } else { + mPidFd = null; + } + } + + @GuardedBy("mLock") + void killLocked(String reason, boolean noisy) { + if (!mKilled) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill"); + if (noisy || mUid == mService.mCurOomAdjUid) { + mService.reportUidInfoMessageLocked(TAG, + "Killing " + toString() + ": " + reason, mUid); + } + if (mPid > 0) { + EventLog.writeEvent(EventLogTags.AM_KILL, UserHandle.getUserId(mUid), + mPid, mProcessName, mAdj, reason); + if (!Process.supportsPidFd()) { + onProcDied(false); + } else { + // We'll notify the listener when we're notified it's dead. + // Meanwhile, we'd also need handle the case of zombie processes. + mKillHandler.postDelayed(mProcKillTimer, this, + ProcessList.PROC_KILL_TIMEOUT); + } + Process.killProcessQuiet(mPid); + ProcessList.killProcessGroup(mUid, mPid); + } + mKilled = true; + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + } + + private Runnable mProcKillTimer = new Runnable() { + @Override + public void run() { + synchronized (mLock) { + // The process is maybe in either D or Z state. + Slog.w(TAG, "Process " + toString() + " is still alive after " + + ProcessList.PROC_KILL_TIMEOUT + "ms"); + // Force a cleanup as we can't keep the fd open forever + mZombie = true; + onProcDied(false); + // But still bookkeep it, so it won't be added as a new one if it's spotted again. + } + } + }; + + @GuardedBy("mLock") + void updateAdjLocked() { + if (Process.readProcFile("/proc/" + mPid + "/oom_score_adj", + LONG_FORMAT, null, LONG_OUT, null)) { + mAdj = (int) LONG_OUT[0]; + } + } + + @GuardedBy("mLock") + void onProcDied(boolean reallyDead) { + if (reallyDead) { + Slog.i(TAG, "Process " + toString() + " died"); + } + mKillHandler.removeCallbacks(mProcKillTimer, this); + if (mOnKillListener != null) { + mOnKillListener.accept(this); + } + } + + @Override + public String toString() { + if (mStringName != null) { + return mStringName; + } + StringBuilder sb = new StringBuilder(128); + sb.append("PhantomProcessRecord {"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(' '); + sb.append(mPid); + sb.append(':'); + sb.append(mPpid); + sb.append(':'); + sb.append(mProcessName); + sb.append('/'); + if (mUid < Process.FIRST_APPLICATION_UID) { + sb.append(mUid); + } else { + sb.append('u'); + sb.append(UserHandle.getUserId(mUid)); + int appId = UserHandle.getAppId(mUid); + if (appId >= Process.FIRST_APPLICATION_UID) { + sb.append('a'); + sb.append(appId - Process.FIRST_APPLICATION_UID); + } else { + sb.append('s'); + sb.append(appId); + } + if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) { + sb.append('i'); + sb.append(appId - Process.FIRST_ISOLATED_UID); + } + } + sb.append('}'); + return mStringName = sb.toString(); + } + + void dump(PrintWriter pw, String prefix) { + final long now = SystemClock.elapsedRealtime(); + pw.print(prefix); + pw.print("user #"); + pw.print(UserHandle.getUserId(mUid)); + pw.print(" uid="); + pw.print(mUid); + pw.print(" pid="); + pw.print(mPid); + pw.print(" ppid="); + pw.print(mPpid); + pw.print(" knownSince="); + TimeUtils.formatDuration(mKnownSince, now, pw); + pw.print(" killed="); + pw.println(mKilled); + pw.print(prefix); + pw.print("lastCpuTime="); + pw.print(mLastCputime); + if (mLastCputime > 0) { + pw.print(" timeUsed="); + TimeUtils.formatDuration(mCurrentCputime - mLastCputime, pw); + } + pw.print(" oom adj="); + pw.print(mAdj); + pw.print(" seq="); + pw.println(mUpdateSeq); + } + + boolean equals(final String processName, final int uid, final int pid) { + return mUid == uid && mPid == pid && TextUtils.equals(mProcessName, processName); + } +} diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index ced2f0f5ebc4..5e6556339b43 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -329,7 +329,7 @@ public final class ProcessList { /** * How long between a process kill and we actually receive its death recipient */ - private static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds; + static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds; /** * Native heap allocations will now have a non-zero tag in the most significant byte. diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index bac7565adfa5..60e59e389cf0 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -237,7 +237,7 @@ public class SettingsToPropertiesMapper { SystemProperties.set(key, value); } catch (Exception e) { // Failure to set a property can be caused by SELinux denial. This usually indicates - // that the property wasn't whitelisted in sepolicy. + // that the property wasn't allowlisted in sepolicy. // No need to report it on all user devices, only on debug builds. log("Unable to set property " + key + " value '" + value + "'", e); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 83bf28e2d3ba..eb60573e6f17 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -423,7 +423,7 @@ class UserController implements Handler.Callback { for (Integer userId : mUserLru) { UserState uss = mStartedUsers.get(userId); if (uss == null) { - // Shouldn't happen, but be sane if it does. + // Shouldn't happen, but recover if it does. continue; } if (uss.state == UserState.STATE_STOPPING diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f63c2ee5ee94..6110759b5a9e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1776,7 +1776,7 @@ public class AudioService extends IAudioService.Stub Settings.Global.getInt( cr, Settings.Global.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); int ringerMode = ringerModeFromSettings; - // sanity check in case the settings are restored from a device with incompatible + // validity check in case the settings are restored from a device with incompatible // ringer modes if (!isValidRingerMode(ringerMode)) { ringerMode = AudioManager.RINGER_MODE_NORMAL; @@ -3397,7 +3397,7 @@ public class AudioService extends IAudioService.Stub // For automotive, // - the car service is always running as system user // - foreground users are non-system users - // Car service is in charge of dispatching the key event include master mute to Android. + // Car service is in charge of dispatching the key event include global mute to Android. // Therefore, the getCurrentUser() is always different to the foreground user. if ((isPlatformAutomotive() && userId == UserHandle.USER_SYSTEM) || (getCurrentUserId() == userId)) { @@ -3409,7 +3409,7 @@ public class AudioService extends IAudioService.Stub } } - /** get master mute state. */ + /** get global mute state. */ public boolean isMasterMute() { return AudioSystem.getMasterMute(); } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index cbfa87f1f7f3..726cdc334943 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -170,7 +170,7 @@ class Convert { int len = config.ranges.size(); List<RadioManager.BandDescriptor> bands = new ArrayList<>(len); - // Just a dummy value. + // Just a placeholder value. int region = RadioManager.REGION_ITU_1; for (AmFmBandRange range : config.ranges) { diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index b33aa0a6fad3..91fa15a913a3 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -729,7 +729,7 @@ public class SyncManager { // Sync adapters were able to access the synced account without the accounts // permission which circumvents our permission model. Therefore, we require // sync adapters that don't have access to the account to get user consent. - // This can be noisy, therefore we will white-list sync adapters installed + // This can be noisy, therefore we will allowlist sync adapters installed // before we started checking for account access because they already know // the account (they run before) which is the genie is out of the bottle. whiteListExistingSyncAdaptersIfNeeded(); diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java index 09b7828cbfa2..478763531abc 100644 --- a/services/core/java/com/android/server/content/SyncOperation.java +++ b/services/core/java/com/android/server/content/SyncOperation.java @@ -50,7 +50,7 @@ public class SyncOperation { public static final int REASON_IS_SYNCABLE = -5; /** Sync started because it has just been set to sync automatically. */ public static final int REASON_SYNC_AUTO = -6; - /** Sync started because master sync automatically has been set to true. */ + /** Sync started because global sync automatically has been set to true. */ public static final int REASON_MASTER_SYNC_AUTO = -7; public static final int REASON_USER_START = -8; diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java index ad04b7d10433..c33b5f1cb58c 100644 --- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java +++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java @@ -48,7 +48,7 @@ public abstract class ContentCaptureManagerInternal { /** * Gets the content capture options for the given user and package, or {@code null} if the - * package is not whitelisted by the service. + * package is not allowlisted by the service. * * <p><b>NOTE: </b>this method is called by the {@code ActivityManager} service and hence cannot * hold the main service lock. diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index f42e18acc821..eb2c7e6105ae 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -190,7 +190,7 @@ class AutomaticBrightnessController { // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the // user's adjustment) immediately, but wait for a drastic enough change in the ambient light. - // The anchor determines what were the light levels when the user has set her preference, and + // The anchor determines what were the light levels when the user has set their preference, and // we use a relative threshold to determine when to revert to the OEM curve. private boolean mShortTermModelValid; private float mShortTermModelAnchor; diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index cc6687f8566d..b1c91a690d7f 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -700,7 +700,7 @@ public class DisplayModeDirector { // Application can specify preferred refresh rate with below attrs. // @see android.view.WindowManager.LayoutParams#preferredRefreshRate // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId - // System also forces some apps like blacklisted app to run at a lower refresh rate. + // System also forces some apps like denylisted app to run at a lower refresh rate. // @see android.R.array#config_highRefreshRateBlacklist public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3; public static final int PRIORITY_APP_REQUEST_SIZE = 4; diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 713f8008e313..425fbc5687ec 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -329,6 +329,30 @@ final class Constants { static final int INVALID_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INVALID; static final int PATH_INTERNAL = HdmiDeviceInfo.PATH_INTERNAL; + // The relationship from one path (physical address) to another. + @IntDef({ + PATH_RELATIONSHIP_UNKNOWN, + PATH_RELATIONSHIP_DIFFERENT_BRANCH, + PATH_RELATIONSHIP_ANCESTOR, + PATH_RELATIONSHIP_DESCENDANT, + PATH_RELATIONSHIP_SIBLING, + PATH_RELATIONSHIP_SAME + }) + @interface PathRelationship {} + + // One or both of the paths is invalid + static final int PATH_RELATIONSHIP_UNKNOWN = 0; + // None of the relationships below holds + static final int PATH_RELATIONSHIP_DIFFERENT_BRANCH = 1; + // A path is either the TV, or between the TV and another path + static final int PATH_RELATIONSHIP_ANCESTOR = 2; + // A path is located somewhere below another path + static final int PATH_RELATIONSHIP_DESCENDANT = 3; + // A path has the same parent as another path + static final int PATH_RELATIONSHIP_SIBLING = 4; + // A path is equal to another path + static final int PATH_RELATIONSHIP_SAME = 5; + // Strategy for device polling. // Should use "OR(|) operation of POLL_STRATEGY_XXX and POLL_ITERATION_XXX. static final int POLL_STRATEGY_MASK = 0x3; // first and second bit. diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index cd65db6055af..a6951ef1958f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -24,10 +24,11 @@ import android.util.Xml; import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; - import com.android.server.hdmi.Constants.AbortReason; import com.android.server.hdmi.Constants.AudioCodec; import com.android.server.hdmi.Constants.FeatureOpcode; +import com.android.server.hdmi.Constants.PathRelationship; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -311,26 +312,43 @@ final class HdmiUtils { * @return true if the new path in the active routing path */ static boolean isInActiveRoutingPath(int activePath, int newPath) { - // Check each nibble of the currently active path and the new path till the position - // where the active nibble is not zero. For (activePath, newPath), - // (1.1.0.0, 1.0.0.0) -> true, new path is a parent - // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant - // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling - // (1.0.0.0, 2.0.0.0) -> false, in a completely different path - for (int i = 12; i >= 0; i -= 4) { - int nibbleActive = (activePath >> i) & 0xF; - if (nibbleActive == 0) { - break; - } - int nibbleNew = (newPath >> i) & 0xF; - if (nibbleNew == 0) { - break; - } - if (nibbleActive != nibbleNew) { - return false; + @PathRelationship int pathRelationship = pathRelationship(newPath, activePath); + return (pathRelationship == Constants.PATH_RELATIONSHIP_ANCESTOR + || pathRelationship == Constants.PATH_RELATIONSHIP_DESCENDANT + || pathRelationship == Constants.PATH_RELATIONSHIP_SAME); + } + + /** + * Computes the relationship from the first path to the second path. + */ + static @PathRelationship int pathRelationship(int firstPath, int secondPath) { + if (firstPath == Constants.INVALID_PHYSICAL_ADDRESS + || secondPath == Constants.INVALID_PHYSICAL_ADDRESS) { + return Constants.PATH_RELATIONSHIP_UNKNOWN; + } + // Loop forwards through both paths, looking for the first nibble where the paths differ. + // Checking this nibble and the next one distinguishes between most possible relationships. + for (int nibbleIndex = 0; nibbleIndex <= 3; nibbleIndex++) { + int shift = 12 - nibbleIndex * 4; + int firstPathNibble = (firstPath >> shift) & 0xF; + int secondPathNibble = (secondPath >> shift) & 0xF; + // Found the first nibble where the paths differ. + if (firstPathNibble != secondPathNibble) { + int firstPathNextNibble = (firstPath >> (shift - 4)) & 0xF; + int secondPathNextNibble = (secondPath >> (shift - 4)) & 0xF; + if (firstPathNibble == 0) { + return Constants.PATH_RELATIONSHIP_ANCESTOR; + } else if (secondPathNibble == 0) { + return Constants.PATH_RELATIONSHIP_DESCENDANT; + } else if (nibbleIndex == 3 + || (firstPathNextNibble == 0 && secondPathNextNibble == 0)) { + return Constants.PATH_RELATIONSHIP_SIBLING; + } else { + return Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH; + } } } - return true; + return Constants.PATH_RELATIONSHIP_SAME; } /** diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 6672daa6f17a..7fc3d2b302ff 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -71,7 +71,7 @@ import java.util.Objects; * <p>See {@code com.android.server.autofill.AutofillManagerService} for a concrete * (no pun intended) example of how to use it. * - * @param <M> "master" service class. + * @param <M> "main" service class. * @param <S> "real" service class. * * @hide diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java index 16cf7eef6a1e..757a5cca0817 100644 --- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java @@ -41,7 +41,7 @@ import java.io.PrintWriter; * Companion for {@link AbstractMasterSystemService}, it's the base class for the "real" service * implementation. * - * @param <M> "master" service class. + * @param <M> "main" service class. * @param <S> "real" service class. * * @hide @@ -208,7 +208,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst } /** - * Gets the master service. + * Gets the main service. */ public final M getMaster() { return mMaster; @@ -301,7 +301,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst } /** - * Removes the service from the master's cache. + * Removes the service from the main service's cache. */ protected final void removeSelfFromCacheLocked() { mMaster.removeCachedServiceLocked(mUserId); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 3235b20fb75e..bba248c2ab74 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3201,7 +3201,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean res = false; if (mCurMethod != null) { if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken); - // create a dummy token for IMS so that IMS cannot inject windows into client app. + // create a placeholder token for IMS so that IMS cannot inject windows into client app. Binder showInputToken = new Binder(); mShowRequestWindowMap.put(showInputToken, windowToken); executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO( diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index f4d0a6254318..807784dde505 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -553,8 +553,9 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void registerLocationListener(LocationRequest request, ILocationListener listener, - String packageName, String attributionTag, String listenerId) { + public void registerLocationListener(String provider, LocationRequest request, + ILocationListener listener, String packageName, String attributionTag, + String listenerId) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, listenerId); int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), @@ -570,16 +571,16 @@ public class LocationManagerService extends ILocationManager.Stub { request = validateAndSanitizeLocationRequest(request, permissionLevel); - LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, - "provider \"" + request.getProvider() + "\" does not exist"); + "provider \"" + provider + "\" does not exist"); manager.registerLocationRequest(request, identity, permissionLevel, listener); } @Override - public void registerLocationPendingIntent(LocationRequest request, PendingIntent pendingIntent, - String packageName, String attributionTag) { + public void registerLocationPendingIntent(String provider, LocationRequest request, + PendingIntent pendingIntent, String packageName, String attributionTag) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, AppOpsManager.toReceiverId(pendingIntent)); int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), @@ -592,24 +593,22 @@ public class LocationManagerService extends ILocationManager.Stub { request = validateAndSanitizeLocationRequest(request, permissionLevel); - LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, - "provider \"" + request.getProvider() + "\" does not exist"); + "provider \"" + provider + "\" does not exist"); manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent); } private LocationRequest validateAndSanitizeLocationRequest(LocationRequest request, @PermissionLevel int permissionLevel) { - Objects.requireNonNull(request.getProvider()); - WorkSource workSource = request.getWorkSource(); if (workSource != null && !workSource.isEmpty()) { mContext.enforceCallingOrSelfPermission( permission.UPDATE_DEVICE_STATS, "setting a work source requires " + permission.UPDATE_DEVICE_STATS); } - if (request.getHideFromAppOps()) { + if (request.isHiddenFromAppOps()) { mContext.enforceCallingOrSelfPermission( permission.UPDATE_APP_OPS_STATS, "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS); @@ -620,12 +619,12 @@ public class LocationManagerService extends ILocationManager.Stub { "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS); } - LocationRequest sanitized = new LocationRequest(request); + LocationRequest.Builder sanitized = new LocationRequest.Builder(request); if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE) != PERMISSION_GRANTED) { - sanitized.setLowPowerMode(false); + sanitized.setLowPower(false); } if (permissionLevel < PERMISSION_FINE) { - switch (sanitized.getQuality()) { + switch (request.getQuality()) { case LocationRequest.ACCURACY_FINE: sanitized.setQuality(LocationRequest.ACCURACY_BLOCK); break; @@ -634,24 +633,21 @@ public class LocationManagerService extends ILocationManager.Stub { break; } - if (sanitized.getInterval() < FASTEST_COARSE_INTERVAL_MS) { - sanitized.setInterval(FASTEST_COARSE_INTERVAL_MS); + if (request.getIntervalMillis() < FASTEST_COARSE_INTERVAL_MS) { + sanitized.setIntervalMillis(FASTEST_COARSE_INTERVAL_MS); } - if (sanitized.getFastestInterval() < FASTEST_COARSE_INTERVAL_MS) { - sanitized.setFastestInterval(FASTEST_COARSE_INTERVAL_MS); + if (request.getMinUpdateIntervalMillis() < FASTEST_COARSE_INTERVAL_MS) { + sanitized.clearMinUpdateIntervalMillis(); } } - if (sanitized.getFastestInterval() > sanitized.getInterval()) { - sanitized.setFastestInterval(request.getInterval()); - } - if (sanitized.getWorkSource() != null) { - if (sanitized.getWorkSource().isEmpty()) { + if (request.getWorkSource() != null) { + if (request.getWorkSource().isEmpty()) { sanitized.setWorkSource(null); - } else if (sanitized.getWorkSource().getPackageName(0) == null) { + } else if (request.getWorkSource().getPackageName(0) == null) { Log.w(TAG, "received (and ignoring) illegal worksource with no package name"); sanitized.setWorkSource(null); } else { - List<WorkChain> workChains = sanitized.getWorkSource().getWorkChains(); + List<WorkChain> workChains = request.getWorkSource().getWorkChains(); if (workChains != null && !workChains.isEmpty() && workChains.get( 0).getAttributionTag() == null) { Log.w(TAG, @@ -661,7 +657,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - return sanitized; + return sanitized.build(); } @Override @@ -679,8 +675,7 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public Location getLastLocation(LocationRequest request, String packageName, - String attributionTag) { + public Location getLastLocation(String provider, String packageName, String attributionTag) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), identity.getPid()); @@ -690,15 +685,12 @@ public class LocationManagerService extends ILocationManager.Stub { // clients in the system process must have an attribution tag set Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null); - request = validateAndSanitizeLocationRequest(request, permissionLevel); - - LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + LocationProviderManager manager = getLocationProviderManager(provider); if (manager == null) { return null; } - Location location = manager.getLastLocation(identity, permissionLevel, - request.isLocationSettingsIgnored()); + Location location = manager.getLastLocation(identity, permissionLevel, false); // lastly - note app ops if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), @@ -710,7 +702,7 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void getCurrentLocation(LocationRequest request, + public void getCurrentLocation(String provider, LocationRequest request, ICancellationSignal cancellationTransport, ILocationCallback consumer, String packageName, String attributionTag, String listenerId) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, @@ -725,9 +717,9 @@ public class LocationManagerService extends ILocationManager.Stub { request = validateAndSanitizeLocationRequest(request, permissionLevel); - LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, - "provider \"" + request.getProvider() + "\" does not exist"); + "provider \"" + provider + "\" does not exist"); manager.getCurrentLocation(request, identity, permissionLevel, cancellationTransport, consumer); diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index 1815a8554705..830548b044a6 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -291,7 +291,7 @@ class LocationProviderManager extends @Override protected final ListenerOperation<LocationTransport> onActive() { - if (!getRequest().getHideFromAppOps()) { + if (!getRequest().isHiddenFromAppOps()) { mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey()); } onHighPowerUsageChanged(); @@ -301,7 +301,7 @@ class LocationProviderManager extends @Override protected final ListenerOperation<LocationTransport> onInactive() { onHighPowerUsageChanged(); - if (!getRequest().getHideFromAppOps()) { + if (!getRequest().isHiddenFromAppOps()) { mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey()); } return null; @@ -343,7 +343,7 @@ class LocationProviderManager extends if (isUsingHighPower != mIsUsingHighPower) { mIsUsingHighPower = isUsingHighPower; - if (!getRequest().getHideFromAppOps()) { + if (!getRequest().isHiddenFromAppOps()) { if (mIsUsingHighPower) { mLocationAttributionHelper.reportHighPowerLocationStart( getIdentity(), getName(), getKey()); @@ -362,7 +362,7 @@ class LocationProviderManager extends } return isActive() - && getRequest().getInterval() < MAX_HIGH_POWER_INTERVAL_MS + && getRequest().getIntervalMillis() < MAX_HIGH_POWER_INTERVAL_MS && getProperties().mPowerRequirement == Criteria.POWER_HIGH; } @@ -448,26 +448,26 @@ class LocationProviderManager extends } private LocationRequest calculateProviderLocationRequest() { - LocationRequest newRequest = new LocationRequest(super.getRequest()); + LocationRequest.Builder builder = new LocationRequest.Builder(super.getRequest()); - if (newRequest.isLocationSettingsIgnored()) { + if (super.getRequest().isLocationSettingsIgnored()) { // if we are not currently allowed use location settings ignored, disable it if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains( getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider( null, getIdentity())) { - newRequest.setLocationSettingsIgnored(false); + builder.setLocationSettingsIgnored(false); } } - if (!newRequest.isLocationSettingsIgnored() && !isThrottlingExempt()) { + if (!super.getRequest().isLocationSettingsIgnored() && !isThrottlingExempt()) { // throttle in the background if (!mForeground) { - newRequest.setInterval(Math.max(newRequest.getInterval(), + builder.setIntervalMillis(Math.max(super.getRequest().getIntervalMillis(), mSettingsHelper.getBackgroundThrottleIntervalMs())); } } - return newRequest; + return builder.build(); } private boolean isThrottlingExempt() { @@ -635,14 +635,15 @@ class LocationProviderManager extends long deltaMs = NANOSECONDS.toMillis( location.getElapsedRealtimeNanos() - mLastLocation.getElapsedRealtimeNanos()); - if (deltaMs < getRequest().getFastestInterval() - MAX_FASTEST_INTERVAL_JITTER_MS) { + if (deltaMs < getRequest().getMinUpdateIntervalMillis() + - MAX_FASTEST_INTERVAL_JITTER_MS) { return null; } // check smallest displacement - double smallestDisplacement = getRequest().getSmallestDisplacement(); - if (smallestDisplacement > 0.0 && location.distanceTo(mLastLocation) - <= smallestDisplacement) { + double smallestDisplacementM = getRequest().getMinUpdateDistanceMeters(); + if (smallestDisplacementM > 0.0 && location.distanceTo(mLastLocation) + <= smallestDisplacementM) { return null; } } @@ -692,7 +693,7 @@ class LocationProviderManager extends if (success) { // check num updates - if successful then this function will always be run // from the same thread, and no additional synchronization is necessary - boolean remove = ++mNumLocationsDelivered >= getRequest().getNumUpdates(); + boolean remove = ++mNumLocationsDelivered >= getRequest().getMaxUpdates(); if (remove) { if (D) { Log.d(TAG, "removing " + getIdentity() + " from " + mName @@ -1326,10 +1327,10 @@ class LocationProviderManager extends public void getCurrentLocation(LocationRequest request, CallerIdentity callerIdentity, int permissionLevel, ICancellationSignal cancellationTransport, ILocationCallback callback) { - Preconditions.checkArgument(mName.equals(request.getProvider())); - - if (request.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) { - request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); + if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) { + request = new LocationRequest.Builder(request) + .setDurationMillis(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) + .build(); } GetCurrentLocationListenerRegistration registration = @@ -1404,8 +1405,6 @@ class LocationProviderManager extends public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, @PermissionLevel int permissionLevel, ILocationListener listener) { - Preconditions.checkArgument(mName.equals(request.getProvider())); - synchronized (mLock) { long identity = Binder.clearCallingIdentity(); try { @@ -1424,8 +1423,6 @@ class LocationProviderManager extends public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, @PermissionLevel int permissionLevel, PendingIntent pendingIntent) { - Preconditions.checkArgument(mName.equals(request.getProvider())); - synchronized (mLock) { long identity = Binder.clearCallingIdentity(); try { @@ -1517,17 +1514,17 @@ class LocationProviderManager extends LocationStatsEnums.USAGE_STARTED, LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, registration.getIdentity().getPackageName(), + mName, registration.getRequest(), key instanceof PendingIntent, - key instanceof IBinder, - /* geofence= */ null, - registration.isForeground()); + /* geofence= */ key instanceof IBinder, + null, registration.isForeground()); mLocationRequestStatistics.startRequesting( registration.getIdentity().getPackageName(), registration.getIdentity().getAttributionTag(), mName, - registration.getRequest().getInterval(), + registration.getRequest().getIntervalMillis(), registration.isForeground()); } @@ -1547,11 +1544,11 @@ class LocationProviderManager extends LocationStatsEnums.USAGE_ENDED, LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, registration.getIdentity().getPackageName(), + mName, registration.getRequest(), key instanceof PendingIntent, - key instanceof IBinder, - /* geofence= */ null, - registration.isForeground()); + /* geofence= */ key instanceof IBinder, + null, registration.isForeground()); } @GuardedBy("mLock") @@ -1643,15 +1640,20 @@ class LocationProviderManager extends for (Registration registration : registrations) { LocationRequest locationRequest = registration.getRequest(); + // passive requests do not contribute to the provider + if (locationRequest.getIntervalMillis() == LocationRequest.PASSIVE_INTERVAL) { + continue; + } + if (locationRequest.isLocationSettingsIgnored()) { providerRequest.setLocationSettingsIgnored(true); } - if (!locationRequest.isLowPowerMode()) { + if (!locationRequest.isLowPower()) { providerRequest.setLowPowerMode(false); } providerRequest.setInterval( - Math.min(locationRequest.getInterval(), providerRequest.getInterval())); + Math.min(locationRequest.getIntervalMillis(), providerRequest.getInterval())); providerRegistrations.add(registration); } @@ -1674,7 +1676,7 @@ class LocationProviderManager extends } for (int i = 0; i < registrationsSize; i++) { LocationRequest request = providerRegistrations.get(i).getRequest(); - if (request.getInterval() <= thresholdIntervalMs) { + if (request.getIntervalMillis() <= thresholdIntervalMs) { providerRequest.getWorkSource().add(providerRegistrations.get(i).getWorkSource()); } } diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java index d4999ab8be0a..92ef5b7c73a8 100644 --- a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java +++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java @@ -20,11 +20,16 @@ import android.annotation.Nullable; import android.content.Context; import android.location.Location; import android.location.LocationManager; +import android.location.LocationRequest; import android.os.Binder; +import com.android.internal.location.ProviderRequest; import com.android.internal.util.Preconditions; import com.android.server.location.util.Injector; +import java.util.ArrayList; +import java.util.Collection; + class PassiveLocationProviderManager extends LocationProviderManager { PassiveLocationProviderManager(Context context, Injector injector) { @@ -57,4 +62,20 @@ class PassiveLocationProviderManager extends LocationProviderManager { } } } + + @Override + protected ProviderRequest mergeRequests(Collection<Registration> registrations) { + ProviderRequest.Builder providerRequest = new ProviderRequest.Builder() + .setInterval(0); + + ArrayList<LocationRequest> requests = new ArrayList<>(registrations.size()); + for (Registration registration : registrations) { + requests.add(registration.getRequest()); + if (registration.getRequest().isLocationSettingsIgnored()) { + providerRequest.setLocationSettingsIgnored(true); + } + } + + return providerRequest.setLocationRequests(requests).build(); + } } diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java index 2d7f02873b8f..7f02b2a807c3 100644 --- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java +++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java @@ -16,6 +16,7 @@ package com.android.server.location.geofence; +import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.KEY_PROXIMITY_ENTERING; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; @@ -350,11 +351,11 @@ public class GeofenceManager extends LocationStatsEnums.USAGE_ENDED, LocationStatsEnums.API_REQUEST_GEOFENCE, registration.getIdentity().getPackageName(), + null, /* LocationRequest= */ null, /* hasListener= */ false, true, - registration.getRequest(), - true); + registration.getRequest(), true); } @Override @@ -363,16 +364,17 @@ public class GeofenceManager extends LocationStatsEnums.USAGE_ENDED, LocationStatsEnums.API_REQUEST_GEOFENCE, registration.getIdentity().getPackageName(), + null, /* LocationRequest= */ null, /* hasListener= */ false, true, - registration.getRequest(), - true); + registration.getRequest(), true); } @Override protected boolean registerWithService(LocationRequest locationRequest) { - getLocationManager().requestLocationUpdates(locationRequest, DIRECT_EXECUTOR, this); + getLocationManager().requestLocationUpdates(FUSED_PROVIDER, locationRequest, + DIRECT_EXECUTOR, this); return true; } @@ -417,13 +419,11 @@ public class GeofenceManager extends intervalMs = mSettingsHelper.getBackgroundThrottleProximityAlertIntervalMs(); } - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - LocationManager.FUSED_PROVIDER, intervalMs, 0, false); - request.setFastestInterval(0); - request.setHideFromAppOps(true); - request.setWorkSource(workSource); - - return request; + return new LocationRequest.Builder(intervalMs) + .setMinUpdateIntervalMillis(0) + .setHiddenFromAppOps(true) + .setWorkSource(workSource) + .build(); } diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java index 14ab79e7ecde..890f51b44ace 100644 --- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java @@ -200,7 +200,7 @@ class GnssConfiguration { } /** - * Updates the GNSS HAL satellite blacklist. + * Updates the GNSS HAL satellite denylist. */ void setSatelliteBlacklist(int[] constellations, int[] svids) { native_set_satellite_blacklist(constellations, svids); diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index a4486d7b5898..0b20bdc1bb3b 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -45,6 +45,7 @@ import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; @@ -273,7 +274,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // if the fix interval is below this we leave GPS on, // if above then we cycle the GPS driver. - // Typical hot TTTF is ~5 seconds, so 10 seconds seems sane. + // Typical hot TTTF is ~5 seconds, so 10 seconds seems valid. private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000; // how long to wait if we have a network error in NTP or PSDS downloading @@ -700,9 +701,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements Context.LOCATION_SERVICE); String provider; LocationChangeListener locationListener; - LocationRequest locationRequest = new LocationRequest() - .setInterval(LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS) - .setFastestInterval(LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS); + LocationRequest.Builder locationRequest = new LocationRequest.Builder( + LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS); if (independentFromGnss) { // For fast GNSS TTFF @@ -716,8 +716,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements locationRequest.setQuality(LocationRequest.ACCURACY_FINE); } - locationRequest.setProvider(provider); - // Ignore location settings if in emergency mode. This is only allowed for // isUserEmergency request (introduced in HAL v2.0), or HAL v1.1. if (mNIHandler.getInEmergency()) { @@ -735,8 +733,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements provider, durationMillis)); try { - locationManager.requestLocationUpdates(locationRequest, - locationListener, mHandler.getLooper()); + locationManager.requestLocationUpdates(provider, locationRequest.build(), + new HandlerExecutor(mHandler), locationListener); locationListener.mNumLocationUpdateRequest++; mHandler.postDelayed(() -> { if (--locationListener.mNumLocationUpdateRequest == 0) { @@ -1952,20 +1950,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // listen for PASSIVE_PROVIDER updates LocationManager locManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); - long minTime = 0; - float minDistance = 0; - LocationRequest request = LocationRequest.createFromDeprecatedProvider( - LocationManager.PASSIVE_PROVIDER, - minTime, - minDistance, - false); - // Don't keep track of this request since it's done on behalf of other clients - // (which are kept track of separately). - request.setHideFromAppOps(true); locManager.requestLocationUpdates( - request, - new NetworkLocationListener(), - getLooper()); + LocationManager.PASSIVE_PROVIDER, + new LocationRequest.Builder(0) + .setHiddenFromAppOps(true) + .build(), + new HandlerExecutor(this), + new NetworkLocationListener()); updateEnabled(); } diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index 0815d46a735d..37db02337a2f 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -141,11 +141,11 @@ public class GnssMeasurementsProvider extends LocationStatsEnums.USAGE_STARTED, LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER, registration.getIdentity().getPackageName(), - /* LocationRequest= */ null, - /* hasListener= */ true, - /* hasIntent= */ false, - /* geofence= */ null, - registration.isForeground()); + null, + null, + true, + false, + null, registration.isForeground()); } @Override @@ -154,11 +154,11 @@ public class GnssMeasurementsProvider extends LocationStatsEnums.USAGE_ENDED, LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER, registration.getIdentity().getPackageName(), - /* LocationRequest= */ null, - /* hasListener= */ true, - /* hasIntent= */ false, - /* geofence= */ null, - registration.isForeground()); + null, + null, + true, + false, + null, registration.isForeground()); } /** diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java index cea5a69526c6..222750629802 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java @@ -518,7 +518,7 @@ class GnssNetworkConnectivityHandler { if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) { if (apn == null) { - // assign a dummy value in the case of C2K as otherwise we will have a runtime + // assign a placeholder value in the case of C2K as otherwise we will have a runtime // exception in the following call to native_agps_data_conn_open apn = "dummy-apn"; } diff --git a/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java b/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java index dccef9b9a9c4..426ce8c2c258 100644 --- a/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java +++ b/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java @@ -31,7 +31,7 @@ import java.util.ArrayList; import java.util.List; /** - * Detects blacklist change and updates the blacklist. + * Detects denylist change and updates the denylist. */ class GnssSatelliteBlacklistHelper { diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java index 19f79273c992..68813b3e0777 100644 --- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java @@ -71,11 +71,11 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu LocationStatsEnums.USAGE_STARTED, LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK, registration.getIdentity().getPackageName(), - /* LocationRequest= */ null, - /* hasListener= */ true, - /* hasIntent= */ false, - /* geofence= */ null, - registration.isForeground()); + null, + null, + true, + false, + null, registration.isForeground()); } @Override @@ -84,10 +84,11 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu LocationStatsEnums.USAGE_ENDED, LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK, registration.getIdentity().getPackageName(), - /* LocationRequest= */ null, - /* hasListener= */ true, - /* hasIntent= */ false, - /* geofence= */ null, + null, + null, + true, + false, + null, registration.isForeground()); } diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java index 8a6b8aa1e463..feb4fbd1de31 100644 --- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java @@ -88,7 +88,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, private boolean mServiceRegistered = false; @GuardedBy("mRegistrations") - private TMergedRequest mCurrentRequest; + @Nullable private TMergedRequest mCurrentRequest; /** * Should be implemented to register with the backing service with the given merged request, and diff --git a/services/core/java/com/android/server/location/util/LocationUsageLogger.java b/services/core/java/com/android/server/location/util/LocationUsageLogger.java index 2e50d4fecf15..a229964d8701 100644 --- a/services/core/java/com/android/server/location/util/LocationUsageLogger.java +++ b/services/core/java/com/android/server/location/util/LocationUsageLogger.java @@ -49,7 +49,7 @@ public class LocationUsageLogger { * Log a location API usage event. */ public void logLocationApiUsage(int usageType, int apiInUse, - String packageName, LocationRequest locationRequest, + String packageName, String provider, LocationRequest locationRequest, boolean hasListener, boolean hasIntent, Geofence geofence, boolean foreground) { try { @@ -64,22 +64,22 @@ public class LocationUsageLogger { usageType, apiInUse, packageName, isLocationRequestNull ? LocationStatsEnums.PROVIDER_UNKNOWN - : bucketizeProvider(locationRequest.getProvider()), + : bucketizeProvider(provider), isLocationRequestNull ? LocationStatsEnums.QUALITY_UNKNOWN : locationRequest.getQuality(), isLocationRequestNull ? LocationStatsEnums.INTERVAL_UNKNOWN - : bucketizeInterval(locationRequest.getInterval()), + : bucketizeInterval(locationRequest.getIntervalMillis()), isLocationRequestNull ? LocationStatsEnums.DISTANCE_UNKNOWN : bucketizeDistance( - locationRequest.getSmallestDisplacement()), - isLocationRequestNull ? 0 : locationRequest.getNumUpdates(), + locationRequest.getMinUpdateDistanceMeters()), + isLocationRequestNull ? 0 : locationRequest.getMaxUpdates(), // only log expireIn for USAGE_STARTED isLocationRequestNull || usageType == LocationStatsEnums.USAGE_ENDED ? LocationStatsEnums.EXPIRATION_UNKNOWN - : bucketizeExpireIn(locationRequest.getExpireIn()), + : bucketizeExpireIn(locationRequest.getDurationMillis()), getCallbackType(apiInUse, hasListener, hasIntent), isGeofenceNull ? LocationStatsEnums.RADIUS_UNKNOWN diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index dbc725ea93e8..1b43db2a421d 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2581,11 +2581,11 @@ public class LockSettingsService extends ILockSettings.Stub { * make it consistent with current behaviour. It also allows ActivityManager to call * unlockUser() with empty secret. * 3. Once a user is migrated to have synthetic password, its value will never change, no matter - * whether the user changes his lockscreen PIN or clear/reset it. When the user clears its + * whether the user changes their lockscreen PIN or clear/reset it. When the user clears its * lockscreen PIN, we still maintain the existing synthetic password in a password blob * protected by a default PIN. * 4. The user SID is linked with synthetic password, but its cleared/re-created when the user - * clears/re-creates his lockscreen PIN. + * clears/re-creates their lockscreen PIN. * * * Different cases of calling this method: diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index e31bf3d514ed..7ab8b27075b7 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -149,15 +149,15 @@ public class SyntheticPasswordManager { } /** - * This class represents the master cryptographic secret for a given user (a.k.a synthietic + * This class represents the main cryptographic secret for a given user (a.k.a synthietic * password). This secret is derived from the user's lockscreen credential or password escrow * token. All other cryptograhic keys related to the user, including disk encryption key, * keystore encryption key, gatekeeper auth key, vendor auth secret and others are directly * derived from this token. * <p> - * The master secret associated with an authentication token is retrievable from + * The main secret associated with an authentication token is retrievable from * {@link AuthenticationToken#getSyntheticPassword()} and the authentication token can be - * reconsturcted from the master secret later with + * reconsturcted from the main secret later with * {@link AuthenticationToken#recreateDirectly(byte[])}. The first time an authentication token * is needed, it should be created with {@link AuthenticationToken#create()} so that the * necessary escrow data ({@link #mEncryptedEscrowSplit0} and {@link #mEscrowSplit1}) is @@ -166,7 +166,7 @@ public class SyntheticPasswordManager { * needs to securely store the secret returned from * {@link AuthenticationToken#getEscrowSecret()}, and at the time of use, load the escrow data * back with {@link AuthenticationToken#setEscrowData(byte[], byte[])} and then re-create the - * master secret from the escrow secret via + * main secret from the escrow secret via * {@link AuthenticationToken#recreateFromEscrow(byte[])}. */ static class AuthenticationToken { diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index d34502922b66..8e932155d8a7 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -825,7 +825,7 @@ public class MediaSessionService extends SystemService implements Monitor { * Information about a full user and its corresponding managed profiles. * * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate - * them when he/she presses a media/volume button. So keeping media sessions for them in one + * them when they press a media/volume button. So keeping media sessions for them in one * place makes more sense and increases the readability.</p> * <p>The contents of this object is guarded by {@link #mLock}. */ diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 3cafafffc62a..05f280884432 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -16,7 +16,6 @@ package com.android.server.net; -import static android.Manifest.permission.NETWORK_STACK; import static android.provider.Settings.ACTION_VPN_SETTINGS; import android.annotation.NonNull; @@ -24,10 +23,8 @@ import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.LinkProperties; @@ -41,6 +38,7 @@ import android.text.TextUtils; import android.util.Slog; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; @@ -63,7 +61,7 @@ public class LockdownVpnTracker { /** Number of VPN attempts before waiting for user intervention. */ private static final int MAX_ERROR_COUNT = 4; - private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET"; + public static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET"; @NonNull private final Context mContext; @NonNull private final ConnectivityService mConnService; @@ -104,13 +102,6 @@ public class LockdownVpnTracker { mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0); } - private BroadcastReceiver mResetReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - reset(); - } - }; - /** * Watch for state changes to both active egress network, kicking off a VPN * connection when ready, or setting firewall rules once VPN is connected. @@ -200,9 +191,6 @@ public class LockdownVpnTracker { mVpn.setEnableTeardown(false); mVpn.setLockdown(true); - - final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET); - mContext.registerReceiver(mResetReceiver, resetFilter, NETWORK_STACK, mHandler); handleStateChangedLocked(); } @@ -222,10 +210,14 @@ public class LockdownVpnTracker { mVpn.setLockdown(false); hideNotification(); - mContext.unregisterReceiver(mResetReceiver); mVpn.setEnableTeardown(true); } + /** + * Reset VPN lockdown tracker. Called by ConnectivityService when receiving + * {@link #ACTION_LOCKDOWN_RESET} pending intent. + */ + @GuardedBy("mConnService.mVpns") public void reset() { Slog.d(TAG, "reset()"); synchronized (mStateLock) { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 48f1ddb023fd..407cedf38917 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -72,11 +72,11 @@ public abstract class NetworkPolicyManagerInternal { } /** - * Informs that an appId has been added or removed from the temp-powersave-whitelist so that + * Informs that an appId has been added or removed from the temp-powersave-allowlist so that * that network rules for that appId can be updated. * - * @param appId The appId which has been updated in the whitelist. - * @param added Denotes whether the {@param appId} has been added or removed from the whitelist. + * @param appId The appId which has been updated in the allowlist. + * @param added Denotes whether the {@param appId} has been added or removed from the allowlist. */ public abstract void onTempPowerSaveWhitelistChange(int appId, boolean added); @@ -107,7 +107,7 @@ public abstract class NetworkPolicyManagerInternal { public abstract void onAdminDataAvailable(); /** - * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may + * Control if a UID should be allowlisted even if it's in app idle mode. Other restrictions may * still be in effect. */ public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index ded77b394066..2acc60db52e3 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -478,7 +478,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // and "battery saver" are not equivalent. /** - * UIDs that have been white-listed to always be able to have network access + * UIDs that have been allowlisted to always be able to have network access * in power save mode, except device idle (doze) still applies. * TODO: An int array might be sufficient */ @@ -486,7 +486,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final SparseBooleanArray mPowerSaveWhitelistExceptIdleAppIds = new SparseBooleanArray(); /** - * UIDs that have been white-listed to always be able to have network access + * UIDs that have been allowlisted to always be able to have network access * in power save mode. * TODO: An int array might be sufficient */ @@ -497,21 +497,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray(); /** - * UIDs that have been white-listed temporarily to be able to have network access despite being + * UIDs that have been allowlisted temporarily to be able to have network access despite being * idle. Other power saving restrictions still apply. */ @GuardedBy("mUidRulesFirstLock") private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray(); /** - * UIDs that have been initially white-listed by system to avoid restricted background. + * UIDs that have been initially allowlisted by system to avoid restricted background. */ @GuardedBy("mUidRulesFirstLock") private final SparseBooleanArray mDefaultRestrictBackgroundAllowlistUids = new SparseBooleanArray(); /** - * UIDs that have been initially white-listed by system to avoid restricted background, + * UIDs that have been initially allowlisted by system to avoid restricted background, * but later revoked by user. */ @GuardedBy("mUidRulesFirstLock") @@ -820,7 +820,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // ignored; both services live in system_server } - // listen for changes to power save whitelist + // listen for changes to power save allowlist final IntentFilter whitelistFilter = new IntentFilter( PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); mContext.registerReceiver(mPowerSaveWhitelistReceiver, whitelistFilter, null, mHandler); @@ -3784,7 +3784,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } // NOTE: since both fw_dozable and fw_powersave uses the same map - // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method. + // (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method. @GuardedBy("mUidRulesFirstLock") private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain, SparseIntArray rules) { @@ -3826,12 +3826,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** - * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze + * Returns whether a uid is allowlisted from power saving restrictions (eg: Battery Saver, Doze * mode, and app idle). * * @param deviceIdleMode if true then we don't consider * {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is - * whitelisted. + * allowlisted. */ @GuardedBy("mUidRulesFirstLock") private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) { @@ -3845,7 +3845,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } // NOTE: since both fw_dozable and fw_powersave uses the same map - // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method. + // (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method. @GuardedBy("mUidRulesFirstLock") private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) { if (enabled) { @@ -4104,7 +4104,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** - * Set whether or not an app should be whitelisted for network access while in app idle. Other + * Set whether or not an app should be allowlisted for network access while in app idle. Other * power saving restrictions may still apply. */ @VisibleForTesting @@ -4133,7 +4133,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - /** Return the list of UIDs currently in the app idle whitelist. */ + /** Return the list of UIDs currently in the app idle allowlist. */ @VisibleForTesting int[] getAppIdleWhitelist() { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); @@ -4153,7 +4153,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { boolean isUidIdle(int uid) { synchronized (mUidRulesFirstLock) { if (mAppIdleTempWhitelistAppIds.get(uid)) { - // UID is temporarily whitelisted. + // UID is temporarily allowlisted. return false; } } diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index d48570fa3b0f..48ec9b4b502d 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -465,7 +465,7 @@ public class BackgroundDexOptService extends JobService { } private int reconcileSecondaryDexFiles(DexManager dm) { - // TODO(calin): should we blacklist packages for which we fail to reconcile? + // TODO(calin): should we denylist packages for which we fail to reconcile? for (String p : dm.getAllPackagesWithSecondaryDexFiles()) { if (mAbortIdleOptimization.get()) { return OPTIMIZE_ABORT_BY_JOB_SCHEDULER; diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java index 784f70118e4e..da65fe2bc0ab 100644 --- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java +++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java @@ -204,7 +204,7 @@ public class DynamicCodeLoggingService extends JobService { // // A typical message might look like this: // type=1400 audit(0.0:521): avc: granted { execute } for comm="executable" - // path="/data/data/com.dummy.app/executable" dev="sda13" ino=1655302 + // path="/data/data/com.placeholder.app/executable" dev="sda13" ino=1655302 // scontext=u:r:untrusted_app_27:s0:c66,c257,c512,c768 // tcontext=u:object_r:app_data_file:s0:c66,c257,c512,c768 tclass=file // diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 42e6d8f0bf83..fc02b3439d16 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -193,7 +193,7 @@ public class PackageDexOptimizer { String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts( pkg, sharedLibraries, pathsWithCode); - // Sanity check that we do not call dexopt with inconsistent data. + // Validity check that we do not call dexopt with inconsistent data. if (paths.size() != classLoaderContexts.length) { String[] splitCodePaths = pkg.getSplitCodePaths(); throw new IllegalStateException("Inconsistent information " @@ -676,7 +676,7 @@ public class PackageDexOptimizer { int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0; // Some apps are executed with restrictions on hidden API usage. If this app is one // of them, pass a flag to dexopt to enable the same restrictions during compilation. - // TODO we should pass the actual flag value to dexopt, rather than assuming blacklist + // TODO we should pass the actual flag value to dexopt, rather than assuming denylist // TODO(b/135203078): This flag is no longer set as part of AndroidPackage // and may not be preserved int hiddenApiFlag = hiddenApiEnforcementPolicy == HIDDEN_API_ENFORCEMENT_DISABLED diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 87dedfe9c84a..bcb5fdba7a3b 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2606,6 +2606,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { DexMetadataHelper.buildDexMetadataPathForApk(targetFile.getName())); stageFileLocked(dexMetadataFile, targetDexMetadataFile); + maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile); } private static ApkChecksum[] createApkChecksums(String splitName, @@ -2662,20 +2663,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @GuardedBy("mLock") - private void inheritFileLocked(File origFile) { - mResolvedInheritedFiles.add(origFile); - + private void maybeInheritFsveritySignatureLocked(File origFile) { // Inherit the fsverity signature file if present. final File fsveritySignatureFile = new File( VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); if (fsveritySignatureFile.exists()) { mResolvedInheritedFiles.add(fsveritySignatureFile); } + } + + @GuardedBy("mLock") + private void inheritFileLocked(File origFile) { + mResolvedInheritedFiles.add(origFile); + + maybeInheritFsveritySignatureLocked(origFile); + // Inherit the dex metadata if present. final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(origFile); if (dexMetadataFile != null) { mResolvedInheritedFiles.add(dexMetadataFile); + maybeInheritFsveritySignatureLocked(dexMetadataFile); } // Inherit the digests if present. final File digestsFile = ApkChecksums.findDigestsForFile(origFile); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 67412a8da7f5..3182e0d6d145 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1849,7 +1849,7 @@ public class PackageManagerService extends IPackageManager.Stub Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mLock) { removeMessages(WRITE_PACKAGE_LIST); - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.writePackageListLPr(msg.arg1); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -2142,7 +2142,7 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.sendPackageRemovedBroadcasts(killApp); } - // Whitelist any restricted permissions first as some may be runtime + // Allowlist any restricted permissions first as some may be runtime // that the installer requested to be granted at install time. if (whitelistedRestrictedPermissions != null && !whitelistedRestrictedPermissions.isEmpty()) { @@ -3520,7 +3520,7 @@ public class PackageManagerService extends IPackageManager.Stub + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); - mPermissionManager.readStateFromPackageSettingsTEMP(); + mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some @@ -3617,7 +3617,7 @@ public class PackageManagerService extends IPackageManager.Stub ver.fingerprint = Build.FINGERPRINT; } - // Grandfather existing (installed before Q) non-system apps to hide + // Legacy existing (installed before Q) non-system apps to hide // their icons in launcher. if (!mOnlyCore && mIsPreQUpgrade) { Slog.i(TAG, "Whitelisting all existing apps to hide their icons"); @@ -7481,7 +7481,7 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } else { // we have an instant application locally, but, we can't admit that since - // callers shouldn't be able to determine prior browsing. create a dummy + // callers shouldn't be able to determine prior browsing. create a placeholder // auxiliary response so the downstream code behaves as if there's an // instant application available externally. when it comes time to start // the instant application, we'll do the right thing. @@ -9531,10 +9531,16 @@ public class PackageManagerService extends IPackageManager.Stub } } + // The version of the application on the /system partition is less than or + // equal to the version on the /data partition. Throw an exception and use + // the application already installed on the /data partition. if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { - // The version of the application on the /system partition is less than or - // equal to the version on the /data partition. Throw an exception and use - // the application already installed on the /data partition. + // In the case of a skipped package, commitReconciledScanResultLocked is not called to + // add the object to the "live" data structures, so this is the final mutation step + // for the package. Which means it needs to be finalized here to cache derived fields. + // This is relevant for cases where the disabled system package is used for flags or + // other metadata. + ((ParsedPackage) parsedPackage).hideAsFinal(); throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName() + " at " + parsedPackage.getPath() + " ignored: updated version " + pkgSetting.versionCode + " better than this " @@ -11101,7 +11107,7 @@ public class PackageManagerService extends IPackageManager.Stub if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) { // Exempt SharedUsers signed with the platform key. // TODO(b/72378145) Fix this exemption. Force signature apps - // to whitelist their privileged permissions just like other + // to allowlist their privileged permissions just like other // priv-apps. synchronized (mLock) { PackageSetting platformPkgSetting = mSettings.mPackages.get("android"); @@ -16461,7 +16467,7 @@ public class PackageManagerService extends IPackageManager.Stub /** * A container of all data needed to commit a package to in-memory data structures and to disk. - * TODO: move most of the data contained her into a PackageSetting for commit. + * TODO: move most of the data contained here into a PackageSetting for commit. */ private static class ReconciledPackage { public final ReconcileRequest request; @@ -17231,7 +17237,7 @@ public class PackageManagerService extends IPackageManager.Stub // Notify BackgroundDexOptService that the package has been changed. // If this is an update of a package which used to fail to compile, - // BackgroundDexOptService will remove it from its blacklist. + // BackgroundDexOptService will remove it from its denylist. // TODO: Layering violation BackgroundDexOptService.notifyPackageChanged(packageName); @@ -21827,7 +21833,7 @@ public class PackageManagerService extends IPackageManager.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); DumpState dumpState = new DumpState(); boolean fullPreferred = false; @@ -23707,7 +23713,7 @@ public class PackageManagerService extends IPackageManager.Stub mDirtyUsers.remove(userId); mUserNeedsBadging.delete(userId); mPermissionManager.onUserRemoved(userId); - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.removeUserLPw(userId); mPendingBroadcasts.remove(userId); mInstantAppRegistry.onUserRemovedLPw(userId); @@ -23808,9 +23814,9 @@ public class PackageManagerService extends IPackageManager.Stub boolean readPermissionStateForUser(@UserIdInt int userId) { synchronized (mPackages) { - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.readPermissionStateForUserSyncLPr(userId); - mPermissionManager.readStateFromPackageSettingsTEMP(); + mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); return mPmInternal.isPermissionUpgradeNeeded(userId); } } @@ -25824,12 +25830,12 @@ public class PackageManagerService extends IPackageManager.Stub /** * Temporary method that wraps mSettings.writeLPr() and calls - * mPermissionManager.writeStateToPackageSettingsTEMP() beforehand. + * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand. * * TODO(zhanghai): This should be removed once we finish migration of permission storage. */ private void writeSettingsLPrTEMP() { - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.writeLPr(); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 4d2e22a2640e..d77683e8ba61 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2753,7 +2753,7 @@ class PackageManagerShellCommand extends ShellCommand { final InstallParams params = new InstallParams(); params.sessionParams = sessionParams; - // Whitelist all permissions by default + // Allowlist all permissions by default sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; String opt; diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 4476e8ac8d90..276f88082df0 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -42,7 +42,9 @@ import java.util.Set; * Settings data for a particular package we know about. */ public class PackageSetting extends PackageSettingBase { - int appId; + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public int appId; /** * This can be null whenever a physical APK on device is missing. This can be the result of diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java index d4a02a9ceb2a..eae89f00eb99 100644 --- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java +++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java @@ -497,7 +497,7 @@ class ShortcutRequestPinProcessor { Slog.d(TAG, "Temporarily adding " + shortcutId + " as dynamic"); } // Add as a dynamic shortcut. In order for a shortcut to be dynamic, it must - // have a target activity, so we set a dummy here. It's later removed + // have a target activity, so we set a placeholder here. It's later removed // in deleteDynamicWithId(). if (original.getActivity() == null) { original.setActivity(mService.getDummyMainActivity(appPackageName)); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 8412077d9a11..96f9982a16a5 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -4005,7 +4005,7 @@ public class ShortcutService extends IShortcutService.Stub { } /** - * Create a dummy "main activity" component name which is used to create a dynamic shortcut + * Create a placeholder "main activity" component name which is used to create a dynamic shortcut * with no main activity temporarily. */ @NonNull diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 462b21535371..ae13bdced9d1 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -1372,7 +1372,7 @@ public class StagingManager { } /** - * A dummy state for starting the pre reboot verification. + * A placeholder state for starting the pre reboot verification. * * See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification */ diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index f7e9e34a4702..87a883c92761 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -726,7 +726,7 @@ public class UserRestrictionsUtils { case android.provider.Settings.Secure.ALWAYS_ON_VPN_APP: case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN: case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST: - // Whitelist system uid (ConnectivityService) and root uid to change always-on vpn + // Allowlist system uid (ConnectivityService) and root uid to change always-on vpn final int appId = UserHandle.getAppId(callingUid); if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) { return false; diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java index b95404febf72..f058ad991e02 100644 --- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java +++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java @@ -48,41 +48,41 @@ import java.util.Set; /** * Responsible for un/installing system packages based on user type. * - * <p>Uses the SystemConfig's install-in-user-type whitelist; + * <p>Uses the SystemConfig's install-in-user-type allowlist; * see {@link SystemConfig#getAndClearPackageToUserTypeWhitelist} and * {@link SystemConfig#getAndClearPackageToUserTypeBlacklist}. * * <p>If {@link #isEnforceMode()} is false, then all system packages are always installed for all * users. The following applies when it is true. * - * <p>Any package can be in one of three states in the {@code SystemConfig} whitelist + * <p>Any package can be in one of three states in the {@code SystemConfig} allowlist * <ol> - * <li>Explicitly blacklisted for a particular user type</li> - * <li>Explicitly whitelisted for a particular user type</li> - * <li>Not mentioned at all, for any user type (neither whitelisted nor blacklisted)</li> + * <li>Explicitly denylisted for a particular user type</li> + * <li>Explicitly allowlisted for a particular user type</li> + * <li>Not mentioned at all, for any user type (neither allowlisted nor denylisted)</li> * </ol> * - * <p>Blacklisting always takes precedence - if a package is blacklisted for a particular user, - * it won't be installed on that type of user (even if it is also whitelisted for that user). - * Next comes whitelisting - if it is whitelisted for a particular user, it will be installed on - * that type of user (as long as it isn't blacklisted). - * Finally, if the package is not mentioned at all (i.e. neither whitelisted nor blacklisted for + * <p>Denylisting always takes precedence - if a package is denylisted for a particular user, + * it won't be installed on that type of user (even if it is also allowlisted for that user). + * Next comes allowlisting - if it is allowlisted for a particular user, it will be installed on + * that type of user (as long as it isn't denylisted). + * Finally, if the package is not mentioned at all (i.e. neither allowlisted nor denylisted for * any user types) in the SystemConfig 'install-in-user-type' lists * then: * <ul> - * <li>If {@link #isImplicitWhitelistMode()}, the package is implicitly treated as whitelisted + * <li>If {@link #isImplicitWhitelistMode()}, the package is implicitly treated as allowlisted * for <b>all</b> users</li> * <li>Otherwise, if {@link #isImplicitWhitelistSystemMode()}, the package is implicitly treated - * as whitelisted for the <b>{@link UserHandle#USER_SYSTEM}</b> user (not other users), + * as allowlisted for the <b>{@link UserHandle#USER_SYSTEM}</b> user (not other users), * which is useful for local development purposes</li> - * <li>Otherwise, the package is implicitly treated as blacklisted for all users</li> + * <li>Otherwise, the package is implicitly treated as denylisted for all users</li> * </ul> * * <p>Packages are only installed/uninstalled by this mechanism when a new user is created or during * an update. In the case of updates:<ul> - * <li>new packages are (un)installed per the whitelist/blacklist</li> - * <li>pre-existing installed blacklisted packages are never uninstalled</li> - * <li>pre-existing not-installed whitelisted packages are only installed if the reason why they + * <li>new packages are (un)installed per the allowlist/denylist</li> + * <li>pre-existing installed denylisted packages are never uninstalled</li> + * <li>pre-existing not-installed allowlisted packages are only installed if the reason why they * had been previously uninstalled was due to UserSystemPackageInstaller</li> * </ul> * @@ -98,14 +98,14 @@ class UserSystemPackageInstaller { private static final boolean DEBUG = false; /** - * System Property whether to only install system packages on a user if they're whitelisted for + * System Property whether to only install system packages on a user if they're allowlisted for * that user type. These are flags and can be freely combined. * <ul> - * <li> 0 - disable whitelist (install all system packages; no logging)</li> - * <li> 1 - enforce (only install system packages if they are whitelisted)</li> - * <li> 2 - log (log non-whitelisted packages)</li> - * <li> 4 - for all users: implicitly whitelist any package not mentioned in the whitelist</li> - * <li> 8 - for SYSTEM: implicitly whitelist any package not mentioned in the whitelist</li> + * <li> 0 - disable allowlist (install all system packages; no logging)</li> + * <li> 1 - enforce (only install system packages if they are allowlisted)</li> + * <li> 2 - log (log non-allowlisted packages)</li> + * <li> 4 - for all users: implicitly allowlist any package not mentioned in the allowlist</li> + * <li> 8 - for SYSTEM: implicitly allowlist any package not mentioned in the allowlist</li> * <li> 16 - ignore OTAs (don't install system packages during OTAs)</li> * <li>-1 - use device default (as defined in res/res/values/config.xml)</li> * </ul> @@ -146,7 +146,7 @@ class UserSystemPackageInstaller { * then mWhitelistedPackagesForUserTypes.get("pkg1") will be a Long whose * bit in position 3 will equal 1. * <p> - * Packages that are whitelisted, but then blacklisted so that they aren't to be installed on + * Packages that are allowlisted, but then denylisted so that they aren't to be installed on * any user, are purposefully still present in this list. */ private final ArrayMap<String, Long> mWhitelistedPackagesForUserTypes; @@ -186,10 +186,10 @@ class UserSystemPackageInstaller { /** * During OTAs and first boot, install/uninstall all system packages for all users based on the - * user's user type and the SystemConfig whitelist. + * user's user type and the SystemConfig allowlist. * We do NOT uninstall packages during an OTA though. * - * This is responsible for enforcing the whitelist for pre-existing users (i.e. USER_SYSTEM); + * This is responsible for enforcing the allowlist for pre-existing users (i.e. USER_SYSTEM); * enforcement for new users is done when they are created in UserManagerService.createUser(). * * @param preExistingPackages list of packages on the device prior to the upgrade. Cannot be @@ -267,7 +267,7 @@ class UserSystemPackageInstaller { } /** - * Checks whether the system packages and the mWhitelistedPackagesForUserTypes whitelist are + * Checks whether the system packages and the mWhitelistedPackagesForUserTypes allowlist are * in 1-to-1 correspondence. */ private void checkWhitelistedSystemPackages(@PackageWhitelistMode int mode) { @@ -276,7 +276,7 @@ class UserSystemPackageInstaller { } Slog.v(TAG, "Checking that all system packages are whitelisted."); - // Check whether all whitelisted packages are indeed on the system. + // Check whether all allowlisted packages are indeed on the system. final List<String> warnings = getPackagesWhitelistWarnings(); final int numberWarnings = warnings.size(); if (numberWarnings == 0) { @@ -290,7 +290,7 @@ class UserSystemPackageInstaller { } } - // Check whether all system packages are indeed whitelisted. + // Check whether all system packages are indeed allowlisted. if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) { return; } @@ -318,7 +318,7 @@ class UserSystemPackageInstaller { } /** - * Gets packages that are listed in the whitelist XML but are not present on the system image. + * Gets packages that are listed in the allowlist XML but are not present on the system image. */ @NonNull private List<String> getPackagesWhitelistWarnings() { @@ -326,7 +326,7 @@ class UserSystemPackageInstaller { final List<String> warnings = new ArrayList<>(); final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class); - // Check whether all whitelisted packages are indeed on the system. + // Check whether all allowlisted packages are indeed on the system. final String notPresentFmt = "%s is whitelisted but not present."; final String notSystemFmt = "%s is whitelisted and present but not a system package."; final String overlayPackageFmt = "%s is whitelisted but it's auto-generated RRO package."; @@ -344,7 +344,7 @@ class UserSystemPackageInstaller { } /** - * Gets packages that are not listed in the whitelist XMLs when they should be. + * Gets packages that are not listed in the allowlist XMLs when they should be. */ @NonNull private List<String> getPackagesWhitelistErrors(@PackageWhitelistMode int mode) { @@ -356,7 +356,7 @@ class UserSystemPackageInstaller { final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages(); final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class); - // Check whether all system packages are indeed whitelisted. + // Check whether all system packages are indeed allowlisted. final String logMessageFmt = "System package %s is not whitelisted using " + "'install-in-user-type' in SystemConfig for any user types!"; pmInt.forEachPackage(pkg -> { @@ -371,7 +371,7 @@ class UserSystemPackageInstaller { return errors; } - /** Whether to only install system packages in new users for which they are whitelisted. */ + /** Whether to only install system packages in new users for which they are allowlisted. */ boolean isEnforceMode() { return isEnforceMode(getWhitelistMode()); } @@ -382,7 +382,7 @@ class UserSystemPackageInstaller { * If in this mode, old system packages will not be installed on pre-existing users during OTAs. * Any system packages that had not been installed at the time of the user's creation, * due to {@link UserSystemPackageInstaller}'s previous actions, will therefore continue to - * remain uninstalled, even if the whitelist (or enforcement mode) now declares that they should + * remain uninstalled, even if the allowlist (or enforcement mode) now declares that they should * be. */ boolean isIgnoreOtaMode() { @@ -390,23 +390,23 @@ class UserSystemPackageInstaller { } /** - * Whether to log a warning concerning potential problems with the user-type package whitelist. + * Whether to log a warning concerning potential problems with the user-type package allowlist. */ boolean isLogMode() { return isLogMode(getWhitelistMode()); } /** - * Whether to treat all packages that are not mentioned at all in the whitelist to be implicitly - * whitelisted for all users. + * Whether to treat all packages that are not mentioned at all in the allowlist to be implicitly + * allowlisted for all users. */ boolean isImplicitWhitelistMode() { return isImplicitWhitelistMode(getWhitelistMode()); } /** - * Whether to treat all packages that are not mentioned at all in the whitelist to be implicitly - * whitelisted for the SYSTEM user. + * Whether to treat all packages that are not mentioned at all in the allowlist to be implicitly + * allowlisted for the SYSTEM user. */ boolean isImplicitWhitelistSystemMode() { return isImplicitWhitelistSystemMode(getWhitelistMode()); @@ -493,7 +493,7 @@ class UserSystemPackageInstaller { /** * Gets the system package names that should be installed on users of the given user type, as - * determined by SystemConfig, the whitelist mode, and the apps actually on the device. + * determined by SystemConfig, the allowlist mode, and the apps actually on the device. * Names are the {@link PackageParser.Package#packageName}, not necessarily the manifest names. * * Returns null if all system packages should be installed (due to enforce-mode being off). @@ -515,7 +515,7 @@ class UserSystemPackageInstaller { } if (shouldInstallPackage(pkg, mWhitelistedPackagesForUserTypes, whitelistedPackages, implicitlyWhitelist)) { - // Although the whitelist uses manifest names, this function returns packageNames. + // Although the allowlist uses manifest names, this function returns packageNames. installPackages.add(pkg.getPackageName()); } }); @@ -524,7 +524,7 @@ class UserSystemPackageInstaller { /** * Returns whether the given system package should be installed on the given user, based on the - * the given whitelist of system packages. + * the given allowlist of system packages. * * @param sysPkg the system package. Must be a system package; no verification for this is done. * @param userTypeWhitelist map of package manifest names to user types on which they should be @@ -534,7 +534,7 @@ class UserSystemPackageInstaller { * <b>particular</b> user. This must be consistent with userTypeWhitelist, * but is passed in separately to avoid repeatedly calculating it from * userTypeWhitelist. - * @param implicitlyWhitelist whether non-mentioned packages are implicitly whitelisted. + * @param implicitlyWhitelist whether non-mentioned packages are implicitly allowlisted. */ @VisibleForTesting static boolean shouldInstallPackage(AndroidPackage sysPkg, @@ -557,7 +557,7 @@ class UserSystemPackageInstaller { } /** - * Gets the package manifest names that are whitelisted for users of the given user type, + * Gets the package manifest names that are allowlisted for users of the given user type, * as determined by SystemConfig. */ @VisibleForTesting @@ -576,9 +576,9 @@ class UserSystemPackageInstaller { /** * Set of package manifest names that are included anywhere in the package-to-user-type - * whitelist, as determined by SystemConfig. + * allowlist, as determined by SystemConfig. * - * Packages that are whitelisted, but then blacklisted so that they aren't to be installed on + * Packages that are allowlisted, but then denylisted so that they aren't to be installed on * any user, are still present in this list, since that is a valid scenario (e.g. if an OEM * completely blacklists an AOSP app). */ @@ -596,9 +596,9 @@ class UserSystemPackageInstaller { * To enforce this: * <ul> * <li>Illegal user types are ignored.</li> - * <li>Packages that never whitelisted at all (even if they are explicitly blacklisted) are + * <li>Packages that never allowlisted at all (even if they are explicitly denylisted) are * ignored.</li> - * <li>Packages that are blacklisted whenever they are whitelisted will be stored with the + * <li>Packages that are denylisted whenever they are allowlisted will be stored with the * value 0 (since this is a valid scenario, e.g. if an OEM completely blacklists an * AOSP app).</li> * </ul> @@ -614,7 +614,7 @@ class UserSystemPackageInstaller { sysConfig.getAndClearPackageToUserTypeWhitelist(); // result maps packageName -> userTypes on which the package should be installed. final ArrayMap<String, Long> result = new ArrayMap<>(whitelist.size() + 1); - // First, do the whitelisted user types. + // First, do the allowlisted user types. for (int i = 0; i < whitelist.size(); i++) { final String pkgName = whitelist.keyAt(i).intern(); final long typesBitSet = getTypesBitSet(whitelist.valueAt(i), baseTypeBitSets); @@ -622,7 +622,7 @@ class UserSystemPackageInstaller { result.put(pkgName, typesBitSet); } } - // Then, un-whitelist any blacklisted user types. + // Then, un-allowlist any denylisted user types. final ArrayMap<String, Set<String>> blacklist = sysConfig.getAndClearPackageToUserTypeBlacklist(); for (int i = 0; i < blacklist.size(); i++) { @@ -632,7 +632,7 @@ class UserSystemPackageInstaller { if (typesBitSet != null) { result.put(pkgName, typesBitSet & ~nonTypesBitSet); } else if (nonTypesBitSet != 0) { - // Package was never whitelisted but is validly blacklisted. + // Package was never allowlisted but is validly denylisted. result.put(pkgName, 0L); } } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 587cb825fbb0..37127233be13 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -185,7 +185,7 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { return; } - // Sanity checks on the arguments. + // Validity checks on the arguments. Objects.requireNonNull(callback); boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE; diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java index fa0183642f94..fc88af921cb3 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java +++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java @@ -85,7 +85,7 @@ public final class DexoptUtils { // The application has splits. Compute their class loader contexts. - // First, cache the relative paths of the splits and do some sanity checks + // First, cache the relative paths of the splits and do some validity checks String[] splitRelativeCodePaths = getSplitRelativeCodePaths(pkg); // The splits have an implicit dependency on the base apk. @@ -407,7 +407,7 @@ public final class DexoptUtils { for (int i = 0; i < splitRelativeCodePaths.length; i++) { File pathFile = new File(splitCodePaths[i]); splitRelativeCodePaths[i] = pathFile.getName(); - // Sanity check that the base paths of the splits are all the same. + // Validity check that the base paths of the splits are all the same. String basePath = pathFile.getParent(); if (!basePath.equals(baseCodePath)) { Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " + diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index d695a01109ff..2d2e72a3facb 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -19,6 +19,7 @@ package com.android.server.pm.parsing; import android.annotation.CheckResult; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.apex.ApexInfo; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -56,6 +57,7 @@ import com.android.internal.util.ArrayUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.PackageStateUnserialized; import libcore.util.EmptyArray; @@ -218,7 +220,9 @@ public class PackageInfoUtils { } ApplicationInfo info = PackageInfoWithoutStateUtils.generateApplicationInfoUnchecked(pkg, - flags, state, userId); + flags, state, userId, false /* assignUserFields */); + + initForUser(info, pkg, userId); if (pkgSetting != null) { // TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up @@ -349,7 +353,11 @@ public class PackageInfoUtils { if (i == null) return null; InstrumentationInfo info = - PackageInfoWithoutStateUtils.generateInstrumentationInfo(i, pkg, flags, userId); + PackageInfoWithoutStateUtils.generateInstrumentationInfo(i, pkg, flags, userId, + false /* assignUserFields */); + + initForUser(info, pkg, userId); + if (info == null) { return null; } @@ -496,6 +504,90 @@ public class PackageInfoUtils { // @formatter:on } + private static void initForUser(ApplicationInfo output, AndroidPackage input, + @UserIdInt int userId) { + PackageImpl pkg = ((PackageImpl) input); + String packageName = input.getPackageName(); + output.uid = UserHandle.getUid(userId, UserHandle.getAppId(input.getUid())); + + if ("android".equals(packageName)) { + output.dataDir = PackageInfoWithoutStateUtils.SYSTEM_DATA_PATH; + return; + } + + // For performance reasons, all these paths are built as strings + if (userId == UserHandle.USER_SYSTEM) { + output.credentialProtectedDataDir = + pkg.getBaseAppDataCredentialProtectedDirForSystemUser() + packageName; + output.deviceProtectedDataDir = + pkg.getBaseAppDataDeviceProtectedDirForSystemUser() + packageName; + } else { + // Convert /data/user/0/ -> /data/user/1/com.example.app + String userIdString = String.valueOf(userId); + int credentialLength = pkg.getBaseAppDataCredentialProtectedDirForSystemUser().length(); + output.credentialProtectedDataDir = + new StringBuilder(pkg.getBaseAppDataCredentialProtectedDirForSystemUser()) + .replace(credentialLength - 2, credentialLength - 1, userIdString) + .append(packageName) + .toString(); + int deviceLength = pkg.getBaseAppDataDeviceProtectedDirForSystemUser().length(); + output.deviceProtectedDataDir = + new StringBuilder(pkg.getBaseAppDataDeviceProtectedDirForSystemUser()) + .replace(deviceLength - 2, deviceLength - 1, userIdString) + .append(packageName) + .toString(); + } + + if (input.isDefaultToDeviceProtectedStorage() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + output.dataDir = output.deviceProtectedDataDir; + } else { + output.dataDir = output.credentialProtectedDataDir; + } + } + + // This duplicates the ApplicationInfo variant because it uses field assignment and the classes + // don't inherit from each other, unfortunately. Consolidating logic would introduce overhead. + private static void initForUser(InstrumentationInfo output, AndroidPackage input, + @UserIdInt int userId) { + PackageImpl pkg = ((PackageImpl) input); + String packageName = input.getPackageName(); + if ("android".equals(packageName)) { + output.dataDir = PackageInfoWithoutStateUtils.SYSTEM_DATA_PATH; + return; + } + + // For performance reasons, all these paths are built as strings + if (userId == UserHandle.USER_SYSTEM) { + output.credentialProtectedDataDir = + pkg.getBaseAppDataCredentialProtectedDirForSystemUser() + packageName; + output.deviceProtectedDataDir = + pkg.getBaseAppDataDeviceProtectedDirForSystemUser() + packageName; + } else { + // Convert /data/user/0/ -> /data/user/1/com.example.app + String userIdString = String.valueOf(userId); + int credentialLength = pkg.getBaseAppDataCredentialProtectedDirForSystemUser().length(); + output.credentialProtectedDataDir = + new StringBuilder(pkg.getBaseAppDataCredentialProtectedDirForSystemUser()) + .replace(credentialLength - 2, credentialLength - 1, userIdString) + .append(packageName) + .toString(); + int deviceLength = pkg.getBaseAppDataDeviceProtectedDirForSystemUser().length(); + output.deviceProtectedDataDir = + new StringBuilder(pkg.getBaseAppDataDeviceProtectedDirForSystemUser()) + .replace(deviceLength - 2, deviceLength - 1, userIdString) + .append(packageName) + .toString(); + } + + if (input.isDefaultToDeviceProtectedStorage() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + output.dataDir = output.deviceProtectedDataDir; + } else { + output.dataDir = output.credentialProtectedDataDir; + } + } + /** * Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache. */ diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java index 0e3e110bdc56..b4c6e9d99751 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java @@ -16,6 +16,7 @@ package com.android.server.pm.parsing.pkg; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo; @@ -28,7 +29,9 @@ import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; import android.content.res.TypedArray; +import android.os.Environment; import android.os.Parcel; +import android.os.UserHandle; import android.os.storage.StorageManager; import android.text.TextUtils; @@ -38,6 +41,7 @@ import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; import com.android.server.pm.parsing.PackageInfoUtils; +import java.io.File; import java.util.UUID; /** @@ -87,8 +91,6 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @DataClass.ParcelWith(ForInternedString.class) private final String manifestPackageName; - private boolean stub; - @Nullable @DataClass.ParcelWith(ForInternedString.class) protected String nativeLibraryDir; @@ -97,8 +99,6 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @DataClass.ParcelWith(ForInternedString.class) protected String nativeLibraryRootDir; - private boolean nativeLibraryRootRequiresIsa; - @Nullable @DataClass.ParcelWith(ForInternedString.class) protected String primaryCpuAbi; @@ -116,44 +116,102 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @DataClass.ParcelWith(ForInternedString.class) protected String seInfoUser; - private boolean coreApp; - - private boolean system; - private boolean factoryTest; + /** + * This is an appId, the uid if the userId is == USER_SYSTEM + */ + private int uid = -1; - private boolean systemExt; - private boolean privileged; - private boolean oem; - private boolean vendor; - private boolean product; - private boolean odm; + // This is kept around as a boolean to avoid flag calculation + // during ApplicationInfo generation. + private boolean nativeLibraryRootRequiresIsa; - private boolean signedWithPlatformKey; + private int mBooleans; /** - * This is an appId, the uid if the userId is == USER_SYSTEM + * @see ParsingPackageImpl.Booleans */ - private int uid = -1; + private static class Booleans { + @IntDef({ + CORE_APP, + SYSTEM, + FACTORY_TEST, + SYSTEM_EXT, + PRIVILEGED, + OEM, + VENDOR, + PRODUCT, + ODM, + SIGNED_WITH_PLATFORM_KEY, + NATIVE_LIBRARY_ROOT_REQUIRES_ISA, + STUB, + }) + public @interface Flags {} + + private static final int CORE_APP = 1; + private static final int SYSTEM = 1 << 1; + private static final int FACTORY_TEST = 1 << 2; + private static final int SYSTEM_EXT = 1 << 3; + private static final int PRIVILEGED = 1 << 4; + private static final int OEM = 1 << 5; + private static final int VENDOR = 1 << 6; + private static final int PRODUCT = 1 << 7; + private static final int ODM = 1 << 8; + private static final int SIGNED_WITH_PLATFORM_KEY = 1 << 9; + private static final int NATIVE_LIBRARY_ROOT_REQUIRES_ISA = 1 << 10; + private static final int STUB = 1 << 11; + } + + private ParsedPackage setBoolean(@Booleans.Flags int flag, boolean value) { + if (value) { + mBooleans |= flag; + } else { + mBooleans &= ~flag; + } + return this; + } + + private boolean getBoolean(@Booleans.Flags int flag) { + return (mBooleans & flag) != 0; + } + + // Derived fields + private int mBaseAppInfoFlags; + private int mBaseAppInfoPrivateFlags; + private String mBaseAppDataCredentialProtectedDirForSystemUser; + private String mBaseAppDataDeviceProtectedDirForSystemUser; @VisibleForTesting public PackageImpl(@NonNull String packageName, @NonNull String baseApkPath, @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp) { super(packageName, baseApkPath, path, manifestArray); this.manifestPackageName = this.packageName; - this.coreApp = isCoreApp; + setBoolean(Booleans.CORE_APP, isCoreApp); } @Override public ParsedPackage hideAsParsed() { + super.hideAsParsed(); return this; } @Override public AndroidPackage hideAsFinal() { // TODO(b/135203078): Lock as immutable + assignDerivedFields(); return this; } + private void assignDerivedFields() { + mBaseAppInfoFlags = PackageInfoUtils.appInfoFlags(this, null); + mBaseAppInfoPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null); + String baseAppDataDir = Environment.getDataDirectoryPath(getVolumeUuid()) + File.separator; + String systemUserSuffix = File.separator + UserHandle.USER_SYSTEM + File.separator; + mBaseAppDataCredentialProtectedDirForSystemUser = TextUtils.safeIntern( + baseAppDataDir + Environment.DIR_USER_CE + systemUserSuffix); + mBaseAppDataDeviceProtectedDirForSystemUser = TextUtils.safeIntern( + baseAppDataDir + Environment.DIR_USER_DE + systemUserSuffix); + } + @Override public long getLongVersionCode() { return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); @@ -437,8 +495,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @Override public ParsedPackage setCoreApp(boolean coreApp) { - this.coreApp = coreApp; - return this; + return setBoolean(Booleans.CORE_APP, coreApp); } @Override @@ -456,8 +513,8 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @Override public ApplicationInfo toAppInfoWithoutState() { ApplicationInfo appInfo = super.toAppInfoWithoutStateWithoutFlags(); - appInfo.flags = PackageInfoUtils.appInfoFlags(this, null); - appInfo.privateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null); + appInfo.flags = mBaseAppInfoFlags; + appInfo.privateFlags = mBaseAppInfoPrivateFlags; appInfo.nativeLibraryDir = nativeLibraryDir; appInfo.nativeLibraryRootDir = nativeLibraryRootDir; appInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; @@ -479,7 +536,6 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); sForInternedString.parcel(this.manifestPackageName, dest, flags); - dest.writeBoolean(this.stub); dest.writeString(this.nativeLibraryDir); dest.writeString(this.nativeLibraryRootDir); dest.writeBoolean(this.nativeLibraryRootRequiresIsa); @@ -489,22 +545,12 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka dest.writeString(this.seInfo); dest.writeString(this.seInfoUser); dest.writeInt(this.uid); - dest.writeBoolean(this.coreApp); - dest.writeBoolean(this.system); - dest.writeBoolean(this.factoryTest); - dest.writeBoolean(this.systemExt); - dest.writeBoolean(this.privileged); - dest.writeBoolean(this.oem); - dest.writeBoolean(this.vendor); - dest.writeBoolean(this.product); - dest.writeBoolean(this.odm); - dest.writeBoolean(this.signedWithPlatformKey); + dest.writeInt(this.mBooleans); } public PackageImpl(Parcel in) { super(in); this.manifestPackageName = sForInternedString.unparcel(in); - this.stub = in.readBoolean(); this.nativeLibraryDir = in.readString(); this.nativeLibraryRootDir = in.readString(); this.nativeLibraryRootRequiresIsa = in.readBoolean(); @@ -514,16 +560,9 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka this.seInfo = in.readString(); this.seInfoUser = in.readString(); this.uid = in.readInt(); - this.coreApp = in.readBoolean(); - this.system = in.readBoolean(); - this.factoryTest = in.readBoolean(); - this.systemExt = in.readBoolean(); - this.privileged = in.readBoolean(); - this.oem = in.readBoolean(); - this.vendor = in.readBoolean(); - this.product = in.readBoolean(); - this.odm = in.readBoolean(); - this.signedWithPlatformKey = in.readBoolean(); + this.mBooleans = in.readInt(); + + assignDerivedFields(); } public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() { @@ -544,9 +583,8 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka return manifestPackageName; } - @DataClass.Generated.Member public boolean isStub() { - return stub; + return getBoolean(Booleans.STUB); } @Nullable @@ -598,52 +636,52 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @Override public boolean isCoreApp() { - return coreApp; + return getBoolean(Booleans.CORE_APP); } @Override public boolean isSystem() { - return system; + return getBoolean(Booleans.SYSTEM); } @Override public boolean isFactoryTest() { - return factoryTest; + return getBoolean(Booleans.FACTORY_TEST); } @Override public boolean isSystemExt() { - return systemExt; + return getBoolean(Booleans.SYSTEM_EXT); } @Override public boolean isPrivileged() { - return privileged; + return getBoolean(Booleans.PRIVILEGED); } @Override public boolean isOem() { - return oem; + return getBoolean(Booleans.OEM); } @Override public boolean isVendor() { - return vendor; + return getBoolean(Booleans.VENDOR); } @Override public boolean isProduct() { - return product; + return getBoolean(Booleans.PRODUCT); } @Override public boolean isOdm() { - return odm; + return getBoolean(Booleans.ODM); } @Override public boolean isSignedWithPlatformKey() { - return signedWithPlatformKey; + return getBoolean(Booleans.SIGNED_WITH_PLATFORM_KEY); } /** @@ -656,7 +694,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @DataClass.Generated.Member public PackageImpl setStub(boolean value) { - stub = value; + setBoolean(Booleans.STUB, value); return this; } @@ -668,77 +706,72 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @Override public PackageImpl setSystem(boolean value) { - system = value; + setBoolean(Booleans.SYSTEM, value); return this; } @Override public PackageImpl setFactoryTest(boolean value) { - factoryTest = value; + setBoolean(Booleans.FACTORY_TEST, value); return this; } @Override public PackageImpl setSystemExt(boolean value) { - systemExt = value; + setBoolean(Booleans.SYSTEM_EXT, value); return this; } @Override public PackageImpl setPrivileged(boolean value) { - privileged = value; + setBoolean(Booleans.PRIVILEGED, value); return this; } @Override public PackageImpl setOem(boolean value) { - oem = value; + setBoolean(Booleans.OEM, value); return this; } @Override public PackageImpl setVendor(boolean value) { - vendor = value; + setBoolean(Booleans.VENDOR, value); return this; } @Override public PackageImpl setProduct(boolean value) { - product = value; + setBoolean(Booleans.PRODUCT, value); return this; } @Override public PackageImpl setOdm(boolean value) { - odm = value; + setBoolean(Booleans.ODM, value); return this; } @Override public PackageImpl setSignedWithPlatformKey(boolean value) { - signedWithPlatformKey = value; + setBoolean(Booleans.SIGNED_WITH_PLATFORM_KEY, value); return this; } - /** - * This is an appId, the uid if the userId is == USER_SYSTEM - */ @Override public PackageImpl setUid(int value) { uid = value; return this; } - @DataClass.Generated( - time = 1580517688900L, - codegenVersion = "1.0.14", - sourceFile = "frameworks/base/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java", - inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String manifestPackageName\nprivate boolean stub\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String nativeLibraryDir\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String nativeLibraryRootDir\nprivate boolean nativeLibraryRootRequiresIsa\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String primaryCpuAbi\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String secondaryCpuAbi\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String secondaryNativeLibraryDir\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String seInfo\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String seInfoUser\nprivate boolean system\nprivate boolean factoryTest\nprivate boolean systemExt\nprivate boolean privileged\nprivate boolean oem\nprivate boolean vendor\nprivate boolean product\nprivate boolean odm\nprivate boolean signedWithPlatformKey\nprivate int uid\npublic static final com.android.server.pm.parsing.pkg.Creator<com.android.server.pm.parsing.pkg.PackageImpl> CREATOR\npublic static com.android.server.pm.parsing.pkg.PackageImpl forParsing(java.lang.String,java.lang.String,java.lang.String,android.content.res.TypedArray,boolean)\npublic static com.android.server.pm.parsing.pkg.AndroidPackage buildFakeForDeletion(java.lang.String,java.lang.String)\npublic static @com.android.internal.annotations.VisibleForTesting android.content.pm.parsing.ParsingPackage forTesting(java.lang.String)\npublic static @com.android.internal.annotations.VisibleForTesting android.content.pm.parsing.ParsingPackage forTesting(java.lang.String,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage hideAsParsed()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.AndroidPackage hideAsFinal()\npublic @java.lang.Override long getLongVersionCode()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removePermission(int)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl addUsesOptionalLibrary(int,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl addUsesLibrary(int,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removeUsesLibrary(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removeUsesOptionalLibrary(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSigningDetails(android.content.pm.PackageParser.SigningDetails)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setRestrictUpdateHash(byte)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setRealPackage(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPersistent(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setDirectBootAware(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearProtectedBroadcasts()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearOriginalPackages()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearAdoptPermissions()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setCodePath(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPackageName(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setAllComponentsDirectBootAware(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setBaseCodePath(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setNativeLibraryDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setNativeLibraryRootDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPrimaryCpuAbi(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSecondaryCpuAbi(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSecondaryNativeLibraryDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSeInfo(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSeInfoUser(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSplitCodePaths(java.lang.String[])\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl capPermissionPriorities()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl markNotActivitiesAsNotExportedIfSingleUser()\npublic @java.lang.Override java.util.UUID getStorageUuid()\npublic @java.lang.Deprecated @java.lang.Override java.lang.String toAppInfoToString()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setCoreApp(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setVersionCode(int)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setVersionCodeMajor(int)\npublic @java.lang.Override android.content.pm.ApplicationInfo toAppInfoWithoutState()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass PackageImpl extends android.content.pm.parsing.ParsingPackageImpl implements [com.android.server.pm.parsing.pkg.ParsedPackage, com.android.server.pm.parsing.pkg.AndroidPackage]\n@com.android.internal.util.DataClass(genConstructor=false, genParcelable=false, genAidl=false, genBuilder=false, genHiddenConstructor=false, genCopyConstructor=false)") - @Deprecated - private void __metadata() {} - - - //@formatter:on - // End of generated code + // The following methods are explicitly not inside any interface. These are hidden under + // PackageImpl which is only accessible to the system server. This is to prevent/discourage + // usage of these fields outside of the utility classes. + public String getBaseAppDataCredentialProtectedDirForSystemUser() { + return mBaseAppDataCredentialProtectedDirForSystemUser; + } + public String getBaseAppDataDeviceProtectedDirForSystemUser() { + return mBaseAppDataDeviceProtectedDirForSystemUser; + } } diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java index 2660f2bda23b..657f32c25a89 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java @@ -105,6 +105,9 @@ public interface ParsedPackage extends AndroidPackage { ParsedPackage setSecondaryNativeLibraryDir(String secondaryNativeLibraryDir); + /** + * This is an appId, the uid if the userId is == USER_SYSTEM + */ ParsedPackage setUid(int uid); ParsedPackage setVersionCode(int versionCode); diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 865b8a1e97eb..962638b4f63c 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -36,7 +36,6 @@ import android.os.UserHandle; import android.util.Log; import android.util.Slog; -import com.android.internal.util.ArrayUtils; import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageSettingBase; @@ -140,10 +139,6 @@ public final class BasePermission { this.perm = perm; } - public boolean hasGids() { - return !ArrayUtils.isEmpty(gids); - } - public int[] computeGids(int userId) { if (perUser) { final int[] userGids = new int[gids.length]; @@ -424,9 +419,9 @@ public final class BasePermission { } public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg, - UidPermissionState uidState) { + PermissionsState permsState) { int index = pkg.getRequestedPermissions().indexOf(name); - if (!uidState.hasRequestedPermission(name) && index == -1) { + if (!permsState.hasRequestedPermission(name) && index == -1) { throw new SecurityException("Package " + pkg.getPackageName() + " has not requested permission " + name); } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index f7721a4d6f22..1ae430a3281a 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1268,10 +1268,10 @@ public final class DefaultPermissionGrantPolicy { continue; } - // Preserve whitelisting flags. + // Preserve allowlisting flags. newFlags |= (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT); - // If we are whitelisting the permission, update the exempt flag before grant. + // If we are allowlisting the permission, update the exempt flag before grant. if (whitelistRestrictedPermissions && pm.isPermissionRestricted(permission)) { pm.updatePermissionFlags(permission, pkg, PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, diff --git a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java deleted file mode 100644 index b9456acfced5..000000000000 --- a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2020 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.pm.permission; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.util.SparseArray; - -import com.android.internal.annotations.GuardedBy; - -/** - * Permission state for this device. - */ -public final class DevicePermissionState { - @GuardedBy("mLock") - @NonNull - private final SparseArray<UserPermissionState> mUserStates = new SparseArray<>(); - - @NonNull - private final Object mLock; - - public DevicePermissionState(@NonNull Object lock) { - mLock = lock; - } - - @Nullable - public UserPermissionState getUserState(@UserIdInt int userId) { - synchronized (mLock) { - return mUserStates.get(userId); - } - } - - @NonNull - public UserPermissionState getOrCreateUserState(@UserIdInt int userId) { - synchronized (mLock) { - UserPermissionState userState = mUserStates.get(userId); - if (userState == null) { - userState = new UserPermissionState(mLock); - mUserStates.put(userId, userState); - } - return userState; - } - } - - public void removeUserState(@UserIdInt int userId) { - synchronized (mLock) { - mUserStates.delete(userId); - } - } - - public int[] getUserIds() { - synchronized (mLock) { - final int userStatesSize = mUserStates.size(); - final int[] userIds = new int[userStatesSize]; - for (int i = 0; i < userStatesSize; i++) { - final int userId = mUserStates.keyAt(i); - userIds[i] = userId; - } - return userIds; - } - } -} diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 75e5944b3cb4..1cfc5b135cfa 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -148,9 +148,12 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal.Default import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; +import com.android.server.pm.permission.PermissionsState.PermissionState; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; +import libcore.util.EmptyArray; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -223,8 +226,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; - @NonNull - private final DevicePermissionState mState; + /** Maps from App ID to PermissionsState */ + private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>(); /** Permission controller: User space permission management */ private PermissionControllerManager mPermissionControllerManager; @@ -392,7 +395,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mUserManagerInt = LocalServices.getService(UserManagerInternal.class); mSettings = new PermissionSettings(mLock); - mState = new DevicePermissionState(mLock); mAppOpsManager = context.getSystemService(AppOpsManager.class); mHandlerThread = new ServiceThread(TAG, @@ -679,12 +681,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { return 0; } - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); return 0; } - return uidState.getPermissionFlags(permName); + return permissionsState.getPermissionFlags(permName, userId); } @Override @@ -786,13 +788,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); return; } - final boolean hadState = uidState.getPermissionState(permName) != null; + final boolean hadState = + permissionsState.getRuntimePermissionState(permName, userId) != null; if (!hadState) { boolean isRequested = false; // Fast path, the current package has requested the permission. @@ -819,18 +822,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } final boolean permissionUpdated = - uidState.updatePermissionFlags(bp, flagMask, flagValues); + permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues); if (permissionUpdated && bp.isRuntime()) { notifyRuntimePermissionStateChanged(packageName, userId); } if (permissionUpdated && callback != null) { // Install and runtime permissions are stored in different places, // so figure out what permission changed and persist the change. - if (!bp.isRuntime()) { + if (permissionsState.getInstallPermissionState(permName) != null) { int userUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); callback.onInstallPermissionUpdatedNotifyListener(userUid); - } else { - callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, pkg.getUid()); + } else if (permissionsState.getRuntimePermissionState(permName, userId) != null + || hadState) { + callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, + pkg.getUid()); } } } @@ -863,14 +868,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean[] changed = new boolean[1]; mPackageManagerInt.forEachPackage(pkg -> { - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - changed[0] |= uidState.updatePermissionFlagsForAllPermissions( - effectiveFlagMask, effectiveFlagValues); + changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions( + userId, effectiveFlagMask, effectiveFlagValues); mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid()); }); @@ -922,20 +926,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final int uid = UserHandle.getUid(userId, pkg.getUid()); - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return PackageManager.PERMISSION_DENIED; } - if (checkSinglePermissionInternal(uid, uidState, permissionName)) { + if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) { return PackageManager.PERMISSION_GRANTED; } final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName); if (fullerPermissionName != null - && checkSinglePermissionInternal(uid, uidState, fullerPermissionName)) { + && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) { return PackageManager.PERMISSION_GRANTED; } @@ -943,8 +946,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean checkSinglePermissionInternal(int uid, - @NonNull UidPermissionState uidState, @NonNull String permissionName) { - if (!uidState.hasPermission(permissionName)) { + @NonNull PermissionsState permissionsState, @NonNull String permissionName) { + if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) { return false; } @@ -1138,9 +1141,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); return null; } @@ -1161,7 +1164,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < permissionCount; i++) { final String permissionName = pkg.getRequestedPermissions().get(i); final int currentFlags = - uidState.getPermissionFlags(permissionName); + permissionsState.getPermissionFlags(permissionName, userId); if ((currentFlags & queryFlags) != 0) { if (whitelistedPermissions == null) { whitelistedPermissions = new ArrayList<>(); @@ -1450,14 +1453,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState); + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1470,7 +1472,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); - final int flags = uidState.getPermissionFlags(permName); + final int flags = permissionsState.getPermissionFlags(permName, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { Log.e(TAG, "Cannot grant system fixed permission " + permName + " for package " + packageName); @@ -1500,9 +1502,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (bp.isDevelopment()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. - // TODO(zhanghai): We are breaking the behavior above by making all permission state - // per-user. It isn't documented behavior and relatively rarely used anyway. - if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) { + if (permissionsState.grantInstallPermission(bp) + != PERMISSION_OPERATION_FAILURE) { if (callback != null) { callback.onInstallPermissionGranted(); } @@ -1520,7 +1521,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } - final int result = uidState.grantPermission(bp); + final int result = permissionsState.grantRuntimePermission(bp, userId); switch (result) { case PERMISSION_OPERATION_FAILURE: { return; @@ -1616,14 +1617,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState); + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1634,7 +1634,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } - final int flags = uidState.getPermissionFlags(permName); + final int flags = permissionsState.getPermissionFlags(permName, userId); // Only the system may revoke SYSTEM_FIXED permissions. if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 && UserHandle.getCallingAppId() != Process.SYSTEM_UID) { @@ -1649,9 +1649,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (bp.isDevelopment()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. - // TODO(zhanghai): We are breaking the behavior above by making all permission state - // per-user. It isn't documented behavior and relatively rarely used anyway. - if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) { + if (permissionsState.revokeInstallPermission(bp) + != PERMISSION_OPERATION_FAILURE) { if (callback != null) { mDefaultPermissionCallback.onInstallPermissionRevoked(); } @@ -1660,11 +1659,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // Permission is already revoked, no need to do anything. - if (!uidState.hasPermission(permName)) { + if (!permissionsState.hasRuntimePermission(permName, userId)) { return; } - if (uidState.revokePermission(bp) == PERMISSION_OPERATION_FAILURE) { + if (permissionsState.revokeRuntimePermission(bp, userId) + == PERMISSION_OPERATION_FAILURE) { return; } @@ -2466,7 +2466,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void onUserRemoved(@UserIdInt int userId) { synchronized (mLock) { - mState.removeUserState(userId); + final int appIdStatesSize = mAppIdStates.size(); + for (int i = 0; i < appIdStatesSize; i++) { + PermissionsState permissionsState = mAppIdStates.valueAt(i); + for (PermissionState permissionState + : permissionsState.getRuntimePermissionStates(userId)) { + BasePermission bp = mSettings.getPermission(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + } + } + } } } @@ -2477,18 +2489,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return Collections.emptySet(); } - final UidPermissionState uidState = getUidState(ps, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); + final PermissionsState permissionsState = getPermissionsState(ps); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); return Collections.emptySet(); } if (!ps.getInstantApp(userId)) { - return uidState.getPermissions(); + return permissionsState.getPermissions(userId); } else { // Install permission state is shared among all users, but instant app state is // per-user, so we can only filter it here unless we make install permission state // per-user as well. - final Set<String> instantPermissions = new ArraySet<>(uidState.getPermissions()); + final Set<String> instantPermissions = new ArraySet<>(permissionsState.getPermissions( + userId)); instantPermissions.removeIf(permissionName -> { BasePermission permission = mSettings.getPermission(permissionName); if (permission == null) { @@ -2520,12 +2533,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return null; } - final UidPermissionState uidState = getUidState(ps, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); + final PermissionsState permissionsState = getPermissionsState(ps); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); return null; } - return uidState.computeGids(userId); + return permissionsState.computeGids(userId); } /** @@ -2562,17 +2575,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return; } + final PermissionsState permissionsState = getOrCreatePermissionsState(ps); final int[] userIds = getAllUserIds(); boolean runtimePermissionsRevoked = false; int[] updatedUserIds = EMPTY_INT_ARRAY; - for (final int userId : userIds) { - final UserPermissionState userState = mState.getOrCreateUserState(userId); - final UidPermissionState uidState = userState.getOrCreateUidState(ps.getAppId()); - - if (uidState.isMissing()) { + for (int userId : userIds) { + if (permissionsState.isMissing(userId)) { Collection<String> requestedPermissions; int targetSdkVersion; if (!ps.isSharedUser()) { @@ -2600,220 +2611,222 @@ public class PermissionManagerService extends IPermissionManager.Stub { && permission.isRuntime() && !permission.isRemoved()) { if (permission.isHardOrSoftRestricted() || permission.isImmutablyRestricted()) { - uidState.updatePermissionFlags(permission, + permissionsState.updatePermissionFlags(permission, userId, FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT); } if (targetSdkVersion < Build.VERSION_CODES.M) { - uidState.updatePermissionFlags(permission, + permissionsState.updatePermissionFlags(permission, userId, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT); - uidState.grantPermission(permission); + permissionsState.grantRuntimePermission(permission, userId); } } } - uidState.setMissing(false); + permissionsState.setMissing(false, userId); updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } + } - UidPermissionState origState = uidState; + PermissionsState origPermissions = permissionsState; - boolean changedInstallPermission = false; + boolean changedInstallPermission = false; - if (replace) { - userState.setInstallPermissionsFixed(ps.name, false); - if (!ps.isSharedUser()) { - origState = new UidPermissionState(uidState); - uidState.reset(); - } else { - // We need to know only about runtime permission changes since the - // calling code always writes the install permissions state but - // the runtime ones are written only if changed. The only cases of - // changed runtime permissions here are promotion of an install to - // runtime and revocation of a runtime from a shared user. - synchronized (mLock) { - if (revokeUnusedSharedUserPermissionsLocked( - ps.getSharedUser().getPackages(), uidState)) { - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - runtimePermissionsRevoked = true; - } + if (replace) { + ps.setInstallPermissionsFixed(false); + if (!ps.isSharedUser()) { + origPermissions = new PermissionsState(permissionsState); + permissionsState.reset(); + } else { + // We need to know only about runtime permission changes since the + // calling code always writes the install permissions state but + // the runtime ones are written only if changed. The only cases of + // changed runtime permissions here are promotion of an install to + // runtime and revocation of a runtime from a shared user. + synchronized (mLock) { + updatedUserIds = revokeUnusedSharedUserPermissionsLocked( + ps.getSharedUser().getPackages(), permissionsState, userIds); + if (!ArrayUtils.isEmpty(updatedUserIds)) { + runtimePermissionsRevoked = true; } } } + } - uidState.setGlobalGids(mGlobalGids); + permissionsState.setGlobalGids(mGlobalGids); - ArraySet<String> newImplicitPermissions = new ArraySet<>(); + ArraySet<String> newImplicitPermissions = new ArraySet<>(); - final int N = pkg.getRequestedPermissions().size(); - for (int i = 0; i < N; i++) { - final String permName = pkg.getRequestedPermissions().get(i); - final BasePermission bp = mSettings.getPermission(permName); - final boolean appSupportsRuntimePermissions = - pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; - String upgradedActivityRecognitionPermission = null; + final int N = pkg.getRequestedPermissions().size(); + for (int i = 0; i < N; i++) { + final String permName = pkg.getRequestedPermissions().get(i); + final BasePermission bp = mSettings.getPermission(permName); + final boolean appSupportsRuntimePermissions = + pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; + String upgradedActivityRecognitionPermission = null; - if (DEBUG_INSTALL && bp != null) { - Log.i(TAG, "Package " + pkg.getPackageName() - + " checking " + permName + ": " + bp); - } + if (DEBUG_INSTALL && bp != null) { + Log.i(TAG, "Package " + pkg.getPackageName() + + " checking " + permName + ": " + bp); + } - if (bp == null || getSourcePackageSetting(bp) == null) { - if (packageOfInterest == null || packageOfInterest.equals( - pkg.getPackageName())) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Unknown permission " + permName - + " in package " + pkg.getPackageName()); - } + if (bp == null || getSourcePackageSetting(bp) == null) { + if (packageOfInterest == null || packageOfInterest.equals( + pkg.getPackageName())) { + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Unknown permission " + permName + + " in package " + pkg.getPackageName()); } - continue; } + continue; + } - // Cache newImplicitPermissions before modifing permissionsState as for the shared - // uids the original and new state are the same object - if (!origState.hasRequestedPermission(permName) - && (pkg.getImplicitPermissions().contains(permName) - || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) { - if (pkg.getImplicitPermissions().contains(permName)) { - // If permName is an implicit permission, try to auto-grant - newImplicitPermissions.add(permName); + // Cache newImplicitPermissions before modifing permissionsState as for the shared + // uids the original and new state are the same object + if (!origPermissions.hasRequestedPermission(permName) + && (pkg.getImplicitPermissions().contains(permName) + || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) { + if (pkg.getImplicitPermissions().contains(permName)) { + // If permName is an implicit permission, try to auto-grant + newImplicitPermissions.add(permName); - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName()); - } - } else { - // Special case for Activity Recognition permission. Even if AR permission - // is not an implicit permission we want to add it to the list (try to - // auto-grant it) if the app was installed on a device before AR permission - // was split, regardless of if the app now requests the new AR permission - // or has updated its target SDK and AR is no longer implicit to it. - // This is a compatibility workaround for apps when AR permission was - // split in Q. - final List<SplitPermissionInfoParcelable> permissionList = - getSplitPermissions(); - int numSplitPerms = permissionList.size(); - for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { - SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum); - String splitPermName = sp.getSplitPermission(); - if (sp.getNewPermissions().contains(permName) - && origState.hasInstallPermission(splitPermName)) { - upgradedActivityRecognitionPermission = splitPermName; - newImplicitPermissions.add(permName); + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName()); + } + } else { + // Special case for Activity Recognition permission. Even if AR permission + // is not an implicit permission we want to add it to the list (try to + // auto-grant it) if the app was installed on a device before AR permission + // was split, regardless of if the app now requests the new AR permission + // or has updated its target SDK and AR is no longer implicit to it. + // This is a compatibility workaround for apps when AR permission was + // split in Q. + final List<SplitPermissionInfoParcelable> permissionList = + getSplitPermissions(); + int numSplitPerms = permissionList.size(); + for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { + SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum); + String splitPermName = sp.getSplitPermission(); + if (sp.getNewPermissions().contains(permName) + && origPermissions.hasInstallPermission(splitPermName)) { + upgradedActivityRecognitionPermission = splitPermName; + newImplicitPermissions.add(permName); - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " - + pkg.getPackageName()); - } - break; + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, permName + " is newly added for " + + pkg.getPackageName()); } + break; } } } + } - // TODO(b/140256621): The package instant app method has been removed - // as part of work in b/135203078, so this has been commented out in the meantime - // Limit ephemeral apps to ephemeral allowed permissions. - // if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) { - // if (DEBUG_PERMISSIONS) { - // Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() - // + " for package " + pkg.getPackageName()); - // } - // continue; - // } - - if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) { - if (DEBUG_PERMISSIONS) { - Log.i(TAG, "Denying runtime-only permission " + bp.getName() - + " for package " + pkg.getPackageName()); - } - continue; + // TODO(b/140256621): The package instant app method has been removed + // as part of work in b/135203078, so this has been commented out in the meantime + // Limit ephemeral apps to ephemeral allowed permissions. +// if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) { +// if (DEBUG_PERMISSIONS) { +// Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() +// + " for package " + pkg.getPackageName()); +// } +// continue; +// } + + if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) { + if (DEBUG_PERMISSIONS) { + Log.i(TAG, "Denying runtime-only permission " + bp.getName() + + " for package " + pkg.getPackageName()); } + continue; + } - final String perm = bp.getName(); - boolean allowedSig = false; - int grant = GRANT_DENIED; + final String perm = bp.getName(); + boolean allowedSig = false; + int grant = GRANT_DENIED; - // Keep track of app op permissions. - if (bp.isAppOp()) { - mSettings.addAppOpPackage(perm, pkg.getPackageName()); - } + // Keep track of app op permissions. + if (bp.isAppOp()) { + mSettings.addAppOpPackage(perm, pkg.getPackageName()); + } - if (bp.isNormal()) { - // For all apps normal permissions are install time ones. + if (bp.isNormal()) { + // For all apps normal permissions are install time ones. + grant = GRANT_INSTALL; + } else if (bp.isRuntime()) { + if (origPermissions.hasInstallPermission(bp.getName()) + || upgradedActivityRecognitionPermission != null) { + // Before Q we represented some runtime permissions as install permissions, + // in Q we cannot do this anymore. Hence upgrade them all. + grant = GRANT_UPGRADE; + } else { + // For modern apps keep runtime permissions unchanged. + grant = GRANT_RUNTIME; + } + } else if (bp.isSignature()) { + // For all apps signature permissions are install time ones. + allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origPermissions); + if (allowedSig) { grant = GRANT_INSTALL; - } else if (bp.isRuntime()) { - if (origState.hasInstallPermission(bp.getName()) - || upgradedActivityRecognitionPermission != null) { - // Before Q we represented some runtime permissions as install permissions, - // in Q we cannot do this anymore. Hence upgrade them all. - grant = GRANT_UPGRADE; - } else { - // For modern apps keep runtime permissions unchanged. - grant = GRANT_RUNTIME; - } - } else if (bp.isSignature()) { - // For all apps signature permissions are install time ones. - allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origState); - if (allowedSig) { - grant = GRANT_INSTALL; - } } + } - if (grant != GRANT_DENIED) { - if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name) - && !bp.isRuntime()) { - // If this is an existing, non-system package, then - // we can't add any new permissions to it. Runtime - // permissions can be added any time - they ad dynamic. - if (!allowedSig && !origState.hasInstallPermission(perm)) { - // Except... if this is a permission that was added - // to the platform (note: need to only do this when - // updating the platform). - if (!isNewPlatformPermissionForPackage(perm, pkg)) { - grant = GRANT_DENIED; - } + if (grant != GRANT_DENIED) { + if (!ps.isSystem() && ps.areInstallPermissionsFixed() && !bp.isRuntime()) { + // If this is an existing, non-system package, then + // we can't add any new permissions to it. Runtime + // permissions can be added any time - they ad dynamic. + if (!allowedSig && !origPermissions.hasInstallPermission(perm)) { + // Except... if this is a permission that was added + // to the platform (note: need to only do this when + // updating the platform). + if (!isNewPlatformPermissionForPackage(perm, pkg)) { + grant = GRANT_DENIED; } } } + } - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Considering granting permission " + perm + " to package " - + pkg.getPackageName()); - } + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Considering granting permission " + perm + " to package " + + pkg.getPackageName()); + } - synchronized (mLock) { - if (grant != GRANT_DENIED) { - switch (grant) { - case GRANT_INSTALL: { - // Revoke this as runtime permission to handle the case of - // a runtime permission being downgraded to an install one. - // Also in permission review mode we keep dangerous permissions - // for legacy apps - final PermissionState origPermissionState = - origState.getPermissionState(perm); - if (origPermissionState != null - && origPermissionState.isRuntime()) { + synchronized (mLock) { + if (grant != GRANT_DENIED) { + switch (grant) { + case GRANT_INSTALL: { + // Revoke this as runtime permission to handle the case of + // a runtime permission being downgraded to an install one. + // Also in permission review mode we keep dangerous permissions + // for legacy apps + for (int userId : userIds) { + if (origPermissions.getRuntimePermissionState( + perm, userId) != null) { // Revoke the runtime permission and clear the flags. - origState.revokePermission(bp); - origState.updatePermissionFlags(bp, + origPermissions.revokeRuntimePermission(bp, userId); + origPermissions.updatePermissionFlags(bp, userId, PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); // If we revoked a permission permission, we have to write. updatedUserIds = ArrayUtils.appendInt( updatedUserIds, userId); } - // Grant an install permission. - if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) { - changedInstallPermission = true; - } - } break; + } + // Grant an install permission. + if (permissionsState.grantInstallPermission(bp) != + PERMISSION_OPERATION_FAILURE) { + changedInstallPermission = true; + } + } break; - case GRANT_RUNTIME: { - boolean hardRestricted = bp.isHardRestricted(); - boolean softRestricted = bp.isSoftRestricted(); + case GRANT_RUNTIME: { + boolean hardRestricted = bp.isHardRestricted(); + boolean softRestricted = bp.isSoftRestricted(); + for (int userId : userIds) { // If permission policy is not ready we don't deal with restricted // permissions as the policy may whitelist some permissions. Once // the policy is initialized we would re-evaluate permissions. @@ -2821,24 +2834,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { mPermissionPolicyInternal != null && mPermissionPolicyInternal.isInitialized(userId); - PermissionState origPermState = origState.getPermissionState(perm); - int flags = origPermState != null ? origPermState.getFlags() : 0; + PermissionState permState = origPermissions + .getRuntimePermissionState(perm, userId); + int flags = permState != null ? permState.getFlags() : 0; boolean wasChanged = false; boolean restrictionExempt = - (origState.getPermissionFlags(bp.name) + (origPermissions.getPermissionFlags(bp.name, userId) & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; - boolean restrictionApplied = (origState.getPermissionFlags( - bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + boolean restrictionApplied = (origPermissions.getPermissionFlags( + bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; if (appSupportsRuntimePermissions) { // If hard restricted we don't allow holding it if (permissionPolicyInitialized && hardRestricted) { if (!restrictionExempt) { - if (origPermState != null && origPermState.isGranted() - && uidState.revokePermission( - bp) != PERMISSION_OPERATION_FAILURE) { + if (permState != null && permState.isGranted() + && permissionsState.revokeRuntimePermission( + bp, userId) != PERMISSION_OPERATION_FAILURE) { wasChanged = true; } if (!restrictionApplied) { @@ -2868,15 +2882,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Hard restricted permissions cannot be held. } else if (!permissionPolicyInitialized || (!hardRestricted || restrictionExempt)) { - if (origPermState != null && origPermState.isGranted()) { - if (uidState.grantPermission(bp) + if (permState != null && permState.isGranted()) { + if (permissionsState.grantRuntimePermission(bp, userId) == PERMISSION_OPERATION_FAILURE) { wasChanged = true; } } } } else { - if (origPermState == null) { + if (permState == null) { // New permission if (PLATFORM_PACKAGE_NAME.equals( bp.getSourcePackageName())) { @@ -2888,8 +2902,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - if (!uidState.hasPermission(bp.name) - && uidState.grantPermission(bp) + if (!permissionsState.hasRuntimePermission(bp.name, userId) + && permissionsState.grantRuntimePermission(bp, userId) != PERMISSION_OPERATION_FAILURE) { wasChanged = true; } @@ -2922,32 +2936,36 @@ public class PermissionManagerService extends IPermissionManager.Stub { updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } - uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, - flags); - } break; - - case GRANT_UPGRADE: { - // Upgrade from Pre-Q to Q permission model. Make all permissions - // runtime - PermissionState origPermState = origState.getPermissionState(perm); - int flags = (origPermState != null) ? origPermState.getFlags() : 0; - - BasePermission bpToRevoke = - upgradedActivityRecognitionPermission == null - ? bp : mSettings.getPermissionLocked( - upgradedActivityRecognitionPermission); - // Remove install permission - if (origState.revokePermission(bpToRevoke) - != PERMISSION_OPERATION_FAILURE) { - origState.updatePermissionFlags(bpToRevoke, - (MASK_PERMISSION_FLAGS_ALL - & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0); - changedInstallPermission = true; - } + permissionsState.updatePermissionFlags(bp, userId, + MASK_PERMISSION_FLAGS_ALL, flags); + } + } break; + + case GRANT_UPGRADE: { + // Upgrade from Pre-Q to Q permission model. Make all permissions + // runtime + PermissionState permState = origPermissions + .getInstallPermissionState(perm); + int flags = (permState != null) ? permState.getFlags() : 0; + + BasePermission bpToRevoke = + upgradedActivityRecognitionPermission == null + ? bp : mSettings.getPermissionLocked( + upgradedActivityRecognitionPermission); + // Remove install permission + if (origPermissions.revokeInstallPermission(bpToRevoke) + != PERMISSION_OPERATION_FAILURE) { + origPermissions.updatePermissionFlags(bpToRevoke, + UserHandle.USER_ALL, + (MASK_PERMISSION_FLAGS_ALL + & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0); + changedInstallPermission = true; + } - boolean hardRestricted = bp.isHardRestricted(); - boolean softRestricted = bp.isSoftRestricted(); + boolean hardRestricted = bp.isHardRestricted(); + boolean softRestricted = bp.isSoftRestricted(); + for (int userId : userIds) { // If permission policy is not ready we don't deal with restricted // permissions as the policy may whitelist some permissions. Once // the policy is initialized we would re-evaluate permissions. @@ -2958,18 +2976,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean wasChanged = false; boolean restrictionExempt = - (origState.getPermissionFlags(bp.name) + (origPermissions.getPermissionFlags(bp.name, userId) & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; - boolean restrictionApplied = (origState.getPermissionFlags( - bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + boolean restrictionApplied = (origPermissions.getPermissionFlags( + bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; if (appSupportsRuntimePermissions) { // If hard restricted we don't allow holding it if (permissionPolicyInitialized && hardRestricted) { if (!restrictionExempt) { - if (origPermState != null && origPermState.isGranted() - && uidState.revokePermission( - bp) != PERMISSION_OPERATION_FAILURE) { + if (permState != null && permState.isGranted() + && permissionsState.revokeRuntimePermission( + bp, userId) != PERMISSION_OPERATION_FAILURE) { wasChanged = true; } if (!restrictionApplied) { @@ -2999,15 +3017,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Hard restricted permissions cannot be held. } else if (!permissionPolicyInitialized || (!hardRestricted || restrictionExempt)) { - if (uidState.grantPermission(bp) - != PERMISSION_OPERATION_FAILURE) { + if (permissionsState.grantRuntimePermission(bp, userId) != + PERMISSION_OPERATION_FAILURE) { wasChanged = true; } } } else { - if (!uidState.hasPermission(bp.name) - && uidState.grantPermission(bp) - != PERMISSION_OPERATION_FAILURE) { + if (!permissionsState.hasRuntimePermission(bp.name, userId) + && permissionsState.grantRuntimePermission(bp, + userId) != PERMISSION_OPERATION_FAILURE) { flags |= FLAG_PERMISSION_REVIEW_REQUIRED; wasChanged = true; } @@ -3040,74 +3058,71 @@ public class PermissionManagerService extends IPermissionManager.Stub { updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } - uidState.updatePermissionFlags(bp, + permissionsState.updatePermissionFlags(bp, userId, MASK_PERMISSION_FLAGS_ALL, flags); - } break; + } + } break; - default: { - if (packageOfInterest == null - || packageOfInterest.equals(pkg.getPackageName())) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Not granting permission " + perm - + " to package " + pkg.getPackageName() - + " because it was previously installed without"); - } + default: { + if (packageOfInterest == null + || packageOfInterest.equals(pkg.getPackageName())) { + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Not granting permission " + perm + + " to package " + pkg.getPackageName() + + " because it was previously installed without"); } - } break; - } - } else { - if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) { - // Also drop the permission flags. - uidState.updatePermissionFlags(bp, - MASK_PERMISSION_FLAGS_ALL, 0); - changedInstallPermission = true; - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Un-granting permission " + perm - + " from package " + pkg.getPackageName() - + " (protectionLevel=" + bp.getProtectionLevel() - + " flags=0x" - + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, - ps)) - + ")"); - } - } else if (bp.isAppOp()) { - // Don't print warning for app op permissions, since it is fine for them - // not to be granted, there is a UI for the user to decide. - if (DEBUG_PERMISSIONS - && (packageOfInterest == null - || packageOfInterest.equals(pkg.getPackageName()))) { - Slog.i(TAG, "Not granting permission " + perm - + " to package " + pkg.getPackageName() - + " (protectionLevel=" + bp.getProtectionLevel() - + " flags=0x" - + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, - ps)) - + ")"); } + } break; + } + } else { + if (permissionsState.revokeInstallPermission(bp) != + PERMISSION_OPERATION_FAILURE) { + // Also drop the permission flags. + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + MASK_PERMISSION_FLAGS_ALL, 0); + changedInstallPermission = true; + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Un-granting permission " + perm + + " from package " + pkg.getPackageName() + + " (protectionLevel=" + bp.getProtectionLevel() + + " flags=0x" + + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps)) + + ")"); + } + } else if (bp.isAppOp()) { + // Don't print warning for app op permissions, since it is fine for them + // not to be granted, there is a UI for the user to decide. + if (DEBUG_PERMISSIONS + && (packageOfInterest == null + || packageOfInterest.equals(pkg.getPackageName()))) { + Slog.i(TAG, "Not granting permission " + perm + + " to package " + pkg.getPackageName() + + " (protectionLevel=" + bp.getProtectionLevel() + + " flags=0x" + + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps)) + + ")"); } } } } + } - if ((changedInstallPermission || replace) - && !userState.areInstallPermissionsFixed(ps.name) - && !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) { - // This is the first that we have heard about this package, so the - // permissions we have now selected are fixed until explicitly - // changed. - userState.setInstallPermissionsFixed(ps.name, true); - } - - synchronized (mLock) { - updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState, pkg, - userId, updatedUserIds); - updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origState, - uidState, pkg, newImplicitPermissions, userId, updatedUserIds); - } + if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() && + !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) { + // This is the first that we have heard about this package, so the + // permissions we have now selected are fixed until explicitly + // changed. + ps.setInstallPermissionsFixed(true); } - updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds, - updatedUserIds); + synchronized (mLock) { + updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg, + userIds, updatedUserIds); + updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions, + permissionsState, pkg, newImplicitPermissions, userIds, updatedUserIds); + updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds, + updatedUserIds); + } // TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important // for shared users. @@ -3144,38 +3159,40 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @return The updated value of the {@code updatedUserIds} parameter */ - private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull UidPermissionState ps, - @NonNull AndroidPackage pkg, int userId, @NonNull int[] updatedUserIds) { + private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull PermissionsState ps, + @NonNull AndroidPackage pkg, @NonNull int[] userIds, @NonNull int[] updatedUserIds) { String pkgName = pkg.getPackageName(); boolean supportsRuntimePermissions = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; - for (String permission : ps.getPermissions()) { - if (!pkg.getImplicitPermissions().contains(permission)) { - if (!ps.hasInstallPermission(permission)) { - int flags = ps.getPermissionFlags(permission); + for (int userId : userIds) { + for (String permission : ps.getPermissions(userId)) { + if (!pkg.getImplicitPermissions().contains(permission)) { + if (!ps.hasInstallPermission(permission)) { + int flags = ps.getRuntimePermissionState(permission, userId).getFlags(); - if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { - BasePermission bp = mSettings.getPermissionLocked(permission); + if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { + BasePermission bp = mSettings.getPermissionLocked(permission); - int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; + int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; - if ((flags & BLOCKING_PERMISSION_FLAGS) == 0 - && supportsRuntimePermissions) { - int revokeResult = ps.revokePermission(bp); - if (revokeResult != PERMISSION_OPERATION_FAILURE) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking runtime permission " - + permission + " for " + pkgName - + " as it is now requested"); + if ((flags & BLOCKING_PERMISSION_FLAGS) == 0 + && supportsRuntimePermissions) { + int revokeResult = ps.revokeRuntimePermission(bp, userId); + if (revokeResult != PERMISSION_OPERATION_FAILURE) { + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Revoking runtime permission " + + permission + " for " + pkgName + + " as it is now requested"); + } } + + flagsToRemove |= USER_PERMISSION_FLAGS; } - flagsToRemove |= USER_PERMISSION_FLAGS; + ps.updatePermissionFlags(bp, userId, flagsToRemove, 0); + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } - - ps.updatePermissionFlags(bp, flagsToRemove, 0); - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } } } @@ -3196,10 +3213,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @param newPerm The permission to inherit to * @param ps The permission state of the package * @param pkg The package requesting the permissions + * @param userId The user the permission belongs to */ private void inheritPermissionStateToNewImplicitPermissionLocked( @NonNull ArraySet<String> sourcePerms, @NonNull String newPerm, - @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg) { + @NonNull PermissionsState ps, @NonNull AndroidPackage pkg, + @UserIdInt int userId) { String pkgName = pkg.getPackageName(); boolean isGranted = false; int flags = 0; @@ -3207,16 +3226,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { int numSourcePerm = sourcePerms.size(); for (int i = 0; i < numSourcePerm; i++) { String sourcePerm = sourcePerms.valueAt(i); - if (ps.hasPermission(sourcePerm)) { + if ((ps.hasRuntimePermission(sourcePerm, userId)) + || ps.hasInstallPermission(sourcePerm)) { if (!isGranted) { flags = 0; } isGranted = true; - flags |= ps.getPermissionFlags(sourcePerm); + flags |= ps.getPermissionFlags(sourcePerm, userId); } else { if (!isGranted) { - flags |= ps.getPermissionFlags(sourcePerm); + flags |= ps.getPermissionFlags(sourcePerm, userId); } } } @@ -3227,11 +3247,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { + " for " + pkgName); } - ps.grantPermission(mSettings.getPermissionLocked(newPerm)); + ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId); } // Add permission flags - ps.updatePermissionFlags(mSettings.getPermission(newPerm), flags, flags); + ps.updatePermissionFlags(mSettings.getPermission(newPerm), userId, flags, flags); } /** @@ -3263,15 +3283,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @param origPs The permission state of the package before the split * @param ps The new permission state * @param pkg The package the permission belongs to - * @param userId The user ID + * @param userIds All user IDs in the system, must be passed in because this method is locked * @param updatedUserIds List of users for which the permission state has already been changed * * @return List of users for which the permission state has been changed */ private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked( - @NonNull UidPermissionState origPs, @NonNull UidPermissionState ps, + @NonNull PermissionsState origPs, @NonNull PermissionsState ps, @NonNull AndroidPackage pkg, @NonNull ArraySet<String> newImplicitPermissions, - @UserIdInt int userId, @NonNull int[] updatedUserIds) { + @NonNull int[] userIds, @NonNull int[] updatedUserIds) { String pkgName = pkg.getPackageName(); ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>(); @@ -3305,33 +3325,35 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (!ps.hasInstallPermission(newPerm)) { BasePermission bp = mSettings.getPermissionLocked(newPerm); - if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) { - ps.updatePermissionFlags(bp, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); - } - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + for (int userId : userIds) { + if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) { + ps.updatePermissionFlags(bp, userId, + FLAG_PERMISSION_REVOKE_WHEN_REQUESTED, + FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); + } + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - boolean inheritsFromInstallPerm = false; - for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size(); - sourcePermNum++) { - if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) { - inheritsFromInstallPerm = true; - break; + boolean inheritsFromInstallPerm = false; + for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size(); + sourcePermNum++) { + if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) { + inheritsFromInstallPerm = true; + break; + } } - } - if (!origPs.hasRequestedPermission(sourcePerms) - && !inheritsFromInstallPerm) { - // Both permissions are new so nothing to inherit. - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms - + " for " + pkgName + " as split permission is also new"); + if (!origPs.hasRequestedPermission(sourcePerms) + && !inheritsFromInstallPerm) { + // Both permissions are new so nothing to inherit. + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms + + " for " + pkgName + " as split permission is also new"); + } + } else { + // Inherit from new install or existing runtime permissions + inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, + newPerm, ps, pkg, userId); } - } else { - // Inherit from new install or existing runtime permissions - inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, - newPerm, ps, pkg); } } } @@ -3462,7 +3484,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean shouldGrantSignaturePermission(String perm, AndroidPackage pkg, - PackageSetting pkgSetting, BasePermission bp, UidPermissionState origPermissions) { + PackageSetting pkgSetting, BasePermission bp, PermissionsState origPermissions) { boolean oemPermission = bp.isOEM(); boolean vendorPrivilegedPermission = bp.isVendorPrivileged(); boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged(); @@ -3738,13 +3760,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // Legacy apps have the permission and get user consent on launch. - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return false; } - return uidState.isPermissionReviewRequired(); + return permissionsState.isPermissionReviewRequired(userId); } private boolean isPackageRequestingPermission(AndroidPackage pkg, String permission) { @@ -3768,10 +3789,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId, String[] grantedPermissions, int callingUid, PermissionCallback callback) { - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } @@ -3796,7 +3816,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { && (supportsRuntimePermissions || !bp.isRuntimeOnly()) && (grantedPermissions == null || ArrayUtils.contains(grantedPermissions, permission))) { - final int flags = uidState.getPermissionFlags(permission); + final int flags = permissionsState.getPermissionFlags(permission, userId); if (supportsRuntimePermissions) { // Installer cannot change immutable permissions. if ((flags & immutableFlags) == 0) { @@ -3818,19 +3838,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg, @UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid, @PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + return; + } + SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>(); boolean updatePermissions = false; final int permissionCount = pkg.getRequestedPermissions().size(); for (int i = 0; i < userIds.length; i++) { int userId = userIds[i]; - final UidPermissionState uidState = getUidState(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); - continue; - } - for (int j = 0; j < permissionCount; j++) { final String permissionName = pkg.getRequestedPermissions().get(j); @@ -3840,14 +3859,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } - if (uidState.hasPermission(permissionName)) { + if (permissionsState.hasPermission(permissionName, userId)) { if (oldGrantedRestrictedPermissions.get(userId) == null) { oldGrantedRestrictedPermissions.put(userId, new ArraySet<>()); } oldGrantedRestrictedPermissions.get(userId).add(permissionName); } - final int oldFlags = uidState.getPermissionFlags(permissionName); + final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId); int newFlags = oldFlags; int mask = 0; @@ -3902,7 +3921,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { // as whitelisting trumps policy i.e. policy cannot grant a non // grantable permission. if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - final boolean isGranted = uidState.hasPermission(permissionName); + final boolean isGranted = permissionsState.hasPermission(permissionName, + userId); if (!isWhitelisted && isGranted) { mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -3938,13 +3958,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int j = 0; j < oldGrantedCount; j++) { final String permission = oldPermsForUser.valueAt(j); // Sometimes we create a new permission state instance during update. - final UidPermissionState newUidState = getUidState(pkg, userId); - if (newUidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() - + " and user " + userId); + final PermissionsState newPermissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); continue; } - if (!newUidState.hasPermission(permission)) { + if (!newPermissionsState.hasPermission(permission, userId)) { callback.onPermissionRevoked(pkg.getUid(), userId, null); break; } @@ -3993,10 +4012,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } - UidPermissionState uidState = getUidState(deletedPs.pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName() - + " and user " + userId); + PermissionsState permissionsState = getPermissionsState(deletedPs.pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()); continue; } @@ -4018,15 +4036,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + // Try to revoke as an install permission which is for all users. // The package is gone - no need to keep flags for applying policy. - uidState.updatePermissionFlags(bp, PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + + if (permissionsState.revokeInstallPermission(bp) + == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + affectedUserId = UserHandle.USER_ALL; + } // Try to revoke as a runtime permission which is per user. - // TODO(zhanghai): This doesn't make sense. revokePermission() doesn't fail, and why are - // we only killing the uid when gids changed, instead of any permission change? - if (uidState.revokePermission(bp) + if (permissionsState.revokeRuntimePermission(bp, userId) == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - affectedUserId = userId; + if (affectedUserId == UserHandle.USER_NULL) { + affectedUserId = userId; + } else if (affectedUserId != userId) { + // Multiple users affected. + affectedUserId = UserHandle.USER_ALL; + } } } @@ -4034,12 +4062,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @GuardedBy("mLock") - private boolean revokeUnusedSharedUserPermissionsLocked( - List<AndroidPackage> pkgList, UidPermissionState uidState) { + private int[] revokeUnusedSharedUserPermissionsLocked( + List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) { // Collect all used permissions in the UID final ArraySet<String> usedPermissions = new ArraySet<>(); if (pkgList == null || pkgList.size() == 0) { - return false; + return EmptyArray.INT; } for (AndroidPackage pkg : pkgList) { if (pkg.getRequestedPermissions().isEmpty()) { @@ -4055,27 +4083,44 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - boolean runtimePermissionChanged = false; - - // Prune permissions - final List<com.android.server.pm.permission.PermissionState> permissionStates = - uidState.getPermissionStates(); - final int permissionStatesSize = permissionStates.size(); - for (int i = permissionStatesSize - 1; i >= 0; i--) { - PermissionState permissionState = permissionStates.get(i); + // Prune install permissions + List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); + final int installPermCount = installPermStates.size(); + for (int i = installPermCount - 1; i >= 0; i--) { + PermissionState permissionState = installPermStates.get(i); if (!usedPermissions.contains(permissionState.getName())) { BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); if (bp != null) { - uidState.revokePermission(bp); - uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, 0); - if (permissionState.isRuntime()) { - runtimePermissionChanged = true; + permissionsState.revokeInstallPermission(bp); + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + MASK_PERMISSION_FLAGS_ALL, 0); + } + } + } + + int[] runtimePermissionChangedUserIds = EmptyArray.INT; + + // Prune runtime permissions + for (int userId : allUserIds) { + List<PermissionState> runtimePermStates = permissionsState + .getRuntimePermissionStates(userId); + final int runtimePermCount = runtimePermStates.size(); + for (int i = runtimePermCount - 1; i >= 0; i--) { + PermissionState permissionState = runtimePermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + MASK_PERMISSION_FLAGS_ALL, 0); + runtimePermissionChangedUserIds = ArrayUtils.appendInt( + runtimePermissionChangedUserIds, userId); } } } } - return runtimePermissionChanged; + return runtimePermissionChangedUserIds; } /** @@ -4323,19 +4368,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } else { mPackageManagerInt.forEachPackage(p -> { - final int[] userIds = mUserManagerInt.getUserIds(); - for (final int userId : userIds) { - final UidPermissionState uidState = getUidState(p, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " - + p.getPackageName() + " and user " + userId); - return; - } - if (uidState.getPermissionState(bp.getName()) != null) { - uidState.revokePermission(bp); - uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, - 0); - } + final PermissionsState permissionsState = getPermissionsState(p); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + p.getPackageName()); + return; + } + if (permissionsState.getInstallPermissionState(bp.getName()) != null) { + permissionsState.revokeInstallPermission(bp); + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + MASK_PERMISSION_FLAGS_ALL, 0); } }); } @@ -4743,124 +4784,62 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Nullable - private UidPermissionState getUidState(@NonNull PackageSetting ps, - @UserIdInt int userId) { - return getUidState(ps.getAppId(), userId); + private PermissionsState getPermissionsState(@NonNull PackageSetting ps) { + return getPermissionsState(ps.getAppId()); } @Nullable - private UidPermissionState getUidState(@NonNull AndroidPackage pkg, - @UserIdInt int userId) { - return getUidState(pkg.getUid(), userId); + private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) { + return getPermissionsState(pkg.getUid()); } @Nullable - private UidPermissionState getUidState(int appId, @UserIdInt int userId) { + private PermissionsState getPermissionsState(int appId) { synchronized (mLock) { - final UserPermissionState userState = mState.getUserState(userId); - if (userState == null) { - return null; - } - return userState.getUidState(appId); + return mAppIdStates.get(appId); } } - private void removeAppState(int appId) { + @Nullable + private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) { + return getOrCreatePermissionsState(ps.getAppId()); + } + + @Nullable + private PermissionsState getOrCreatePermissionsState(int appId) { synchronized (mLock) { - final int[] userIds = mState.getUserIds(); - for (final int userId : userIds) { - final UserPermissionState userState = mState.getUserState(userId); - userState.removeUidState(appId); + PermissionsState state = mAppIdStates.get(appId); + if (state == null) { + state = new PermissionsState(); + mAppIdStates.put(appId, state); } + return state; } } - private void readStateFromPackageSettings() { - final int[] userIds = getAllUserIds(); - mPackageManagerInt.forEachPackageSetting(ps -> { - final int appId = ps.getAppId(); - final PermissionsState permissionsState = ps.getPermissionsState(); + private void removePermissionsState(int appId) { + synchronized (mLock) { + mAppIdStates.remove(appId); + } + } + private void readPermissionsStateFromPackageSettings() { + mPackageManagerInt.forEachPackageSetting(ps -> { synchronized (mLock) { - for (final int userId : userIds) { - final UserPermissionState userState = mState.getOrCreateUserState(userId); - - userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed()); - final UidPermissionState uidState = userState.getOrCreateUidState(appId); - uidState.reset(); - uidState.setGlobalGids(permissionsState.getGlobalGids()); - uidState.setMissing(permissionsState.isMissing(userId)); - readStateFromPermissionStates(uidState, - permissionsState.getInstallPermissionStates(), false); - readStateFromPermissionStates(uidState, - permissionsState.getRuntimePermissionStates(userId), true); - } + mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState())); } }); } - private void readStateFromPermissionStates(@NonNull UidPermissionState uidState, - @NonNull List<PermissionsState.PermissionState> permissionStates, boolean isRuntime) { - final int permissionStatesSize = permissionStates.size(); - for (int i = 0; i < permissionStatesSize; i++) { - final PermissionsState.PermissionState permissionState = permissionStates.get(i); - final BasePermission permission = permissionState.getPermission(); - uidState.putPermissionState(permission, isRuntime, permissionState.isGranted(), - permissionState.getFlags()); - } - } - - private void writeStateToPackageSettings() { - final int[] userIds = mState.getUserIds(); + private void writePermissionsStateToPackageSettings() { mPackageManagerInt.forEachPackageSetting(ps -> { - ps.setInstallPermissionsFixed(false); - final PermissionsState permissionsState = ps.getPermissionsState(); - permissionsState.reset(); - final int appId = ps.getAppId(); - synchronized (mLock) { - for (final int userId : userIds) { - final UserPermissionState userState = mState.getUserState(userId); - if (userState == null) { - Slog.e(TAG, "Missing user state for " + userId); - continue; - } - - if (userState.areInstallPermissionsFixed(ps.name)) { - ps.setInstallPermissionsFixed(true); - } - - final UidPermissionState uidState = userState.getUidState(appId); - if (uidState == null) { - Slog.e(TAG, "Missing permission state for " + ps.name + " and user " - + userId); - continue; - } - - permissionsState.setGlobalGids(uidState.getGlobalGids()); - permissionsState.setMissing(uidState.isMissing(), userId); - final List<PermissionState> permissionStates = uidState.getPermissionStates(); - final int permissionStatesSize = permissionStates.size(); - for (int i = 0; i < permissionStatesSize; i++) { - final PermissionState permissionState = permissionStates.get(i); - - final BasePermission permission = permissionState.getPermission(); - if (permissionState.isGranted()) { - if (permissionState.isRuntime()) { - permissionsState.grantRuntimePermission(permission, userId); - } else { - permissionsState.grantInstallPermission(permission); - } - } - final int flags = permissionState.getFlags(); - if (flags != 0) { - final int flagsUserId = permissionState.isRuntime() ? userId - : UserHandle.USER_ALL; - permissionsState.updatePermissionFlags(permission, flagsUserId, flags, - flags); - } - } + final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId()); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + ps.name); + return; } + ps.getPermissionsState().copyFrom(permissionsState); } }); } @@ -4897,12 +4876,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } @Override - public void readStateFromPackageSettingsTEMP() { - PermissionManagerService.this.readStateFromPackageSettings(); + public void readPermissionsStateFromPackageSettingsTEMP() { + PermissionManagerService.this.readPermissionsStateFromPackageSettings(); } @Override - public void writeStateToPackageSettingsTEMP() { - PermissionManagerService.this.writeStateToPackageSettings(); + public void writePermissionsStateToPackageSettingsTEMP() { + PermissionManagerService.this.writePermissionsStateToPackageSettings(); } @Override public void onUserRemoved(@UserIdInt int userId) { @@ -4910,7 +4889,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override public void removePermissionsStateTEMP(int appId) { - PermissionManagerService.this.removeAppState(appId); + PermissionManagerService.this.removePermissionsState(appId); } @Override @UserIdInt diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 7f6a1d4284d2..ca207d8f0d41 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -189,7 +189,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @NonNull AndroidPackage pkg, @NonNull int[] userIds, @NonNull List<String> permissions, int callingUid, @PackageManager.PermissionWhitelistFlags int whitelistFlags); - /** Sets the whitelisted, restricted permissions for the given package. */ + /** Sets the allowlisted, restricted permissions for the given package. */ public abstract void setWhitelistedRestrictedPermissions( @NonNull String packageName, @NonNull List<String> permissions, @PackageManager.PermissionWhitelistFlags int flags, int userId); @@ -266,21 +266,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); /** - * Read permission state from package settings. + * Read {@code PermissionsState} from package settings. * * TODO(zhanghai): This is a temporary method because we should not expose * {@code PackageSetting} which is a implementation detail that permission should not know. * Instead, it should retrieve the legacy state via a defined API. */ - public abstract void readStateFromPackageSettingsTEMP(); + public abstract void readPermissionsStateFromPackageSettingsTEMP(); /** - * Write permission state to package settings. + * Write {@code PermissionsState} from to settings. * * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence * for permission. */ - public abstract void writeStateToPackageSettingsTEMP(); + public abstract void writePermissionsStateToPackageSettingsTEMP(); /** * Notify that a user has been removed and its permission state should be removed as well. diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java deleted file mode 100644 index 2ed9a50353d4..000000000000 --- a/services/core/java/com/android/server/pm/permission/PermissionState.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2020 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.pm.permission; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; - -import com.android.internal.annotations.GuardedBy; - -/** - * State for a single permission. - */ -public final class PermissionState { - - @NonNull - private final BasePermission mPermission; - - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private boolean mRuntime; - - @GuardedBy("mLock") - private boolean mGranted; - - @GuardedBy("mLock") - private int mFlags; - - public PermissionState(@NonNull BasePermission permission, boolean isRuntime) { - mPermission = permission; - mRuntime = isRuntime; - } - - public PermissionState(@NonNull PermissionState other) { - this(other.mPermission, other.mRuntime); - - mGranted = other.mGranted; - mFlags = other.mFlags; - } - - @NonNull - public BasePermission getPermission() { - return mPermission; - } - - @NonNull - public String getName() { - return mPermission.getName(); - } - - @Nullable - public int[] computeGids(@UserIdInt int userId) { - return mPermission.computeGids(userId); - } - - public boolean isRuntime() { - synchronized (mLock) { - return mRuntime; - } - } - - public boolean isGranted() { - synchronized (mLock) { - return mGranted; - } - } - - public boolean grant() { - synchronized (mLock) { - if (mGranted) { - return false; - } - mGranted = true; - UidPermissionState.invalidateCache(); - return true; - } - } - - public boolean revoke() { - synchronized (mLock) { - if (!mGranted) { - return false; - } - mGranted = false; - UidPermissionState.invalidateCache(); - return true; - } - } - - public int getFlags() { - synchronized (mLock) { - return mFlags; - } - } - - public boolean updateFlags(int flagMask, int flagValues) { - synchronized (mLock) { - final int newFlags = flagValues & flagMask; - - // Okay to do before the modification because we hold the lock. - UidPermissionState.invalidateCache(); - - final int oldFlags = mFlags; - mFlags = (mFlags & ~flagMask) | newFlags; - return mFlags != oldFlags; - } - } - - public boolean isDefault() { - synchronized (mLock) { - return !mGranted && mFlags == 0; - } - } -} diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java index 4fb2d5fc200e..bad59cb1b567 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionsState.java +++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java @@ -86,10 +86,6 @@ public final class PermissionsState { copyFrom(prototype); } - public int[] getGlobalGids() { - return mGlobalGids; - } - /** * Sets the global gids, applicable to all users. * @@ -829,7 +825,7 @@ public final class PermissionsState { PermissionState userState = mUserStates.get(userId); if (userState == null) { - userState = new PermissionState(mPerm); + userState = new PermissionState(mPerm.getName()); mUserStates.put(userId, userState); } @@ -912,7 +908,7 @@ public final class PermissionsState { } return userState.mFlags != oldFlags; } else if (newFlags != 0) { - userState = new PermissionState(mPerm); + userState = new PermissionState(mPerm.getName()); userState.mFlags = newFlags; mUserStates.put(userId, userState); return true; @@ -933,16 +929,16 @@ public final class PermissionsState { } public static final class PermissionState { - private final BasePermission mPermission; + private final String mName; private boolean mGranted; private int mFlags; - public PermissionState(BasePermission permission) { - mPermission = permission; + public PermissionState(String name) { + mName = name; } public PermissionState(PermissionState other) { - mPermission = other.mPermission; + mName = other.mName; mGranted = other.mGranted; mFlags = other.mFlags; } @@ -951,12 +947,8 @@ public final class PermissionsState { return !mGranted && mFlags == 0; } - public BasePermission getPermission() { - return mPermission; - } - public String getName() { - return mPermission.getName(); + return mName; } public boolean isGranted() { diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java deleted file mode 100644 index 4c047ffd30e8..000000000000 --- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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.pm.permission; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.content.pm.PackageManager; -import android.util.ArrayMap; -import android.util.ArraySet; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.ArrayUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * Permission state for a UID. - * <p> - * This class is also responsible for keeping track of the Linux GIDs per - * user for a package or a shared user. The GIDs are computed as a set of - * the GIDs for all granted permissions' GIDs on a per user basis. - */ -public final class UidPermissionState { - /** The permission operation failed. */ - public static final int PERMISSION_OPERATION_FAILURE = -1; - - /** The permission operation succeeded and no gids changed. */ - public static final int PERMISSION_OPERATION_SUCCESS = 0; - - /** The permission operation succeeded and gids changed. */ - public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1; - - private static final int[] NO_GIDS = {}; - - @NonNull - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private ArrayMap<String, PermissionState> mPermissions; - - @NonNull - private int[] mGlobalGids = NO_GIDS; - - private boolean mMissing; - - private boolean mPermissionReviewRequired; - - public UidPermissionState() { - /* do nothing */ - } - - public UidPermissionState(@NonNull UidPermissionState prototype) { - copyFrom(prototype); - } - - /** - * Gets the global gids, applicable to all users. - */ - @NonNull - public int[] getGlobalGids() { - return mGlobalGids; - } - - /** - * Sets the global gids, applicable to all users. - * - * @param globalGids The global gids. - */ - public void setGlobalGids(@NonNull int[] globalGids) { - if (!ArrayUtils.isEmpty(globalGids)) { - mGlobalGids = Arrays.copyOf(globalGids, globalGids.length); - } - } - - static void invalidateCache() { - PackageManager.invalidatePackageInfoCache(); - } - - /** - * Initialized this instance from another one. - * - * @param other The other instance. - */ - public void copyFrom(@NonNull UidPermissionState other) { - if (other == this) { - return; - } - - synchronized (mLock) { - if (mPermissions != null) { - if (other.mPermissions == null) { - mPermissions = null; - } else { - mPermissions.clear(); - } - } - if (other.mPermissions != null) { - if (mPermissions == null) { - mPermissions = new ArrayMap<>(); - } - final int permissionCount = other.mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - String name = other.mPermissions.keyAt(i); - PermissionState permissionState = other.mPermissions.valueAt(i); - mPermissions.put(name, new PermissionState(permissionState)); - } - } - } - - mGlobalGids = NO_GIDS; - if (other.mGlobalGids != NO_GIDS) { - mGlobalGids = other.mGlobalGids.clone(); - } - - mMissing = other.mMissing; - - mPermissionReviewRequired = other.mPermissionReviewRequired; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final UidPermissionState other = (UidPermissionState) obj; - - synchronized (mLock) { - if (mPermissions == null) { - if (other.mPermissions != null) { - return false; - } - } else if (!mPermissions.equals(other.mPermissions)) { - return false; - } - } - - if (mMissing != other.mMissing) { - return false; - } - - if (mPermissionReviewRequired != other.mPermissionReviewRequired) { - return false; - } - return Arrays.equals(mGlobalGids, other.mGlobalGids); - } - - /** - * Check whether the permissions state is missing for a user. This can happen if permission - * state is rolled back and we'll need to generate a reasonable default state to keep the app - * usable. - */ - public boolean isMissing() { - return mMissing; - } - - /** - * Set whether the permissions state is missing for a user. This can happen if permission state - * is rolled back and we'll need to generate a reasonable default state to keep the app usable. - */ - public void setMissing(boolean missing) { - mMissing = missing; - } - - public boolean isPermissionReviewRequired() { - return mPermissionReviewRequired; - } - - /** - * Gets whether the state has a given permission. - * - * @param name The permission name. - * @return Whether the state has the permission. - */ - public boolean hasPermission(@NonNull String name) { - synchronized (mLock) { - if (mPermissions == null) { - return false; - } - PermissionState permissionState = mPermissions.get(name); - return permissionState != null && permissionState.isGranted(); - } - } - - /** - * Gets whether the state has a given install permission. - * - * @param name The permission name. - * @return Whether the state has the install permission. - */ - public boolean hasInstallPermission(@NonNull String name) { - synchronized (mLock) { - if (mPermissions == null) { - return false; - } - PermissionState permissionState = mPermissions.get(name); - return permissionState != null && permissionState.isGranted() - && !permissionState.isRuntime(); - } - } - - /** - * Returns whether the state has any known request for the given permission name, - * whether or not it has been granted. - * - * @deprecated Not all requested permissions may be here. - */ - @Deprecated - public boolean hasRequestedPermission(@NonNull ArraySet<String> names) { - synchronized (mLock) { - if (mPermissions == null) { - return false; - } - for (int i = names.size() - 1; i >= 0; i--) { - if (mPermissions.get(names.valueAt(i)) != null) { - return true; - } - } - } - - return false; - } - - /** - * Returns whether the state has any known request for the given permission name, - * whether or not it has been granted. - * - * @deprecated Not all requested permissions may be here. - */ - @Deprecated - public boolean hasRequestedPermission(@NonNull String name) { - return mPermissions != null && (mPermissions.get(name) != null); - } - - /** - * Gets all permissions for a given device user id regardless if they - * are install time or runtime permissions. - * - * @return The permissions or an empty set. - */ - @NonNull - public Set<String> getPermissions() { - synchronized (mLock) { - if (mPermissions == null) { - return Collections.emptySet(); - } - - Set<String> permissions = new ArraySet<>(mPermissions.size()); - - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - String permission = mPermissions.keyAt(i); - - if (hasPermission(permission)) { - permissions.add(permission); - } - } - - return permissions; - } - } - - /** - * Gets the flags for a permission. - * - * @param name The permission name. - * @return The permission state or null if no such. - */ - public int getPermissionFlags(@NonNull String name) { - PermissionState permState = getPermissionState(name); - if (permState != null) { - return permState.getFlags(); - } - return 0; - } - - /** - * Update the flags associated with a given permission. - * @param permission The permission whose flags to update. - * @param flagMask Mask for which flags to change. - * @param flagValues New values for the mask flags. - * @return Whether the permission flags changed. - */ - public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask, - int flagValues) { - if (flagMask == 0) { - return false; - } - - PermissionState permissionState = ensurePermissionState(permission); - - final int oldFlags = permissionState.getFlags(); - - synchronized (mLock) { - final boolean updated = permissionState.updateFlags(flagMask, flagValues); - if (updated) { - final int newFlags = permissionState.getFlags(); - if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0 - && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - mPermissionReviewRequired = true; - } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0 - && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { - if (mPermissionReviewRequired && !hasPermissionRequiringReview()) { - mPermissionReviewRequired = false; - } - } - } - return updated; - } - } - - private boolean hasPermissionRequiringReview() { - synchronized (mLock) { - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - final PermissionState permission = mPermissions.valueAt(i); - if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - return true; - } - } - } - return false; - } - - public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) { - synchronized (mLock) { - if (mPermissions == null) { - return false; - } - boolean changed = false; - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - PermissionState permissionState = mPermissions.valueAt(i); - changed |= permissionState.updateFlags(flagMask, flagValues); - } - return changed; - } - } - - /** - * Compute the Linux gids for a given device user from the permissions - * granted to this user. Note that these are computed to avoid additional - * state as they are rarely accessed. - * - * @param userId The device user id. - * @return The gids for the device user. - */ - @NonNull - public int[] computeGids(@UserIdInt int userId) { - int[] gids = mGlobalGids; - - synchronized (mLock) { - if (mPermissions != null) { - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - PermissionState permissionState = mPermissions.valueAt(i); - if (!permissionState.isGranted()) { - continue; - } - final int[] permGids = permissionState.computeGids(userId); - if (permGids != NO_GIDS) { - gids = appendInts(gids, permGids); - } - } - } - } - - return gids; - } - - /** - * Compute the Linux gids for all device users from the permissions - * granted to these users. - * - * @return The gids for all device users. - */ - @NonNull - public int[] computeGids(@NonNull int[] userIds) { - int[] gids = mGlobalGids; - - for (int userId : userIds) { - final int[] userGids = computeGids(userId); - gids = appendInts(gids, userGids); - } - - return gids; - } - - /** - * Resets the internal state of this object. - */ - public void reset() { - mGlobalGids = NO_GIDS; - - synchronized (mLock) { - mPermissions = null; - invalidateCache(); - } - - mMissing = false; - mPermissionReviewRequired = false; - } - - /** - * Gets the state for a permission or null if no such. - * - * @param name The permission name. - * @return The permission state. - */ - @Nullable - public PermissionState getPermissionState(@NonNull String name) { - synchronized (mLock) { - if (mPermissions == null) { - return null; - } - return mPermissions.get(name); - } - } - - /** - * Gets all permission states. - * - * @return The permission states or an empty set. - */ - @NonNull - public List<PermissionState> getPermissionStates() { - synchronized (mLock) { - if (mPermissions == null) { - return Collections.emptyList(); - } - return new ArrayList<>(mPermissions.values()); - } - } - - /** - * Put a permission state. - */ - public void putPermissionState(@NonNull BasePermission permission, boolean isRuntime, - boolean isGranted, int flags) { - synchronized (mLock) { - ensureNoPermissionState(permission.name); - PermissionState permissionState = ensurePermissionState(permission, isRuntime); - if (isGranted) { - permissionState.grant(); - } - permissionState.updateFlags(flags, flags); - if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - mPermissionReviewRequired = true; - } - } - } - - /** - * Grant a permission. - * - * @param permission The permission to grant. - * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, - * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link - * #PERMISSION_OPERATION_FAILURE}. - */ - public int grantPermission(@NonNull BasePermission permission) { - if (hasPermission(permission.getName())) { - return PERMISSION_OPERATION_SUCCESS; - } - - PermissionState permissionState = ensurePermissionState(permission); - - if (!permissionState.grant()) { - return PERMISSION_OPERATION_FAILURE; - } - - return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED - : PERMISSION_OPERATION_SUCCESS; - } - - /** - * Revoke a permission. - * - * @param permission The permission to revoke. - * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, - * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link - * #PERMISSION_OPERATION_FAILURE}. - */ - public int revokePermission(@NonNull BasePermission permission) { - final String permissionName = permission.getName(); - if (!hasPermission(permissionName)) { - return PERMISSION_OPERATION_SUCCESS; - } - - PermissionState permissionState; - synchronized (mLock) { - permissionState = mPermissions.get(permissionName); - } - - if (!permissionState.revoke()) { - return PERMISSION_OPERATION_FAILURE; - } - - if (permissionState.isDefault()) { - ensureNoPermissionState(permissionName); - } - - return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED - : PERMISSION_OPERATION_SUCCESS; - } - - // TODO: fix this to use arraycopy and append all ints in one go - private static int[] appendInts(int[] current, int[] added) { - if (current != null && added != null) { - for (int guid : added) { - current = ArrayUtils.appendInt(current, guid); - } - } - return current; - } - - @NonNull - private PermissionState ensurePermissionState(@NonNull BasePermission permission) { - return ensurePermissionState(permission, permission.isRuntime()); - } - - @NonNull - private PermissionState ensurePermissionState(@NonNull BasePermission permission, - boolean isRuntime) { - final String permissionName = permission.getName(); - synchronized (mLock) { - if (mPermissions == null) { - mPermissions = new ArrayMap<>(); - } - PermissionState permissionState = mPermissions.get(permissionName); - if (permissionState == null) { - permissionState = new PermissionState(permission, isRuntime); - mPermissions.put(permissionName, permissionState); - } - return permissionState; - } - } - - private void ensureNoPermissionState(@NonNull String name) { - synchronized (mLock) { - if (mPermissions == null) { - return; - } - mPermissions.remove(name); - if (mPermissions.isEmpty()) { - mPermissions = null; - } - } - } -} diff --git a/services/core/java/com/android/server/pm/permission/UserPermissionState.java b/services/core/java/com/android/server/pm/permission/UserPermissionState.java deleted file mode 100644 index 7f55cb161e40..000000000000 --- a/services/core/java/com/android/server/pm/permission/UserPermissionState.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2020 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.pm.permission; - -import android.annotation.AppIdInt; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.UserHandle; -import android.util.ArraySet; -import android.util.SparseArray; - -import com.android.internal.annotations.GuardedBy; - -/** - * Permission state for a user. - */ -public final class UserPermissionState { - /** - * Whether the install permissions have been granted to a package, so that no install - * permissions should be added to it unless the package is upgraded. - */ - @GuardedBy("mLock") - @NonNull - private final ArraySet<String> mInstallPermissionsFixed = new ArraySet<>(); - - /** - * Maps from app ID to {@link UidPermissionState}. - */ - @GuardedBy("mLock") - @NonNull - private final SparseArray<UidPermissionState> mUidStates = new SparseArray<>(); - - @NonNull - private final Object mLock; - - public UserPermissionState(@NonNull Object lock) { - mLock = lock; - } - - public boolean areInstallPermissionsFixed(@NonNull String packageName) { - synchronized (mLock) { - return mInstallPermissionsFixed.contains(packageName); - } - } - - public void setInstallPermissionsFixed(@NonNull String packageName, boolean fixed) { - synchronized (mLock) { - if (fixed) { - mInstallPermissionsFixed.add(packageName); - } else { - mInstallPermissionsFixed.remove(packageName); - } - } - } - - @Nullable - public UidPermissionState getUidState(@AppIdInt int appId) { - checkAppId(appId); - synchronized (mLock) { - return mUidStates.get(appId); - } - } - - @NonNull - public UidPermissionState getOrCreateUidState(@AppIdInt int appId) { - checkAppId(appId); - synchronized (mLock) { - UidPermissionState uidState = mUidStates.get(appId); - if (uidState == null) { - uidState = new UidPermissionState(); - mUidStates.put(appId, uidState); - } - return uidState; - } - } - - public void removeUidState(@AppIdInt int appId) { - checkAppId(appId); - synchronized (mLock) { - mUidStates.delete(appId); - } - } - - private void checkAppId(@AppIdInt int appId) { - if (UserHandle.getUserId(appId) != 0) { - throw new IllegalArgumentException(appId + " is not an app ID"); - } - } -} diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index d01a30fbd818..35b14490eba2 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -281,7 +281,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int LONG_PRESS_HOME_NOTHING = 0; static final int LONG_PRESS_HOME_ALL_APPS = 1; static final int LONG_PRESS_HOME_ASSIST = 2; - static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_ASSIST; + static final int LONG_PRESS_HOME_NOTIFICATION_PANEL = 3; + static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_NOTIFICATION_PANEL; // must match: config_doubleTapOnHomeBehavior in config.xml static final int DOUBLE_TAP_HOME_NOTHING = 0; @@ -1694,8 +1695,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { case LONG_PRESS_HOME_ASSIST: launchAssistAction(null, deviceId); break; + case LONG_PRESS_HOME_NOTIFICATION_PANEL: + IStatusBarService statusBarService = getStatusBarService(); + if (statusBarService != null) { + try { + statusBarService.togglePanel(); + } catch (RemoteException e) { + // do nothing. + } + } + break; default: - Log.w(TAG, "Undefined home long press behavior: " + Log.w(TAG, "Undefined long press on home behavior: " + mLongPressOnHomeBehavior); break; } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 9ff164ac4a7b..6044ee18614a 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -3494,7 +3494,7 @@ public final class PowerManagerService extends SystemService } if (mDeviceIdleMode) { // If we are in idle mode, we will also ignore all partial wake locks that are - // for application uids that are not whitelisted. + // for application uids that are not allowlisted. final UidState state = wakeLock.mUidState; if (Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 && Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 && diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java index 0d910e47dc0d..f4a014adba42 100644 --- a/services/core/java/com/android/server/power/WirelessChargerDetector.java +++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java @@ -92,7 +92,7 @@ public class WirelessChargerDetector { // cosine of the maximum angle variance that we tolerate while at rest. private static final double MOVEMENT_ANGLE_COS_THRESHOLD = Math.cos(5 * Math.PI / 180); - // Sanity thresholds for the gravity vector. + // Validity thresholds for the gravity vector. private static final double MIN_GRAVITY = SensorManager.GRAVITY_EARTH - 1.0f; private static final double MAX_GRAVITY = SensorManager.GRAVITY_EARTH + 1.0f; diff --git a/services/core/java/com/android/server/powerstats/BatteryTrigger.java b/services/core/java/com/android/server/powerstats/BatteryTrigger.java new file mode 100644 index 000000000000..c1f97f2685b5 --- /dev/null +++ b/services/core/java/com/android/server/powerstats/BatteryTrigger.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; +import android.util.Log; + +/** + * BatteryTrigger instantiates a BroadcastReceiver that listens for changes + * to the battery. When the battery level drops by 1% a message is sent to + * the PowerStatsLogger to log the rail energy data to on-device storage. + */ +public final class BatteryTrigger extends PowerStatsLogTrigger { + private static final String TAG = BatteryTrigger.class.getSimpleName(); + private static final boolean DEBUG = false; + + private int mBatteryLevel = 0; + + private final BroadcastReceiver mBatteryLevelReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case Intent.ACTION_BATTERY_CHANGED: + int newBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); + + if (newBatteryLevel < mBatteryLevel) { + if (DEBUG) Log.d(TAG, "Battery level dropped. Log rail data"); + logPowerStatsData(); + } + + mBatteryLevel = newBatteryLevel; + break; + } + } + }; + + public BatteryTrigger(Context context, PowerStatsLogger powerStatsLogger, + boolean triggerEnabled) { + super(context, powerStatsLogger); + + if (triggerEnabled) { + IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + Intent batteryStatus = mContext.registerReceiver(mBatteryLevelReceiver, filter); + mBatteryLevel = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); + } + } +} diff --git a/services/core/java/com/android/server/powerstats/PowerStatsData.java b/services/core/java/com/android/server/powerstats/PowerStatsData.java new file mode 100644 index 000000000000..755bd5fce45e --- /dev/null +++ b/services/core/java/com/android/server/powerstats/PowerStatsData.java @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import android.util.Log; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; +import android.util.proto.ProtoUtils; +import android.util.proto.WireTypeMismatchException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * PowerStatsData is a class that performs two operations: + * 1) Unpacks serialized protobuf byte arrays, as defined in powerstatsservice.proto, + * into RailInfo or EnergyData object arrays. + * + * 2) Packs RailInfo or EnergyData object arrays in protobuf byte arrays as + * defined in powerstatsservice.proto. + * + * Inside frameworks, proto source is generated with the genstream option + * and therefore the getter/setter helper functions are not available. + * The protos need to be packed/unpacked in a more manual way using + * ProtoOutputStream/ProtoInputStream. + */ +public class PowerStatsData { + private static final String TAG = PowerStatsData.class.getSimpleName(); + + private List<Data> mDataList; + + public PowerStatsData(ProtoInputStream pis) throws IOException { + mDataList = new ArrayList<Data>(); + unpackProto(pis); + } + + public PowerStatsData(Data[] data) { + mDataList = new ArrayList<Data>(Arrays.asList(data)); + } + + private void unpackProto(ProtoInputStream pis) throws IOException { + long token; + + while (true) { + try { + switch (pis.nextField()) { + case (int) PowerStatsServiceProto.RAIL_INFO: + token = pis.start(PowerStatsServiceProto.RAIL_INFO); + mDataList.add(new RailInfo(pis)); + pis.end(token); + break; + + case (int) PowerStatsServiceProto.ENERGY_DATA: + token = pis.start(PowerStatsServiceProto.ENERGY_DATA); + mDataList.add(new EnergyData(pis)); + pis.end(token); + break; + + case ProtoInputStream.NO_MORE_FIELDS: + return; + + default: + Log.e(TAG, "Unhandled field in proto: " + + ProtoUtils.currentFieldToString(pis)); + break; + } + } catch (WireTypeMismatchException wtme) { + Log.e(TAG, "Wire Type mismatch in proto: " + ProtoUtils.currentFieldToString(pis)); + } + } + } + + /** + * Write this object to an output stream in protobuf format. + * + * @param pos ProtoOutputStream of file where data is to be written. Data is + * written in protobuf format as defined by powerstatsservice.proto. + */ + public void toProto(ProtoOutputStream pos) { + long token; + + for (Data data : mDataList) { + if (data instanceof RailInfo) { + token = pos.start(PowerStatsServiceProto.RAIL_INFO); + } else { + token = pos.start(PowerStatsServiceProto.ENERGY_DATA); + } + data.toProto(pos); + pos.end(token); + } + } + + /** + * Convert mDataList to proto format and return the serialized byte array. + * + * @return byte array containing a serialized protobuf of mDataList. + */ + public byte[] getProtoBytes() { + ProtoOutputStream pos = new ProtoOutputStream(); + long token; + + for (Data data : mDataList) { + if (data instanceof RailInfo) { + token = pos.start(PowerStatsServiceProto.RAIL_INFO); + } else { + token = pos.start(PowerStatsServiceProto.ENERGY_DATA); + } + data.toProto(pos); + pos.end(token); + } + return pos.getBytes(); + } + + /** + * Print this object to logcat. + */ + public void print() { + for (Data data : mDataList) { + Log.d(TAG, data.toString()); + } + } + + /** + * RailInfo is a class that stores a description for an individual ODPM + * rail. It provides functionality to unpack a RailInfo object from a + * serialized protobuf byte array, and to pack a RailInfo object into + * a ProtoOutputStream. + */ + public static class RailInfo extends Data { + public String mRailName; + public String mSubSysName; + public long mSamplingRate; + + public RailInfo(ProtoInputStream pis) throws IOException { + unpackProto(pis); + } + + public RailInfo(long index, String railName, String subSysName, long samplingRate) { + mIndex = index; + mRailName = railName; + mSubSysName = subSysName; + mSamplingRate = samplingRate; + } + + @Override + protected void unpackProto(ProtoInputStream pis) throws IOException { + while (true) { + try { + switch (pis.nextField()) { + case (int) RailInfoProto.INDEX: + mIndex = pis.readInt(RailInfoProto.INDEX); + break; + + case (int) RailInfoProto.RAIL_NAME: + mRailName = pis.readString(RailInfoProto.RAIL_NAME); + break; + + case (int) RailInfoProto.SUBSYS_NAME: + mSubSysName = pis.readString(RailInfoProto.SUBSYS_NAME); + break; + + case (int) RailInfoProto.SAMPLING_RATE: + mSamplingRate = pis.readInt(RailInfoProto.SAMPLING_RATE); + break; + + case ProtoInputStream.NO_MORE_FIELDS: + return; + + default: + Log.e(TAG, "Unhandled field in RailInfoProto: " + + ProtoUtils.currentFieldToString(pis)); + break; + } + } catch (WireTypeMismatchException wtme) { + Log.e(TAG, "Wire Type mismatch in RailInfoProto: " + + ProtoUtils.currentFieldToString(pis)); + } + } + } + + @Override + public void toProto(ProtoOutputStream pos) { + pos.write(RailInfoProto.INDEX, mIndex); + pos.write(RailInfoProto.RAIL_NAME, mRailName); + pos.write(RailInfoProto.SUBSYS_NAME, mSubSysName); + pos.write(RailInfoProto.SAMPLING_RATE, mSamplingRate); + } + + @Override + public String toString() { + return String.format("Index = " + mIndex + + ", RailName = " + mRailName + + ", SubSysName = " + mSubSysName + + ", SamplingRate = " + mSamplingRate); + } + } + + /** + * EnergyData is a class that stores an energy (uWs) data reading for an + * individual ODPM rail. It provides functionality to unpack an EnergyData + * object from a serialized protobuf byte array, and to pack an EnergyData + * object into a ProtoOutputStream. + */ + public static class EnergyData extends Data { + public long mTimestampMs; + public long mEnergyUWs; + + public EnergyData(ProtoInputStream pis) throws IOException { + unpackProto(pis); + } + + public EnergyData(long index, long timestampMs, long energyUWs) { + mIndex = index; + mTimestampMs = timestampMs; + mEnergyUWs = energyUWs; + } + + @Override + protected void unpackProto(ProtoInputStream pis) throws IOException { + while (true) { + try { + switch (pis.nextField()) { + case (int) EnergyDataProto.INDEX: + mIndex = pis.readInt(EnergyDataProto.INDEX); + break; + + case (int) EnergyDataProto.TIMESTAMP_MS: + mTimestampMs = pis.readLong(EnergyDataProto.TIMESTAMP_MS); + break; + + case (int) EnergyDataProto.ENERGY_UWS: + mEnergyUWs = pis.readLong(EnergyDataProto.ENERGY_UWS); + break; + + case ProtoInputStream.NO_MORE_FIELDS: + return; + + default: + Log.e(TAG, "Unhandled field in EnergyDataProto: " + + ProtoUtils.currentFieldToString(pis)); + break; + } + } catch (WireTypeMismatchException wtme) { + Log.e(TAG, "Wire Type mismatch in EnergyDataProto: " + + ProtoUtils.currentFieldToString(pis)); + } + } + } + + @Override + protected void toProto(ProtoOutputStream pos) { + pos.write(EnergyDataProto.INDEX, mIndex); + pos.write(EnergyDataProto.TIMESTAMP_MS, mTimestampMs); + pos.write(EnergyDataProto.ENERGY_UWS, mEnergyUWs); + } + + @Override + public String toString() { + return String.format("Index = " + mIndex + + ", Timestamp (ms) = " + mTimestampMs + + ", Energy (uWs) = " + mEnergyUWs); + } + } + + private abstract static class Data { + public long mIndex; + protected abstract void unpackProto(ProtoInputStream pis) throws IOException; + protected abstract void toProto(ProtoOutputStream pos); + } +} diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java new file mode 100644 index 000000000000..84a6fc94598e --- /dev/null +++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import android.content.Context; +import android.util.Log; + +import com.android.internal.util.FileRotator; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.concurrent.locks.ReentrantLock; + +/** + * PowerStatsDataStorage implements the on-device storage cache for energy + * data. This data must be persisted across boot cycles so we store it + * on-device. Versioning of this data is handled by deleting any data that + * does not match the current version. The cache is implemented as a circular + * buffer using the FileRotator class in android.util. We maintain 48 hours + * worth of logs in 12 files (4 hours each). + */ +public class PowerStatsDataStorage { + private static final String TAG = PowerStatsDataStorage.class.getSimpleName(); + + private static final long MILLISECONDS_PER_HOUR = 1000 * 60 * 60; + // Rotate files every 4 hours. + private static final long ROTATE_AGE_MILLIS = 4 * MILLISECONDS_PER_HOUR; + // Store 48 hours worth of data. + private static final long DELETE_AGE_MILLIS = 48 * MILLISECONDS_PER_HOUR; + + private final ReentrantLock mLock = new ReentrantLock(); + private File mDataStorageDir; + private final FileRotator mFileRotator; + + private static class DataElement { + private static final int LENGTH_FIELD_WIDTH = 4; + private static final int MAX_DATA_ELEMENT_SIZE = 1000; + + private byte[] mData; + + private byte[] toByteArray() throws IOException { + ByteArrayOutputStream data = new ByteArrayOutputStream(); + data.write(ByteBuffer.allocate(LENGTH_FIELD_WIDTH).putInt(mData.length).array()); + data.write(mData); + return data.toByteArray(); + } + + protected byte[] getData() { + return mData; + } + + private DataElement(byte[] data) { + mData = data; + } + + private DataElement(InputStream in) throws IOException { + byte[] lengthBytes = new byte[LENGTH_FIELD_WIDTH]; + int bytesRead = in.read(lengthBytes); + mData = new byte[0]; + + if (bytesRead == LENGTH_FIELD_WIDTH) { + int length = ByteBuffer.wrap(lengthBytes).getInt(); + + if (0 < length && length < MAX_DATA_ELEMENT_SIZE) { + mData = new byte[length]; + bytesRead = in.read(mData); + + if (bytesRead != length) { + throw new IOException("Invalid bytes read, expected: " + length + + ", actual: " + bytesRead); + } + } else { + throw new IOException("DataElement size is invalid: " + length); + } + } else { + throw new IOException("Did not read " + LENGTH_FIELD_WIDTH + " bytes (" + bytesRead + + ")"); + } + } + } + + /** + * Used by external classes to read DataElements from on-device storage. + * This callback is passed in to the read() function and is called for + * each DataElement read from on-device storage. + */ + public interface DataElementReadCallback { + /** + * When performing a read of the on-device storage this callback + * must be passed in to the read function. The function will be + * called for each DataElement read from on-device storage. + * + * @param data Byte array containing a DataElement payload. + */ + void onReadDataElement(byte[] data); + } + + private static class DataReader implements FileRotator.Reader { + private DataElementReadCallback mCallback; + + DataReader(DataElementReadCallback callback) { + mCallback = callback; + } + + @Override + public void read(InputStream in) throws IOException { + while (in.available() > 0) { + try { + DataElement dataElement = new DataElement(in); + mCallback.onReadDataElement(dataElement.getData()); + } catch (IOException e) { + Log.e(TAG, "Failed to read from storage. " + e.getMessage()); + } + } + } + } + + private static class DataRewriter implements FileRotator.Rewriter { + byte[] mActiveFileData; + byte[] mNewData; + + DataRewriter(byte[] data) { + mActiveFileData = new byte[0]; + mNewData = data; + } + + @Override + public void reset() { + // ignored + } + + @Override + public void read(InputStream in) throws IOException { + mActiveFileData = new byte[in.available()]; + in.read(mActiveFileData); + } + + @Override + public boolean shouldWrite() { + return true; + } + + @Override + public void write(OutputStream out) throws IOException { + out.write(mActiveFileData); + out.write(mNewData); + } + } + + public PowerStatsDataStorage(Context context, File dataStoragePath, + String dataStorageFilename) { + mDataStorageDir = dataStoragePath; + + if (!mDataStorageDir.exists() && !mDataStorageDir.mkdirs()) { + Log.wtf(TAG, "mDataStorageDir does not exist: " + mDataStorageDir.getPath()); + mFileRotator = null; + } else { + // Delete files written with an old version number. The version is included in the + // filename, so any files that don't match the current version number can be deleted. + File[] files = mDataStorageDir.listFiles(); + for (int i = 0; i < files.length; i++) { + if (!files[i].getName().matches(dataStorageFilename + "(.*)")) { + files[i].delete(); + } + } + + mFileRotator = new FileRotator(mDataStorageDir, + dataStorageFilename, + ROTATE_AGE_MILLIS, + DELETE_AGE_MILLIS); + } + } + + /** + * Writes data stored in PowerStatsDataStorage to a file descriptor. + * + * @param data Byte array to write to on-device storage. Byte array is + * converted to a DataElement which prefixes the payload with + * the data length. The DataElement is then converted to a byte + * array and written to on-device storage. + */ + public void write(byte[] data) { + mLock.lock(); + + long currentTimeMillis = System.currentTimeMillis(); + try { + DataElement dataElement = new DataElement(data); + mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()), + currentTimeMillis); + mFileRotator.maybeRotate(currentTimeMillis); + } catch (IOException e) { + Log.e(TAG, "Failed to write to on-device storage: " + e); + } + + mLock.unlock(); + } + + /** + * Reads all DataElements stored in on-device storage. For each + * DataElement retrieved from on-device storage, callback is called. + */ + public void read(DataElementReadCallback callback) throws IOException { + mFileRotator.readMatching(new DataReader(callback), Long.MIN_VALUE, Long.MAX_VALUE); + } +} diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java new file mode 100644 index 000000000000..dc996a3e2d2e --- /dev/null +++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 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.powerstats; + +/** + * PowerStatsHALWrapper is a wrapper class for the PowerStats HAL API calls. + */ +public final class PowerStatsHALWrapper { + private static final String TAG = PowerStatsHALWrapper.class.getSimpleName(); + + /** + * IPowerStatsHALWrapper defines the interface to the PowerStatsHAL. + */ + public interface IPowerStatsHALWrapper { + /** + * Returns rail info for all available ODPM rails. + * + * @return array of RailInfo objects containing rail info for all + * available rails. + */ + PowerStatsData.RailInfo[] readRailInfo(); + + /** + * Returns energy data for all available ODPM rails. Available rails can + * be retrieved by calling nativeGetRailInfo. Energy data and + * rail info can be linked through the index field. + * + * @return array of EnergyData objects containing energy data for all + * available rails. + */ + PowerStatsData.EnergyData[] readEnergyData(); + + /** + * Returns boolean indicating if connection to power stats HAL was + * established. + * + * @return true if connection to power stats HAL was correctly established + * and that energy data and rail info can be read from the interface. + * false otherwise + */ + boolean initialize(); + } + + /** + * PowerStatsHALWrapperImpl is the implementation of the IPowerStatsHALWrapper + * used by the PowerStatsService. Other implementations will be used by the testing + * framework and will be passed into the PowerStatsService through an injector. + */ + public static final class PowerStatsHALWrapperImpl implements IPowerStatsHALWrapper { + private static native boolean nativeInit(); + private static native PowerStatsData.RailInfo[] nativeGetRailInfo(); + private static native PowerStatsData.EnergyData[] nativeGetEnergyData(); + + /** + * Returns rail info for all available ODPM rails. + * + * @return array of RailInfo objects containing rail info for all + * available rails. + */ + @Override + public PowerStatsData.RailInfo[] readRailInfo() { + return nativeGetRailInfo(); + } + + /** + * Returns energy data for all available ODPM rails. Available rails can + * be retrieved by calling nativeGetRailInfo. Energy data and + * rail info can be linked through the index field. + * + * @return array of EnergyData objects containing energy data for all + * available rails. + */ + @Override + public PowerStatsData.EnergyData[] readEnergyData() { + return nativeGetEnergyData(); + } + + /** + * Returns boolean indicating if connection to power stats HAL was + * established. + * + * @return true if connection to power stats HAL was correctly established + * and that energy data and rail info can be read from the interface. + * false otherwise + */ + @Override + public boolean initialize() { + return nativeInit(); + } + } +} diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java new file mode 100644 index 000000000000..1754185ea71b --- /dev/null +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import android.content.Context; +import android.os.Message; + +/** + * PowerStatsLogTrigger is the base class for other trigger classes. + * It provides the logPowerStatsData() function which sends a message + * to the PowerStatsLogger to read the rail energy data and log it to + * on-device storage. This class is abstract and cannot be instantiated. + */ +public abstract class PowerStatsLogTrigger { + private static final String TAG = PowerStatsLogTrigger.class.getSimpleName(); + + protected Context mContext; + private PowerStatsLogger mPowerStatsLogger; + + protected void logPowerStatsData() { + Message.obtain( + mPowerStatsLogger, + PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE).sendToTarget(); + } + + public PowerStatsLogTrigger(Context context, PowerStatsLogger powerStatsLogger) { + mContext = context; + mPowerStatsLogger = powerStatsLogger; + } +} diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java new file mode 100644 index 000000000000..71a34a4174e5 --- /dev/null +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; + +import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * PowerStatsLogger is responsible for logging energy data to on-device + * storage. Messages are sent to its message handler to request that energy + * data be logged, at which time it queries the PowerStats HAL and logs the + * data to on-device storage. The on-device storage is dumped to file by + * calling writeToFile with a file descriptor that points to the output file. + */ +public final class PowerStatsLogger extends Handler { + private static final String TAG = PowerStatsLogger.class.getSimpleName(); + private static final boolean DEBUG = false; + protected static final int MSG_LOG_TO_DATA_STORAGE = 0; + + private final PowerStatsDataStorage mPowerStatsDataStorage; + private final IPowerStatsHALWrapper mPowerStatsHALWrapper; + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_LOG_TO_DATA_STORAGE: + if (DEBUG) Log.d(TAG, "Logging to data storage"); + PowerStatsData energyData = + new PowerStatsData(mPowerStatsHALWrapper.readEnergyData()); + mPowerStatsDataStorage.write(energyData.getProtoBytes()); + break; + } + } + + /** + * Writes data stored in PowerStatsDataStorage to a file descriptor. + * + * @param fd FileDescriptor where data stored in PowerStatsDataStorage is + * written. Data is written in protobuf format as defined by + * powerstatsservice.proto. + */ + public void writeToFile(FileDescriptor fd) { + if (DEBUG) Log.d(TAG, "Writing to file"); + + final ProtoOutputStream pos = new ProtoOutputStream(fd); + + try { + PowerStatsData railInfo = new PowerStatsData(mPowerStatsHALWrapper.readRailInfo()); + railInfo.toProto(pos); + if (DEBUG) railInfo.print(); + + mPowerStatsDataStorage.read(new PowerStatsDataStorage.DataElementReadCallback() { + @Override + public void onReadDataElement(byte[] data) { + try { + final ProtoInputStream pis = + new ProtoInputStream(new ByteArrayInputStream(data)); + // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write + // a byte array that already contains a serialized proto, so I have to + // deserialize, then re-serialize. This is computationally inefficient. + final PowerStatsData energyData = new PowerStatsData(pis); + energyData.toProto(pos); + if (DEBUG) energyData.print(); + } catch (IOException e) { + Log.e(TAG, "Failed to write energy data to incident report."); + } + } + }); + } catch (IOException e) { + Log.e(TAG, "Failed to write rail info to incident report."); + } + + pos.flush(); + } + + public PowerStatsLogger(Context context, File dataStoragePath, String dataStorageFilename, + IPowerStatsHALWrapper powerStatsHALWrapper) { + super(Looper.getMainLooper()); + mPowerStatsHALWrapper = powerStatsHALWrapper; + mPowerStatsDataStorage = new PowerStatsDataStorage(context, dataStoragePath, + dataStorageFilename); + } +} diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java new file mode 100644 index 000000000000..fd609c14276c --- /dev/null +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import android.content.Context; +import android.os.Binder; +import android.os.Environment; +import android.os.UserHandle; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.DumpUtils; +import com.android.server.SystemService; +import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; +import com.android.server.powerstats.PowerStatsHALWrapper.PowerStatsHALWrapperImpl; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * This class provides a system service that estimates system power usage + * per subsystem (modem, wifi, gps, display, etc) and provides those power + * estimates to subscribers. + */ +public class PowerStatsService extends SystemService { + private static final String TAG = PowerStatsService.class.getSimpleName(); + private static final boolean DEBUG = false; + private static final String DATA_STORAGE_SUBDIR = "powerstats"; + private static final int DATA_STORAGE_VERSION = 0; + private static final String DATA_STORAGE_FILENAME = "log.powerstats." + DATA_STORAGE_VERSION; + + private final Injector mInjector; + + private Context mContext; + private IPowerStatsHALWrapper mPowerStatsHALWrapper; + private PowerStatsLogger mPowerStatsLogger; + private BatteryTrigger mBatteryTrigger; + private TimerTrigger mTimerTrigger; + + @VisibleForTesting + static class Injector { + File createDataStoragePath() { + return new File(Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), + DATA_STORAGE_SUBDIR); + } + + String createDataStorageFilename() { + return DATA_STORAGE_FILENAME; + } + + IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() { + return new PowerStatsHALWrapperImpl(); + } + + PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, + String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { + return new PowerStatsLogger(context, dataStoragePath, dataStorageFilename, + powerStatsHALWrapper); + } + + BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) { + return new BatteryTrigger(context, powerStatsLogger, true /* trigger enabled */); + } + + TimerTrigger createTimerTrigger(Context context, PowerStatsLogger powerStatsLogger) { + return new TimerTrigger(context, powerStatsLogger, true /* trigger enabled */); + } + } + + private final class BinderService extends Binder { + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + + if (args.length > 0 && "--proto".equals(args[0])) { + mPowerStatsLogger.writeToFile(fd); + } + } + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_BOOT_COMPLETED) { + onSystemServiceReady(); + } + } + + @Override + public void onStart() { + publishBinderService(Context.POWER_STATS_SERVICE, new BinderService()); + } + + private void onSystemServiceReady() { + mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl(); + + if (mPowerStatsHALWrapper.initialize()) { + if (DEBUG) Log.d(TAG, "Starting PowerStatsService"); + + // Only start logger and triggers if initialization is successful. + mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, + mInjector.createDataStoragePath(), mInjector.createDataStorageFilename(), + mPowerStatsHALWrapper); + mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger); + mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger); + } else { + Log.e(TAG, "Initialization of PowerStatsHAL wrapper failed"); + } + } + + public PowerStatsService(Context context) { + this(context, new Injector()); + } + + @VisibleForTesting + public PowerStatsService(Context context, Injector injector) { + super(context); + mContext = context; + mInjector = injector; + } +} diff --git a/services/core/java/com/android/server/powerstats/TEST_MAPPING b/services/core/java/com/android/server/powerstats/TEST_MAPPING new file mode 100644 index 000000000000..79224a580cd8 --- /dev/null +++ b/services/core/java/com/android/server/powerstats/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.powerstats" + } + ] + } + ] +} diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java new file mode 100644 index 000000000000..a9bee8bec77f --- /dev/null +++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import android.content.Context; +import android.os.Handler; +import android.util.Log; + +/** + * TimerTrigger sets a 60 second opportunistic timer using postDelayed. + * When the timer expires a message is sent to the PowerStatsLogger to + * read the rail energy data and log it to on-device storage. + */ +public final class TimerTrigger extends PowerStatsLogTrigger { + private static final String TAG = TimerTrigger.class.getSimpleName(); + private static final boolean DEBUG = false; + // TODO(b/166689029): Make configurable through global settings. + private static final long LOG_PERIOD_MS = 60 * 1000; + + private final Handler mHandler; + + private Runnable mLogData = new Runnable() { + @Override + public void run() { + // Do not wake the device for these messages. Opportunistically log rail data every + // LOG_PERIOD_MS. + mHandler.postDelayed(mLogData, LOG_PERIOD_MS); + if (DEBUG) Log.d(TAG, "Received delayed message. Log rail data"); + logPowerStatsData(); + } + }; + + public TimerTrigger(Context context, PowerStatsLogger powerStatsLogger, + boolean triggerEnabled) { + super(context, powerStatsLogger); + mHandler = mContext.getMainThreadHandler(); + + if (triggerEnabled) { + mLogData.run(); + } + } +} diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 6ba675db0aed..9350edf6d68a 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -1099,7 +1099,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba */ @AnyThread private boolean isRollbackWhitelisted(String packageName) { - // TODO: Remove #isModule when the white list is ready. + // TODO: Remove #isModule when the allowlist is ready. return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName) || isModule(packageName); } diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java index c769a50ec890..ca7f0366df60 100644 --- a/services/core/java/com/android/server/search/Searchables.java +++ b/services/core/java/com/android/server/search/Searchables.java @@ -419,7 +419,7 @@ public class Searchables { if (activities != null && !activities.isEmpty()) { ActivityInfo ai = activities.get(0).activityInfo; - // TODO: do some sanity checks here? + // TODO: do some validity checks here? return new ComponentName(ai.packageName, ai.name); } Log.w(LOG_TAG, "No web search activity found"); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 0314cf8605bc..c871a5e9647f 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2144,7 +2144,7 @@ public class StatsPullAtomService extends SystemService { JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY); JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY); JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY); - // Sanity check: Ensure all 4 lists have the same length. + // Validity check: Ensure all 4 lists have the same length. int length = pkg_names.length(); if (app_sizes.length() != length || app_data_sizes.length() != length || app_cache_sizes.length() != length) { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index b902b5a163e4..e9215f9793d2 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.app.ITransientNotificationCallback; import android.os.Bundle; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsetsController.Appearance; @@ -143,4 +144,9 @@ public interface StatusBarManagerInternal { * request) */ void requestWindowMagnificationConnection(boolean request); + + /** + * Handles a logging command from the WM shell command. + */ + void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 9bc8afa3561f..874b910e54f2 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -36,6 +36,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -534,6 +535,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } catch (RemoteException ex) { } } } + + @Override + public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) { + if (mBar != null) { + try { + mBar.handleWindowManagerLoggingCommand(args, outFd); + } catch (RemoteException ex) { } + } + } }; private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() { diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java index 56bcdd9ec146..e02cef149c13 100644 --- a/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java +++ b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java @@ -52,7 +52,7 @@ public abstract class TextServicesManagerInternal { }; /** - * @return Global instance if exists. Otherwise, a dummy no-op instance. + * @return Global instance if exists. Otherwise, a placeholder no-op instance. */ @NonNull public static TextServicesManagerInternal get() { diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java index d64032325539..6a12b7c8f9a5 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java @@ -117,12 +117,13 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat @Override public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) { + boolean geoDetectionEnabled = mGeoDetectionFeatureEnabled && isGeoDetectionEnabled(userId); return new ConfigurationInternal.Builder(userId) .setUserConfigAllowed(isUserConfigAllowed(userId)) .setAutoDetectionSupported(isAutoDetectionSupported()) .setAutoDetectionEnabled(isAutoDetectionEnabled()) .setLocationEnabled(isLocationEnabled(userId)) - .setGeoDetectionEnabled(isGeoDetectionEnabled(userId)) + .setGeoDetectionEnabled(geoDetectionEnabled) .build(); } @@ -167,9 +168,11 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled(); setAutoDetectionEnabled(autoDetectionEnabled); - final int userId = configuration.getUserId(); - final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled(); - setGeoDetectionEnabled(userId, geoTzDetectionEnabled); + if (mGeoDetectionFeatureEnabled) { + final int userId = configuration.getUserId(); + final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled(); + setGeoDetectionEnabled(userId, geoTzDetectionEnabled); + } } } @@ -211,4 +214,4 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat return mContext.getSystemService(ConnectivityManager.class) .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java index 6e180bc69c5d..68c8eaa5b39b 100644 --- a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java +++ b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java @@ -173,7 +173,7 @@ final class TvRemoteProviderWatcher { return false; } - // Check if package name is white-listed here. + // Check if package name is allowlisted here. if (!mUnbundledServicePackages.contains(serviceInfo.packageName)) { Slog.w(TAG, "Ignoring atv remote provider service because the package has not " + "been set and/or whitelisted: " diff --git a/services/core/java/com/android/server/tv/TvRemoteService.java b/services/core/java/com/android/server/tv/TvRemoteService.java index 58946456b940..c71cdef6234c 100644 --- a/services/core/java/com/android/server/tv/TvRemoteService.java +++ b/services/core/java/com/android/server/tv/TvRemoteService.java @@ -24,10 +24,10 @@ import com.android.server.Watchdog; /** * TvRemoteService represents a system service that allows a connected - * remote control (emote) service to inject white-listed input events + * remote control (emote) service to inject allowlisted input events * and call other specified methods for functioning as an emote service. * <p/> - * This service is intended for use only by white-listed packages. + * This service is intended for use only by allowlisted packages. */ public class TvRemoteService extends SystemService implements Watchdog.Monitor { private static final String TAG = "TvRemoteService"; diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 53d51463295f..39f6c597508c 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -684,7 +684,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS); final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now); - // Sanity check that provider still belongs to source package + // Validity check that provider still belongs to source package // Both direct boot aware and unaware packages are fine as we // will do filtering at query time to avoid multiple parsing. final ProviderInfo pi = getProviderInfo(uri.getAuthority(), sourceUserId, diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 202a3dcf46dc..5f4d46cabdc0 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2022,7 +2022,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } WallpaperData wd = mWallpaperMap.get(user.id); if (wd == null) { - // User hasn't started yet, so load her settings to peek at the wallpaper + // User hasn't started yet, so load their settings to peek at the wallpaper loadSettingsLocked(user.id, false); wd = mWallpaperMap.get(user.id); } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 6e9526afa962..fb06a9cb5887 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -67,6 +67,8 @@ import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityOptions; +import android.app.ActivityOptions.SourceInfo; import android.app.WaitResult; import android.app.WindowConfiguration.WindowingMode; import android.content.ComponentName; @@ -161,6 +163,8 @@ class ActivityMetricsLogger { * launched successfully. */ static final class LaunchingState { + /** The device uptime of {@link #notifyActivityLaunching}. */ + private final long mCurrentUpTimeMs = SystemClock.uptimeMillis(); /** The timestamp of {@link #notifyActivityLaunching}. */ private long mCurrentTransitionStartTimeNs; /** Non-null when a {@link TransitionInfo} is created for this state. */ @@ -199,6 +203,10 @@ class ActivityMetricsLogger { /** The latest activity to have been launched. */ @NonNull ActivityRecord mLastLaunchedActivity; + /** The type of the source that triggers the launch event. */ + @SourceInfo.SourceType int mSourceType; + /** The time from the source event (e.g. touch) to {@link #notifyActivityLaunching}. */ + int mSourceEventDelayMs = INVALID_DELAY; /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyTransitionStarting}. */ int mCurrentTransitionDelayMs; /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyStartingWindowDrawn}. */ @@ -222,8 +230,8 @@ class ActivityMetricsLogger { /** @return Non-null if there will be a window drawn event for the launch. */ @Nullable static TransitionInfo create(@NonNull ActivityRecord r, - @NonNull LaunchingState launchingState, boolean processRunning, - boolean processSwitch, int startResult) { + @NonNull LaunchingState launchingState, @Nullable ActivityOptions options, + boolean processRunning, boolean processSwitch, int startResult) { int transitionType = INVALID_TRANSITION_TYPE; if (processRunning) { if (startResult == START_SUCCESS) { @@ -240,22 +248,31 @@ class ActivityMetricsLogger { // That means the startResult is neither START_SUCCESS nor START_TASK_TO_FRONT. return null; } - return new TransitionInfo(r, launchingState, transitionType, processRunning, + return new TransitionInfo(r, launchingState, options, transitionType, processRunning, processSwitch); } /** Use {@link TransitionInfo#create} instead to ensure the transition type is valid. */ - private TransitionInfo(ActivityRecord r, LaunchingState launchingState, int transitionType, - boolean processRunning, boolean processSwitch) { + private TransitionInfo(ActivityRecord r, LaunchingState launchingState, + ActivityOptions options, int transitionType, boolean processRunning, + boolean processSwitch) { mLaunchingState = launchingState; mTransitionStartTimeNs = launchingState.mCurrentTransitionStartTimeNs; mTransitionType = transitionType; mProcessRunning = processRunning; mProcessSwitch = processSwitch; mCurrentTransitionDeviceUptime = - (int) TimeUnit.MILLISECONDS.toSeconds(SystemClock.uptimeMillis()); + (int) TimeUnit.MILLISECONDS.toSeconds(launchingState.mCurrentUpTimeMs); setLatestLaunchedActivity(r); launchingState.mAssociatedTransitionInfo = this; + if (options != null) { + final SourceInfo sourceInfo = options.getSourceInfo(); + if (sourceInfo != null) { + mSourceType = sourceInfo.type; + mSourceEventDelayMs = + (int) (launchingState.mCurrentUpTimeMs - sourceInfo.eventTimeMs); + } + } } /** @@ -324,6 +341,8 @@ class ActivityMetricsLogger { final private String launchedActivityAppRecordRequiredAbi; final String launchedActivityShortComponentName; final private String processName; + @VisibleForTesting final @SourceInfo.SourceType int sourceType; + @VisibleForTesting final int sourceEventDelayMs; final private int reason; final private int startingWindowDelayMs; final private int bindApplicationDelayMs; @@ -352,12 +371,14 @@ class ActivityMetricsLogger { ? null : launchedActivity.app.getRequiredAbi(); reason = info.mReason; + sourceEventDelayMs = info.mSourceEventDelayMs; startingWindowDelayMs = info.mStartingWindowDelayMs; bindApplicationDelayMs = info.mBindApplicationDelayMs; windowsDrawnDelayMs = info.mWindowsDrawnDelayMs; type = info.mTransitionType; processRecord = launchedActivity.app; processName = launchedActivity.processName; + sourceType = info.mSourceType; userId = launchedActivity.mUserId; launchedActivityShortComponentName = launchedActivity.shortComponentName; activityRecordIdHashCode = System.identityHashCode(launchedActivity); @@ -513,9 +534,10 @@ class ActivityMetricsLogger { * @param resultCode One of the {@link android.app.ActivityManager}.START_* flags, indicating * the result of the launch. * @param launchedActivity The activity that is being launched + * @param options The given options of the launching activity. */ void notifyActivityLaunched(@NonNull LaunchingState launchingState, int resultCode, - @Nullable ActivityRecord launchedActivity) { + @Nullable ActivityRecord launchedActivity, @Nullable ActivityOptions options) { if (launchedActivity == null) { // The launch is aborted, e.g. intent not resolved, class not found. abort(null /* info */, "nothing launched"); @@ -560,7 +582,7 @@ class ActivityMetricsLogger { } final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState, - processRunning, processSwitch, resultCode); + options, processRunning, processSwitch, resultCode); if (newInfo == null) { abort(info, "unrecognized launch"); return; @@ -864,7 +886,9 @@ class ActivityMetricsLogger { info.windowsDrawnDelayMs, launchToken, packageOptimizationInfo.getCompilationReason(), - packageOptimizationInfo.getCompilationFilter()); + packageOptimizationInfo.getCompilationFilter(), + info.sourceType, + info.sourceEventDelayMs); if (DEBUG_METRICS) { Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)", @@ -970,7 +994,9 @@ class ActivityMetricsLogger { : FrameworkStatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE, info.mLastLaunchedActivity.info.name, info.mProcessRunning, - startupTimeMs); + startupTimeMs, + info.mSourceType, + info.mSourceEventDelayMs); // Ends the trace started at the beginning of this function. This is located here to allow // the trace slice to have a noticable duration. diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 5196416e2cd3..ab464501193c 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2515,7 +2515,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { targetActivity.applyOptionsLocked(); } finally { mActivityMetricsLogger.notifyActivityLaunched(launchingState, - START_TASK_TO_FRONT, targetActivity); + START_TASK_TO_FRONT, targetActivity, activityOptions); } mService.getActivityStartController().postStartActivityProcessingForLastStarter( diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 19755f29043e..e8e4059af324 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -616,7 +616,7 @@ class ActivityStarter { voiceInteractor, startFlags, doResume, options, inTask, false /* restrictedBgActivity */, intentGrants); mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, - mLastStartActivityResult, mLastStartActivityRecord); + mLastStartActivityResult, mLastStartActivityRecord, options); } finally { onExecutionComplete(); } @@ -704,7 +704,7 @@ class ActivityStarter { // ActivityMetricsLogger will then wait for the windows to be drawn and populate // WaitResult. mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res, - mLastStartActivityRecord); + mLastStartActivityRecord, mOptions); return getExternalResult(mRequest.waitResult == null ? res : waitForResult(res, mLastStartActivityRecord)); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4db121b6ea33..2ca849de7f22 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3811,7 +3811,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mWmService.mWindowPlacerLocked.performSurfacePlacement(); } - // TODO: Super crazy long method that should be broken down... + // TODO: Super unexpected long method that should be broken down... void applySurfaceChangesTransaction() { final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked; diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java index d9cf637ffaf8..aac6b2544c4f 100644 --- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java +++ b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java @@ -32,7 +32,7 @@ import com.android.server.wm.utils.DeviceConfigInterface; import java.io.PrintWriter; /** - * A Blacklist for packages that should force the display out of high refresh rate. + * A Denylist for packages that should force the display out of high refresh rate. */ class HighRefreshRateBlacklist { @@ -72,7 +72,7 @@ class HighRefreshRateBlacklist { } } else { // If there's no config, or the config has been deleted, fallback to the device's - // default blacklist + // default denylist for (String pkg : mDefaultBlacklist) { mBlacklistedPackages.add(pkg); } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 714591a831bd..acf5f75f7e23 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -562,7 +562,8 @@ final class InputMonitor { } if (mAddNavInputConsumerHandle) { - mNavInputConsumer.show(mInputTransaction, w); + // We set the layer to z=MAX-1 so that it's always on top. + mNavInputConsumer.show(mInputTransaction, Integer.MAX_VALUE - 1); mAddNavInputConsumerHandle = false; } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 6c416830b59e..35338bb67469 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -250,7 +250,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, - START_TASK_TO_FRONT, targetActivity); + START_TASK_TO_FRONT, targetActivity, null /* options */); // Register for stack order changes mDefaultTaskDisplayArea.registerStackOrderChangedListener(this); diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index 2cb7d5a23647..072116f04aac 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -107,7 +107,7 @@ class RefreshRatePolicy { return mLowRefreshRateId; } - // If app is blacklisted using higher refresh rate, return default (lower) refresh rate + // If app is denylisted using higher refresh rate, return default (lower) refresh rate if (mHighRefreshRateBlacklist.isBlacklisted(packageName)) { return mLowRefreshRateId; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 15a44e8ab2f8..63f363e1cc14 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -425,8 +425,7 @@ class Task extends WindowContainer<WindowContainer> { int maxRecents; /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for - * determining the order when restoring. Sign indicates whether last task movement was to front - * (positive) or back (negative). Absolute value indicates time. */ + * determining the order when restoring. */ long mLastTimeMoved; /** If original intent did not allow relinquishing task identity, save that information */ @@ -952,6 +951,11 @@ class Task extends WindowContainer<WindowContainer> { removeImmediately(); if (isLeafTask()) { mAtmService.getTaskChangeNotificationController().notifyTaskRemoved(mTaskId); + + final TaskDisplayArea taskDisplayArea = getDisplayArea(); + if (taskDisplayArea != null) { + taskDisplayArea.onLeafTaskRemoved(mTaskId); + } } } @@ -1525,15 +1529,14 @@ class Task extends WindowContainer<WindowContainer> { mStackSupervisor.updateTopResumedActivityIfNeeded(); } - void updateTaskMovement(boolean toFront) { + void updateTaskMovement(boolean toTop, int position) { + EventLogTags.writeWmTaskMoved(mTaskId, toTop ? 1 : 0, position); + final TaskDisplayArea taskDisplayArea = getDisplayArea(); + if (taskDisplayArea != null && isLeafTask()) { + taskDisplayArea.onLeafTaskMoved(this, toTop); + } if (isPersistable) { mLastTimeMoved = System.currentTimeMillis(); - // Sign is used to keep tasks sorted when persisted. Tasks sent to the bottom most - // recently will be most negative, tasks sent to the bottom before that will be less - // negative. Similarly for recent tasks moved to the top which will be most positive. - if (!toFront) { - mLastTimeMoved *= -1; - } } mRootWindowContainer.invalidateTaskLayers(); } @@ -3180,6 +3183,7 @@ class Task extends WindowContainer<WindowContainer> { @Override void positionChildAt(int position, WindowContainer child, boolean includingParents) { + final boolean toTop = position >= (mChildren.size() - 1); position = getAdjustedChildPosition(child, position); super.positionChildAt(position, child, includingParents); @@ -3187,10 +3191,9 @@ class Task extends WindowContainer<WindowContainer> { if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "positionChildAt: child=" + child + " position=" + position + " parent=" + this); - final int toTop = position >= (mChildren.size() - 1) ? 1 : 0; final Task task = child.asTask(); if (task != null) { - EventLogTags.writeWmTaskMoved(task.mTaskId, toTop, position); + task.updateTaskMovement(toTop, position); } } @@ -5938,31 +5941,12 @@ class Task extends WindowContainer<WindowContainer> { if (shouldSleepOrShutDownActivities() && mLastPausedActivity == next && mRootWindowContainer.allPausedActivitiesComplete()) { - // If the current top activity may be able to occlude keyguard but the occluded state - // has not been set, update visibility and check again if we should continue to resume. - boolean nothingToResume = true; - if (!mAtmService.mShuttingDown) { - final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard - && next.canShowWhenLocked(); - final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next - && next.containsDismissKeyguardWindow(); - - if (canShowWhenLocked || mayDismissKeyguard) { - ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - !PRESERVE_WINDOWS); - nothingToResume = shouldSleepActivities(); - } else if (next.currentLaunchCanTurnScreenOn() && next.canTurnScreenOn()) { - nothingToResume = false; - } - } - if (nothingToResume) { - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - executeAppTransition(options); - if (DEBUG_STATES) Slog.d(TAG_STATES, - "resumeTopActivityLocked: Going to sleep and all paused"); - return false; - } + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + executeAppTransition(options); + if (DEBUG_STATES) Slog.d(TAG_STATES, + "resumeTopActivityLocked: Going to sleep and all paused"); + return false; } // Make sure that the user who owns this activity is started. If not, @@ -6896,9 +6880,6 @@ class Task extends WindowContainer<WindowContainer> { if (!deferResume) { mRootWindowContainer.resumeFocusedStacksTopActivities(); } - EventLogTags.writeWmTaskToFront(tr.mUserId, tr.mTaskId); - mAtmService.getTaskChangeNotificationController() - .notifyTaskMovedToFront(tr.getTaskInfo()); } finally { mDisplayContent.continueUpdateImeTarget(); } @@ -7284,7 +7265,6 @@ class Task extends WindowContainer<WindowContainer> { Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position); } positionChildAt(position, task, includingParents); - task.updateTaskMovement(toTop); getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); @@ -7421,7 +7401,6 @@ class Task extends WindowContainer<WindowContainer> { } positionChildAt(POSITION_TOP, child, true /* includingParents */); - child.updateTaskMovement(true); final DisplayContent displayContent = getDisplayContent(); displayContent.layoutAndAssignWindowLayersIfNeeded(); @@ -7434,7 +7413,6 @@ class Task extends WindowContainer<WindowContainer> { final Task nextFocusableStack = getDisplayArea().getNextFocusableStack( child.getRootTask(), true /* ignoreCurrent */); positionChildAtBottom(child, nextFocusableStack == null /* includingParents */); - child.updateTaskMovement(true); } @VisibleForTesting @@ -7459,12 +7437,6 @@ class Task extends WindowContainer<WindowContainer> { } final boolean isTop = getTopChild() == child; - - final Task task = child.asTask(); - if (task != null) { - task.updateTaskMovement(isTop); - } - if (isTop) { final DisplayContent displayContent = getDisplayContent(); displayContent.layoutAndAssignWindowLayersIfNeeded(); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 2b32e40f7332..890f56e50bac 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -152,6 +153,12 @@ final class TaskDisplayArea extends DisplayArea<Task> { */ private boolean mRemoved; + + /** + * The id of a leaf task that most recently being moved to front. + */ + private int mLastLeafTaskToFrontId; + TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name, int displayAreaFeature) { super(service, Type.ANY, name, displayAreaFeature); @@ -382,7 +389,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { this /* child */, true /* includingParents */); } - child.updateTaskMovement(moveToTop); + child.updateTaskMovement(moveToTop, targetPosition); mDisplayContent.layoutAndAssignWindowLayersIfNeeded(); @@ -405,6 +412,30 @@ final class TaskDisplayArea extends DisplayArea<Task> { } } + void onLeafTaskRemoved(int taskId) { + if (mLastLeafTaskToFrontId == taskId) { + mLastLeafTaskToFrontId = INVALID_TASK_ID; + } + } + + void onLeafTaskMoved(Task t, boolean toTop) { + if (!toTop) { + if (t.mTaskId == mLastLeafTaskToFrontId) { + mLastLeafTaskToFrontId = INVALID_TASK_ID; + } + return; + } + if (t.mTaskId == mLastLeafTaskToFrontId || t.topRunningActivityLocked() == null) { + return; + } + + mLastLeafTaskToFrontId = t.mTaskId; + EventLogTags.writeWmTaskToFront(t.mUserId, t.mTaskId); + // Notifying only when a leak task moved to front. Or the listeners would be notified + // couple times from the leaf task all the way up to the root task. + mAtmService.getTaskChangeNotificationController().notifyTaskMovedToFront(t.getTaskInfo()); + } + @Override boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback, boolean traverseTopToBottom) { diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java index 20af250280f7..a3dc29058f1e 100644 --- a/services/core/java/com/android/server/wm/TaskPersister.java +++ b/services/core/java/com/android/server/wm/TaskPersister.java @@ -197,7 +197,7 @@ public class TaskPersister implements PersisterQueue.Listener { mPersisterQueue.addItem(new TaskWriteQueueItem(task, mService), flush); } } else { - // Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is + // Placeholder. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is // notified. mPersisterQueue.addItem(PersisterQueue.EMPTY_ITEM, flush); } diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 271d2b1a002f..506e0dd508fb 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -20,6 +20,7 @@ import static android.os.Build.IS_USER; import android.graphics.Point; import android.graphics.Rect; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ShellCommand; import android.os.UserHandle; @@ -32,10 +33,13 @@ import android.view.ViewDebug; import com.android.internal.os.ByteTransferPipe; import com.android.internal.protolog.ProtoLogImpl; +import com.android.server.LocalServices; +import com.android.server.statusbar.StatusBarManagerInternal; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -83,7 +87,21 @@ public class WindowManagerShellCommand extends ShellCommand { // trace files can be written. return mInternal.mWindowTracing.onShellCommand(this); case "logging": - return ProtoLogImpl.getSingleInstance().onShellCommand(this); + String[] args = peekRemainingArgs(); + int result = ProtoLogImpl.getSingleInstance().onShellCommand(this); + if (result != 0) { + // Let the shell try and handle this + try (ParcelFileDescriptor pfd + = ParcelFileDescriptor.dup(getOutFileDescriptor())){ + pw.println("Not handled, calling status bar with args: " + + Arrays.toString(args)); + LocalServices.getService(StatusBarManagerInternal.class) + .handleWindowManagerLoggingCommand(args, pfd); + } catch (IOException e) { + pw.println("Failed to handle logging command: " + e.getMessage()); + } + } + return result; case "set-user-rotation": return runSetDisplayUserRotation(pw); case "set-fix-to-user-rotation": diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index d84f9d1a7dad..1b649fda1976 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -37,6 +37,7 @@ cc_library_static { "com_android_server_locksettings_SyntheticPasswordManager.cpp", "com_android_server_net_NetworkStatsService.cpp", "com_android_server_power_PowerManagerService.cpp", + "com_android_server_powerstats_PowerStatsService.cpp", "com_android_server_security_VerityUtils.cpp", "com_android_server_SerialService.cpp", "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp", diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp new file mode 100644 index 000000000000..5eb6b7343945 --- /dev/null +++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2020 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. + */ + +#define LOG_TAG "PowerStatsService" + +#include <android/hardware/power/stats/1.0/IPowerStats.h> +#include <jni.h> +#include <nativehelper/JNIHelp.h> + +#include <log/log.h> + +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::power::stats::V1_0::EnergyData; +using android::hardware::power::stats::V1_0::RailInfo; +using android::hardware::power::stats::V1_0::Status; + +static jclass class_railInfo; +static jmethodID method_railInfoInit; +static jclass class_energyData; +static jmethodID method_energyDataInit; + +namespace android { + +static std::mutex gPowerStatsHalMutex; +static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr; + +static void deinitPowerStats() { + gPowerStatsHalV1_0_ptr = nullptr; +} + +struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient { + virtual void serviceDied(uint64_t cookie, + const wp<android::hidl::base::V1_0::IBase> &who) override { + // The HAL just died. Reset all handles to HAL services. + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + deinitPowerStats(); + } +}; + +sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient(); + +static bool connectToPowerStatsHal() { + if (gPowerStatsHalV1_0_ptr == nullptr) { + gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService(); + + if (gPowerStatsHalV1_0_ptr == nullptr) { + ALOGE("Unable to get power.stats HAL service."); + return false; + } + + // Link death recipient to power.stats service handle + hardware::Return<bool> linked = + gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0); + if (!linked.isOk()) { + ALOGE("Transaction error in linking to power.stats HAL death: %s", + linked.description().c_str()); + deinitPowerStats(); + return false; + } else if (!linked) { + ALOGW("Unable to link to power.stats HAL death notifications"); + return false; + } + } + return true; +} + +static bool checkResult(const Return<void> &ret, const char *function) { + if (!ret.isOk()) { + ALOGE("%s failed: requested HAL service not available. Description: %s", function, + ret.description().c_str()); + if (ret.isDeadObject()) { + deinitPowerStats(); + } + return false; + } + return true; +} + +static jobjectArray nativeGetRailInfo(JNIEnv *env, jclass clazz) { + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + + if (!connectToPowerStatsHal()) { + ALOGE("nativeGetRailInfo failed to connect to power.stats HAL"); + return nullptr; + } + + hidl_vec<RailInfo> list; + Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&list](auto rails, auto status) { + if (status != Status::SUCCESS) { + ALOGW("Rail information is not available"); + } else { + list = std::move(rails); + } + }); + + if (!checkResult(ret, __func__)) { + ALOGE("getRailInfo failed"); + return nullptr; + } else { + jobjectArray railInfoArray = env->NewObjectArray(list.size(), class_railInfo, nullptr); + for (int i = 0; i < list.size(); i++) { + jstring railName = env->NewStringUTF(list[i].railName.c_str()); + jstring subsysName = env->NewStringUTF(list[i].subsysName.c_str()); + jobject railInfo = env->NewObject(class_railInfo, method_railInfoInit, list[i].index, + railName, subsysName, list[i].samplingRate); + env->SetObjectArrayElement(railInfoArray, i, railInfo); + env->DeleteLocalRef(railName); + env->DeleteLocalRef(subsysName); + env->DeleteLocalRef(railInfo); + } + return railInfoArray; + } +} + +static jobjectArray nativeGetEnergyData(JNIEnv *env, jclass clazz) { + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + + if (!connectToPowerStatsHal()) { + ALOGE("nativeGetEnergy failed to connect to power.stats HAL"); + } + + hidl_vec<EnergyData> list; + Return<void> ret = + gPowerStatsHalV1_0_ptr->getEnergyData({}, [&list](auto energyData, auto status) { + if (status != Status::SUCCESS) { + ALOGW("getEnergyData is not supported"); + } else { + list = std::move(energyData); + } + }); + + if (!checkResult(ret, __func__)) { + ALOGE("getEnergyData failed"); + return nullptr; + } else { + jobjectArray energyDataArray = env->NewObjectArray(list.size(), class_energyData, nullptr); + for (int i = 0; i < list.size(); i++) { + jobject energyData = env->NewObject(class_energyData, method_energyDataInit, + list[i].index, list[i].timestamp, list[i].energy); + env->SetObjectArrayElement(energyDataArray, i, energyData); + env->DeleteLocalRef(energyData); + } + return energyDataArray; + } +} + +static jboolean nativeInit(JNIEnv *env, jclass clazz) { + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + + jclass temp = env->FindClass("com/android/server/powerstats/PowerStatsData$RailInfo"); + if (temp == nullptr) return false; + + class_railInfo = (jclass)env->NewGlobalRef(temp); + if (class_railInfo == nullptr) return false; + + method_railInfoInit = + env->GetMethodID(class_railInfo, "<init>", "(JLjava/lang/String;Ljava/lang/String;J)V"); + if (method_railInfoInit == nullptr) return false; + + temp = env->FindClass("com/android/server/powerstats/PowerStatsData$EnergyData"); + if (temp == nullptr) return false; + + class_energyData = (jclass)env->NewGlobalRef(temp); + if (class_energyData == nullptr) return false; + + method_energyDataInit = env->GetMethodID(class_energyData, "<init>", "(JJJ)V"); + if (method_energyDataInit == nullptr) return false; + + bool rv = true; + + if (!connectToPowerStatsHal()) { + ALOGE("nativeInit failed to connect to power.stats HAL"); + rv = false; + } else { + Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&rv](auto rails, auto status) { + if (status != Status::SUCCESS) { + ALOGE("nativeInit RailInfo is unavailable"); + rv = false; + } + }); + + ret = gPowerStatsHalV1_0_ptr->getEnergyData({}, [&rv](auto energyData, auto status) { + if (status != Status::SUCCESS) { + ALOGE("nativeInit EnergyData is unavailable"); + rv = false; + } + }); + } + + return rv; +} + +static const JNINativeMethod method_table[] = { + {"nativeInit", "()Z", (void *)nativeInit}, + {"nativeGetRailInfo", "()[Lcom/android/server/powerstats/PowerStatsData$RailInfo;", + (void *)nativeGetRailInfo}, + {"nativeGetEnergyData", "()[Lcom/android/server/powerstats/PowerStatsData$EnergyData;", + (void *)nativeGetEnergyData}, +}; + +int register_android_server_PowerStatsService(JNIEnv *env) { + return jniRegisterNativeMethods(env, + "com/android/server/powerstats/" + "PowerStatsHALWrapper$PowerStatsHALWrapperImpl", + method_table, NELEM(method_table)); +} + +}; // namespace android diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp index 46e6f912edb0..dda44fb72cfc 100644 --- a/services/core/jni/com_android_server_security_VerityUtils.cpp +++ b/services/core/jni/com_android_server_security_VerityUtils.cpp @@ -78,7 +78,7 @@ int statxForFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath) { return -errno; } - // Sanity check. + // Validity check. if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) { ALOGE("Unexpected, STATX_ATTR_VERITY not supported by kernel"); return -ENOSYS; diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 5df1adafed5e..e7f6db959060 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -29,6 +29,7 @@ int register_android_server_ConsumerIrService(JNIEnv *env); int register_android_server_InputManager(JNIEnv* env); int register_android_server_LightsService(JNIEnv* env); int register_android_server_PowerManagerService(JNIEnv* env); +int register_android_server_PowerStatsService(JNIEnv* env); int register_android_server_storage_AppFuse(JNIEnv* env); int register_android_server_SerialService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); @@ -82,6 +83,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_broadcastradio_BroadcastRadioService(env); register_android_server_broadcastradio_Tuner(vm, env); register_android_server_PowerManagerService(env); + register_android_server_PowerStatsService(env); register_android_server_SerialService(env); register_android_server_InputManager(env); register_android_server_LightsService(env); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 4b9fab4d8ed6..183a1495b075 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5745,14 +5745,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent) { + public void wipeDataWithReason(int flags, String wipeReasonForUser, + boolean calledOnParentInstance) { if (!mHasFeature) { return; } final CallerIdentity caller = getCallerIdentity(); boolean calledByProfileOwnerOnOrgOwnedDevice = isProfileOwnerOfOrganizationOwnedDevice(caller); - if (parent) { + if (calledOnParentInstance) { Preconditions.checkCallAuthorization(calledByProfileOwnerOnOrgOwnedDevice, "Wiping the entire device can only be done by a profile owner on " + "organization-owned device."); @@ -5772,7 +5773,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String.format("No active admin for user %d", caller.getUserId())); if (TextUtils.isEmpty(wipeReasonForUser)) { - if (calledByProfileOwnerOnOrgOwnedDevice && !parent) { + if (calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance) { wipeReasonForUser = mContext.getString(R.string.device_ownership_relinquished); } else { wipeReasonForUser = mContext.getString( @@ -5783,7 +5784,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { int userId = admin.getUserHandle().getIdentifier(); if (calledByProfileOwnerOnOrgOwnedDevice) { // When wipeData is called on the parent instance, it implies wiping the entire device. - if (parent) { + if (calledOnParentInstance) { userId = UserHandle.USER_SYSTEM; } else { // when wipeData is _not_ called on the parent instance, it implies relinquishing @@ -5808,7 +5809,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON) .setAdmin(admin.info.getComponent()) .setInt(flags) - .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT) + .setStrings(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT) .write(); String internalReason = String.format( "DevicePolicyManager.wipeDataWithReason() from %s, organization-owned? %s", @@ -7866,15 +7867,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } synchronized (getLockObject()) { + final ComponentName doComponent = mOwners.getDeviceOwnerComponent(); + final ComponentName poComponent = + mOwners.getProfileOwnerComponent(userHandle.getIdentifier()); + // Return test only admin by default. + if (isAdminTestOnlyLocked(doComponent, userHandle.getIdentifier())) { + return doComponent; + } else if (isAdminTestOnlyLocked(poComponent, userHandle.getIdentifier())) { + return poComponent; + } final String supervisor = mContext.getResources().getString( com.android.internal.R.string.config_defaultSupervisionProfileOwnerComponent); if (supervisor == null) { return null; } final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor); - final ComponentName doComponent = mOwners.getDeviceOwnerComponent(); - final ComponentName poComponent = - mOwners.getProfileOwnerComponent(userHandle.getIdentifier()); if (supervisorComponent.equals(doComponent) || supervisorComponent.equals( poComponent)) { return supervisorComponent; @@ -9527,8 +9534,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { && UserRestrictionsUtils.canProfileOwnerChange(key, userHandle); boolean orgOwnedProfileOwnerCanChangesGlobally = parent && isProfileOwnerOfOrganizationOwnedDevice(caller) - && UserRestrictionsUtils - .canProfileOwnerOfOrganizationOwnedDeviceChange(key); + && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange( + key); if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangesGlobally) { throw new SecurityException("Profile owner cannot set user restriction " + key); @@ -10224,6 +10231,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new SecurityException( "User " + userId + " is not allowed to call setSecondaryLockscreenEnabled"); } + synchronized (getLockObject()) { + if (isAdminTestOnlyLocked(who, userId)) { + // Allow testOnly admins to bypass supervision config requirement. + return; + } + } // Only the default supervision app can use this API. final String supervisor = mContext.getResources().getString( com.android.internal.R.string.config_defaultSupervisionProfileOwnerComponent); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 97ae505b4fcf..ddd23778884a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -150,6 +150,7 @@ import com.android.server.policy.role.LegacyRoleResolutionPolicy; import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; import com.android.server.power.ThermalManagerService; +import com.android.server.powerstats.PowerStatsService; import com.android.server.profcollect.ProfcollectForwardingService; import com.android.server.recoverysystem.RecoverySystemService; import com.android.server.restrictions.RestrictionsManagerService; @@ -761,6 +762,11 @@ public final class SystemServer { mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class); t.traceEnd(); + t.traceBegin("StartPowerStatsService"); + // Tracks rail data to be used for power statistics. + mSystemServiceManager.startService(PowerStatsService.class); + t.traceEnd(); + // Activity manager runs the show. t.traceBegin("StartActivityManager"); // TODO: Might need to move after migration to WM. diff --git a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java index 7672cd0040ec..c03a5a7f4bec 100644 --- a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java +++ b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java @@ -129,7 +129,7 @@ abstract class AbstractProtoDiskReadWriter<T> { if (files == null || files.length == 0) { return null; } else if (files.length > 1) { - // This can't possibly happen, but sanity check. + // This can't possibly happen, but validity check. Slog.w(TAG, "Found multiple files with the same name: " + Arrays.toString(files)); } return parseFile(files[0]); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java new file mode 100644 index 000000000000..04e8b634e72e --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2020 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.am; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManagerInternal; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; +import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.server.LocalServices; +import com.android.server.ServiceThread; +import com.android.server.am.ActivityManagerService.Injector; +import com.android.server.appop.AppOpsService; +import com.android.server.wm.ActivityTaskManagerService; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.quality.Strictness; + +import java.io.File; + +@Presubmit +public class AppChildProcessTest { + private static final String TAG = AppChildProcessTest.class.getSimpleName(); + + @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); + @Mock private AppOpsService mAppOpsService; + @Mock private PackageManagerInternal mPackageManagerInt; + private StaticMockitoSession mMockitoSession; + + private Context mContext = getInstrumentation().getTargetContext(); + private TestInjector mInjector; + private ActivityManagerService mAms; + private ProcessList mProcessList; + private PhantomProcessList mPhantomProcessList; + private Handler mHandler; + private HandlerThread mHandlerThread; + + @BeforeClass + public static void setUpOnce() { + System.setProperty("dexmaker.share_classloader", "true"); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mMockitoSession = mockitoSession() + .spyStatic(Process.class) + .strictness(Strictness.LENIENT) + .startMocking(); + + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + final ProcessList pList = new ProcessList(); + mProcessList = spy(pList); + + mInjector = new TestInjector(mContext); + mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread()); + mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext); + mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); + mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal()); + mAms.mPackageManagerInt = mPackageManagerInt; + pList.mService = mAms; + mPhantomProcessList = mAms.mPhantomProcessList; + doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent(); + doReturn(false).when(() -> Process.supportsPidFd()); + // Remove stale instance of PackageManagerInternal if there is any + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(PackageManagerInternal.class); + mMockitoSession.finishMocking(); + mHandlerThread.quit(); + } + + @Test + public void testManageAppChildProcesses() throws Exception { + final int initPid = 1; + final int rootUid = 0; + final int zygote64Pid = 100; + final int zygote32Pid = 101; + final int app1Pid = 200; + final int app2Pid = 201; + final int app1Uid = 10000; + final int app2Uid = 10001; + final int child1Pid = 300; + final int child2Pid = 301; + final int nativePid = 400; + final String zygote64ProcessName = "zygote64"; + final String zygote32ProcessName = "zygote32"; + final String app1ProcessName = "test1"; + final String app2ProcessName = "test2"; + final String child1ProcessName = "test1_child1"; + final String child2ProcessName = "test1_child1_child2"; + final String nativeProcessName = "test_native"; + + makeParent(zygote64Pid, initPid); + makeParent(zygote32Pid, initPid); + + makeAppProcess(app1Pid, app1Uid, app1ProcessName, app1ProcessName); + makeParent(app1Pid, zygote64Pid); + makeAppProcess(app2Pid, app2Uid, app2ProcessName, app2ProcessName); + makeParent(app2Pid, zygote64Pid); + + assertEquals(0, mPhantomProcessList.mPhantomProcesses.size()); + + // Verify zygote itself isn't a phantom process + assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked( + zygote64ProcessName, rootUid, zygote64Pid)); + assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked( + zygote32ProcessName, rootUid, zygote32Pid)); + // Verify none of the app isn't a phantom process + assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked( + app1ProcessName, app1Uid, app1Pid)); + assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked( + app2ProcessName, app2Uid, app2Pid)); + + // "Fork" an app child process + makeParent(child1Pid, app1Pid); + PhantomProcessRecord pr = mPhantomProcessList + .getOrCreatePhantomProcessIfNeededLocked(child1ProcessName, app1Uid, child1Pid); + assertTrue(pr != null); + assertEquals(1, mPhantomProcessList.mPhantomProcesses.size()); + assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0)); + verifyPhantomProcessRecord(pr, child1ProcessName, app1Uid, child1Pid); + + // Create another native process from init + makeParent(nativePid, initPid); + assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked( + nativeProcessName, rootUid, nativePid)); + assertEquals(1, mPhantomProcessList.mPhantomProcesses.size()); + assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0)); + + // "Fork" another app child process + makeParent(child2Pid, child1Pid); + PhantomProcessRecord pr2 = mPhantomProcessList + .getOrCreatePhantomProcessIfNeededLocked(child2ProcessName, app1Uid, child2Pid); + assertTrue(pr2 != null); + assertEquals(2, mPhantomProcessList.mPhantomProcesses.size()); + verifyPhantomProcessRecord(pr2, child2ProcessName, app1Uid, child2Pid); + + ArraySet<PhantomProcessRecord> set = new ArraySet<>(); + set.add(pr); + set.add(pr2); + for (int i = mPhantomProcessList.mPhantomProcesses.size() - 1; i >= 0; i--) { + set.remove(mPhantomProcessList.mPhantomProcesses.valueAt(i)); + } + assertEquals(0, set.size()); + } + + private void verifyPhantomProcessRecord(PhantomProcessRecord pr, + String processName, int uid, int pid) { + assertEquals(processName, pr.mProcessName); + assertEquals(uid, pr.mUid); + assertEquals(pid, pr.mPid); + } + + private void makeAppProcess(int pid, int uid, String packageName, String processName) { + ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = packageName; + ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid); + app.pid = pid; + mAms.mPidsSelfLocked.doAddInternal(app); + } + + private void makeParent(int pid, int ppid) { + doReturn(ppid).when(() -> Process.getParentPid(eq(pid))); + } + + private class TestInjector extends Injector { + TestInjector(Context context) { + super(context); + } + + @Override + public AppOpsService getAppOpsService(File file, Handler handler) { + return mAppOpsService; + } + + @Override + public Handler getUiHandler(ActivityManagerService service) { + return mHandler; + } + + @Override + public ProcessList getProcessList(ActivityManagerService service) { + return mProcessList; + } + } + + static class ServiceThreadRule implements TestRule { + private ServiceThread mThread; + + ServiceThread getThread() { + return mThread; + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + mThread = new ServiceThread("TestServiceThread", + Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */); + mThread.start(); + try { + base.evaluate(); + } finally { + mThread.getThreadHandler().runWithScissors(mThread::quit, 0 /* timeout */); + } + } + }; + } + } + +} diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java index 8db09b4f156c..e9a50b36693a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -120,6 +120,7 @@ public class ApplicationExitInfoTest { mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); mProcessList = spy(new ProcessList()); + ProcessList.sKillHandler = null; mAppExitInfoTracker = spy(new AppExitInfoTracker()); setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mIsolatedUidRecords", spy(mAppExitInfoTracker.new IsolatedUidRecords())); @@ -147,6 +148,7 @@ public class ApplicationExitInfoTest { public void tearDown() { LocalServices.removeServiceForTest(PackageManagerInternal.class); mHandlerThread.quit(); + ProcessList.sKillHandler = null; } private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index e724e60b3ae5..7f86faa4b393 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -364,7 +364,7 @@ public class ConnectivityControllerTest { .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean()); assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED)); assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE)); - // Whitelisting doesn't need to be requested again. + // Allowlisting doesn't need to be requested again. controller.requestStandbyExceptionLocked(red); inOrder.verify(mNetPolicyManagerInternal, never()) .setAppIdleWhitelist(eq(UID_RED), anyBoolean()); @@ -434,7 +434,7 @@ public class ConnectivityControllerTest { .setAppIdleWhitelist(eq(UID_BLUE), anyBoolean()); assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED)); assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE)); - // Whitelisting doesn't need to be requested again. + // Allowlisting doesn't need to be requested again. controller.evaluateStateLocked(red); inOrder.verify(mNetPolicyManagerInternal, never()) .setAppIdleWhitelist(eq(UID_RED), anyBoolean()); @@ -473,7 +473,7 @@ public class ConnectivityControllerTest { assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED)); assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE)); - // Test that a currently whitelisted uid is now removed. + // Test that a currently allowlisted uid is now removed. controller.requestStandbyExceptionLocked(blue); inOrder.verify(mNetPolicyManagerInternal, times(1)) .setAppIdleWhitelist(eq(UID_BLUE), eq(true)); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index d8874e40a760..1cf133a2bbf2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -302,7 +302,7 @@ public class QuotaControllerTest { // Make sure tests aren't passing just because the default bucket is likely ACTIVE. js.setStandbyBucket(FREQUENT_INDEX); // Make sure Doze and background-not-restricted don't affect tests. - js.setDeviceNotDozingConstraintSatisfied(/* state */ true, /* whitelisted */false); + js.setDeviceNotDozingConstraintSatisfied(/* state */ true, /* allowlisted */false); js.setBackgroundNotRestrictedConstraintSatisfied(true); return js; } diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java index f2246dac01ca..a0f48c674316 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java @@ -148,7 +148,7 @@ public class LocationFudgerTest { // accurate the coarsened average will be. we use 70% as a lower bound by -very- roughly // taking the area within a grid where we expect a reasonable percentage of points generated // by step() to fall in another grid square. this likely doesn't have much mathematical - // validity, but it serves as a sanity test as least. + // validity, but it serves as a validity test as least. assertThat(passed / (double) iterations).isGreaterThan(.70); } diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index 80ad0a838bbb..c36cdeb582bd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -24,7 +24,6 @@ import static android.app.AppOpsManager.OP_MONITOR_LOCATION; import static android.location.Criteria.ACCURACY_COARSE; import static android.location.Criteria.ACCURACY_FINE; import static android.location.Criteria.POWER_HIGH; -import static android.location.LocationManager.PASSIVE_PROVIDER; import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; import static androidx.test.ext.truth.location.LocationSubject.assertThat; @@ -356,8 +355,7 @@ public class LocationProviderManagerTest { @Test public void testPassive_Listener() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0, - 0, false); + LocationRequest request = new LocationRequest.Builder(0).build(); mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); @@ -382,8 +380,7 @@ public class LocationProviderManagerTest { ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest( - LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), IDENTITY, + mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); @@ -437,8 +434,7 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest( - LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity, + mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); @@ -451,8 +447,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_Unregister() throws Exception { ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest( - LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), IDENTITY, + mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY, PERMISSION_FINE, listener); mManager.unregisterLocationRequest(listener); @@ -470,8 +465,7 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest( - LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity, + mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, PERMISSION_FINE, listener); CountDownLatch blocker = new CountDownLatch(1); @@ -493,8 +487,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_NumUpdates() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false).setNumUpdates(5); + LocationRequest request = new LocationRequest.Builder(0).setMaxUpdates(5).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -511,8 +504,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_ExpiringAlarm() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false).setExpireIn(5000); + LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(5000).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); long baseTimeMs = SystemClock.elapsedRealtime(); @@ -535,8 +527,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_ExpiringNoAlarm() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false).setExpireIn(25); + LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(25).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Thread.sleep(25); @@ -547,22 +538,10 @@ public class LocationProviderManagerTest { } @Test - public void testRegisterListener_AlreadyExpired() throws Exception { - ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false).setExpireIn(-1); - mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); - - mProvider.setProviderLocation(createLocation(NAME, mRandom)); - verify(listener, never()).onLocationChanged(any(Location.class), - nullable(IRemoteCallback.class)); - } - - @Test public void testRegisterListener_FastestInterval() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5000, 0, - false).setFastestInterval(5000); + LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateIntervalMillis( + 5000).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -575,8 +554,8 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_SmallestDisplacement() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5000, 0, - false).setSmallestDisplacement(1f); + LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateDistanceMeters( + 1f).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); @@ -590,7 +569,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_NoteOpFailure() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + LocationRequest request = new LocationRequest.Builder(0).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(), @@ -608,8 +587,7 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest( - LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity, + mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, PERMISSION_FINE, listener); CountDownLatch blocker = new CountDownLatch(1); @@ -637,8 +615,7 @@ public class LocationProviderManagerTest { ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false); + LocationRequest locationRequest = new LocationRequest.Builder(0).build(); ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, cancellationSignal, listener); @@ -654,8 +631,7 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_Cancel() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false); + LocationRequest locationRequest = new LocationRequest.Builder(0).build(); ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, cancellationSignal, listener); @@ -669,8 +645,7 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_ProviderDisabled() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false); + LocationRequest locationRequest = new LocationRequest.Builder(0).build(); ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, cancellationSignal, listener); @@ -686,8 +661,7 @@ public class LocationProviderManagerTest { mProvider.setProviderAllowed(false); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false); + LocationRequest locationRequest = new LocationRequest.Builder(0).build(); ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, cancellationSignal, listener); @@ -705,8 +679,7 @@ public class LocationProviderManagerTest { mProvider.setProviderLocation(loc); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false); + LocationRequest locationRequest = new LocationRequest.Builder(0).build(); ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, cancellationSignal, listener); @@ -718,8 +691,7 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_Timeout() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false); + LocationRequest locationRequest = new LocationRequest.Builder(0).build(); ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, cancellationSignal, listener); @@ -742,7 +714,7 @@ public class LocationProviderManagerTest { IDENTITY.getPackageName())).isFalse(); ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + LocationRequest request = new LocationRequest.Builder(0).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION, @@ -771,7 +743,7 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().locationRequests).isEmpty(); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false); + LocationRequest request1 = new LocationRequest.Builder(5).build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().reportLocation).isTrue(); @@ -782,8 +754,7 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().workSource).isNotNull(); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, - false).setLowPowerMode(true); + LocationRequest request2 = new LocationRequest.Builder(1).setLowPower(true).build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); assertThat(mProvider.getRequest().reportLocation).isTrue(); @@ -811,7 +782,7 @@ public class LocationProviderManagerTest { @Test public void testProviderRequest_BackgroundThrottle() { ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false); + LocationRequest request1 = new LocationRequest.Builder(5).build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().interval).isEqualTo(5); @@ -827,7 +798,7 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false); + LocationRequest request1 = new LocationRequest.Builder(5).build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().reportLocation).isTrue(); @@ -835,8 +806,8 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse(); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, - false).setLocationSettingsIgnored(true); + LocationRequest request2 = new LocationRequest.Builder(1).setLocationSettingsIgnored( + true).build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); assertThat(mProvider.getRequest().reportLocation).isTrue(); @@ -850,12 +821,12 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, false); + LocationRequest request1 = new LocationRequest.Builder(1).build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, - false).setLocationSettingsIgnored(true); + LocationRequest request2 = new LocationRequest.Builder(5).setLocationSettingsIgnored( + true).build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId()); @@ -872,8 +843,8 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, - false).setLocationSettingsIgnored(true); + LocationRequest request = new LocationRequest.Builder(1).setLocationSettingsIgnored( + true).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet()); @@ -889,8 +860,8 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, - false).setLocationSettingsIgnored(true); + LocationRequest request1 = new LocationRequest.Builder(5).setLocationSettingsIgnored( + true).build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().interval).isEqualTo(5); @@ -905,7 +876,7 @@ public class LocationProviderManagerTest { LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF); ILocationListener listener = createMockLocationListener(); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false); + LocationRequest request = new LocationRequest.Builder(5).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); assertThat(mProvider.getRequest().reportLocation).isTrue(); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java index 69a9f4415fe7..0b5a6998cd0d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java @@ -43,7 +43,6 @@ import org.mockito.InOrder; import java.util.Collection; import java.util.function.Consumer; -import java.util.function.Predicate; @SuppressWarnings("unchecked") @Presubmit @@ -355,10 +354,6 @@ public class ListenerMultiplexerTest { removeRegistration(consumer, registration); } - public void removeListenerIf(Predicate<Consumer<TestListenerRegistration>> predicate) { - removeRegistrationIf(predicate); - } - public void setActive(Integer request, boolean active) { updateRegistrations(testRegistration -> { if (testRegistration.getRequest().equals(request)) { diff --git a/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml b/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml index 3b8b3b1af9b5..fcab92b84879 100644 --- a/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml +++ b/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="dummy">dummy</string> </resources> diff --git a/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java b/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java index 10b9e7cf1218..90df786e5970 100644 --- a/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java +++ b/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java @@ -50,14 +50,14 @@ public class CertBlacklisterTest extends AndroidTestCase { public void testClearBlacklistPubkey() throws Exception { // clear the gservices setting for a clean slate overrideSettings(PUBKEY_KEY, ""); - // read the contents of the pubkey blacklist + // read the contents of the pubkey denylist String blacklist = IoUtils.readFileAsString(PUBKEY_PATH); // Verify that it's empty assertEquals("", blacklist); } public void testSetBlacklistPubkey() throws Exception { - // build a new thing to blacklist + // build a new thing to denylist String badPubkey = "7ccabd7db47e94a5759901b6a7dfd45d1c091ccc"; // add the gservices override overrideSettings(PUBKEY_KEY, badPubkey); @@ -110,14 +110,14 @@ public class CertBlacklisterTest extends AndroidTestCase { public void testClearBlacklistSerial() throws Exception { // clear the gservices setting for a clean slate overrideSettings(SERIAL_KEY, ""); - // read the contents of the pubkey blacklist + // read the contents of the pubkey denylist String blacklist = IoUtils.readFileAsString(SERIAL_PATH); // Verify that it's empty assertEquals("", blacklist); } public void testSetBlacklistSerial() throws Exception { - // build a new thing to blacklist + // build a new thing to denylist String badSerial = "22e514121e61c643b1e9b06bd4b9f7d0"; // add the gservices override overrideSettings(SERIAL_KEY, badSerial); diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java index ebbebcb02923..33ea1d6f829e 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java @@ -81,14 +81,14 @@ public class NetworkManagementInternalTest { mNmi.isNetworkRestrictedForUid(TEST_UID)); mInjector.reset(); - // Data saver is on and uid is not whitelisted + // Data saver is on and uid is not allowlisted mInjector.setDataSaverMode(true); mInjector.setUidOnMeteredNetworkList(false, TEST_UID, false); assertTrue("Should be true since data saver is on and the uid is not whitelisted", mNmi.isNetworkRestrictedForUid(TEST_UID)); mInjector.reset(); - // Data saver is on and uid is whitelisted + // Data saver is on and uid is allowlisted mInjector.setDataSaverMode(true); mInjector.setUidOnMeteredNetworkList(false, TEST_UID, true); assertFalse("Should be false since data saver is on and the uid is whitelisted", diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java index 8871348d0027..9a5c6337824a 100644 --- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java @@ -176,7 +176,7 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase { } /** - * Sends dummy widget updates to {@link #mManager}. + * Sends placeholder widget updates to {@link #mManager}. * @param widgetId widget to update * @param viewIds a list of view ids for which * {@link AppWidgetManager#notifyAppWidgetViewDataChanged} will be called diff --git a/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java b/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java index 803119f4c5fa..fd99b21c9538 100644 --- a/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java +++ b/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java @@ -21,7 +21,7 @@ import android.content.Context; import android.content.Intent; /** - * Dummy widget for testing + * Placeholder widget for testing */ public class DummyAppWidget extends BroadcastReceiver { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java index 94cad1ed18b8..cc1fdab3d22a 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java @@ -83,7 +83,7 @@ public class AuthServiceTest { public void setUp() { MockitoAnnotations.initMocks(this); - // Dummy test config + // Placeholder test config final String[] config = { "0:2:15", // ID0:Fingerprint:Strong "1:4:15", // ID1:Iris:Strong 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 8759077ea0ad..4ce6411dc306 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4392,6 +4392,16 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testSecondaryLockscreen_nonSupervisionApp() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_UID; + // Ensure packages are *not* flagged as test_only. + doReturn(new ApplicationInfo()).when(getServices().ipackageManager).getApplicationInfo( + eq(admin1.getPackageName()), + anyInt(), + eq(CALLER_USER_HANDLE)); + doReturn(new ApplicationInfo()).when(getServices().ipackageManager).getApplicationInfo( + eq(admin2.getPackageName()), + anyInt(), + eq(CALLER_USER_HANDLE)); + // Initial state is disabled. assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))); @@ -5138,7 +5148,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; assertTrue(dpms.isNotificationListenerServicePermitted(packageName, userId)); - // Attempt to set to empty list (which means no listener is whitelisted) + // Attempt to set to empty list (which means no listener is allowlisted) mContext.binder.callingUid = adminUid; assertFalse(dpms.setPermittedCrossProfileNotificationListeners( admin1, Collections.emptyList())); @@ -5212,7 +5222,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertTrue(dpms.isNotificationListenerServicePermitted( systemListener, MANAGED_PROFILE_USER_ID)); - // Setting only one package in the whitelist + // Setting only one package in the allowlist mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; assertTrue(dpms.setPermittedCrossProfileNotificationListeners( admin1, Collections.singletonList(permittedListener))); @@ -5226,11 +5236,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { permittedListener, MANAGED_PROFILE_USER_ID)); assertFalse(dpms.isNotificationListenerServicePermitted( notPermittedListener, MANAGED_PROFILE_USER_ID)); - // System packages are always allowed (even if not in the whitelist) + // System packages are always allowed (even if not in the allowlist) assertTrue(dpms.isNotificationListenerServicePermitted( systemListener, MANAGED_PROFILE_USER_ID)); - // Setting an empty whitelist - only system listeners allowed + // Setting an empty allowlist - only system listeners allowed mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; assertTrue(dpms.setPermittedCrossProfileNotificationListeners( admin1, Collections.emptyList())); @@ -5241,11 +5251,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { permittedListener, MANAGED_PROFILE_USER_ID)); assertFalse(dpms.isNotificationListenerServicePermitted( notPermittedListener, MANAGED_PROFILE_USER_ID)); - // System packages are always allowed (even if not in the whitelist) + // System packages are always allowed (even if not in the allowlist) assertTrue(dpms.isNotificationListenerServicePermitted( systemListener, MANAGED_PROFILE_USER_ID)); - // Setting a null whitelist - all listeners allowed + // Setting a null allowlist - all listeners allowed mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; assertTrue(dpms.setPermittedCrossProfileNotificationListeners(admin1, null)); assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1)); @@ -5293,7 +5303,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertTrue(dpms.isNotificationListenerServicePermitted( systemListener, UserHandle.USER_SYSTEM)); - // Setting an empty whitelist - only system listeners allowed in managed profile, but + // Setting an empty allowlist - only system listeners allowed in managed profile, but // all allowed in primary profile mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; assertTrue(dpms.setPermittedCrossProfileNotificationListeners( diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java index e8e6dedaa258..df672c9f248d 100644 --- a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java @@ -294,7 +294,7 @@ public class AmbientBrightnessStatsTrackerTest { AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker(); ArrayList<AmbientBrightnessDayStats> userStats; float[] expectedStats; - // Generate some dummy data + // Generate some placeholder data // Data: very old which should not be read statsTracker.start(); statsTracker.add(0, 0.05f); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java index 48dda019f570..ae966aaf2b58 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -466,7 +466,7 @@ public class BrightnessMappingStrategyTest { Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); - // Let's start with a sanity check: + // Let's start with a validity check: assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */); @@ -494,7 +494,7 @@ public class BrightnessMappingStrategyTest { Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); - // Sanity check: + // Validity check: assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */); @@ -540,7 +540,7 @@ public class BrightnessMappingStrategyTest { Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); - // Sanity, as per tradition: + // Validity, as per tradition: assertEquals(y0, strategy.getBrightness(x0), 0.01f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); assertEquals(y4, strategy.getBrightness(x4), 0.01f /* tolerance */); diff --git a/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java b/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java index d438a0eb9411..3ecff91d8f96 100644 --- a/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java @@ -448,7 +448,7 @@ public class EmergencyAffordanceServiceTest { List<SubscriptionInfo> subInfos = new ArrayList<>(2); // Test with Multiple SIMs. SIM1 is a non-EA SIM - // Only country iso is valuable, all other info are filled with dummy values + // Only country iso is valuable, all other info are filled with placeholder values SubscriptionInfo subInfo = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "226", NON_EMERGENCY_ISO_CODE, false, null, null); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java index f72dbf6ed0b0..d8f5c4c8f58c 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java @@ -17,6 +17,9 @@ package com.android.server.hdmi; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import android.platform.test.annotations.Presubmit; import android.util.Slog; @@ -145,4 +148,91 @@ public class HdmiUtilsTest { assertThat(config).isEqualTo(expectedConfig); } + + @Test + public void isAffectingActiveRoutingPath() { + // New path alters the parent + assertTrue(HdmiUtils.isAffectingActiveRoutingPath(0x1100, 0x2000)); + // New path is a sibling + assertTrue(HdmiUtils.isAffectingActiveRoutingPath(0x1100, 0x1200)); + // New path is the descendant of a sibling + assertFalse(HdmiUtils.isAffectingActiveRoutingPath(0x1100, 0x1210)); + // In a completely different path + assertFalse(HdmiUtils.isAffectingActiveRoutingPath(0x1000, 0x3200)); + } + + @Test + public void isInActiveRoutingPath() { + // New path is a parent + assertTrue(HdmiUtils.isInActiveRoutingPath(0x1100, 0x1000)); + // New path is a descendant + assertTrue(HdmiUtils.isInActiveRoutingPath(0x1210, 0x1212)); + // New path is a sibling + assertFalse(HdmiUtils.isInActiveRoutingPath(0x1100, 0x1200)); + // In a completely different path + assertFalse(HdmiUtils.isInActiveRoutingPath(0x1000, 0x2000)); + } + + @Test + public void pathRelationship_unknown() { + assertThat(HdmiUtils.pathRelationship(0x1234, Constants.INVALID_PHYSICAL_ADDRESS)) + .isEqualTo(Constants.PATH_RELATIONSHIP_UNKNOWN); + assertThat(HdmiUtils.pathRelationship(Constants.INVALID_PHYSICAL_ADDRESS, 0x1234)) + .isEqualTo(Constants.PATH_RELATIONSHIP_UNKNOWN); + assertThat(HdmiUtils.pathRelationship(Constants.INVALID_PHYSICAL_ADDRESS, + Constants.INVALID_PHYSICAL_ADDRESS)) + .isEqualTo(Constants.PATH_RELATIONSHIP_UNKNOWN); + } + + @Test + public void pathRelationship_differentBranch() { + assertThat(HdmiUtils.pathRelationship(0x1200, 0x2000)) + .isEqualTo(Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH); + assertThat(HdmiUtils.pathRelationship(0x1234, 0x1224)) + .isEqualTo(Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH); + assertThat(HdmiUtils.pathRelationship(0x1234, 0x1134)) + .isEqualTo(Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH); + assertThat(HdmiUtils.pathRelationship(0x1234, 0x2234)) + .isEqualTo(Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH); + } + + @Test + public void pathRelationship_ancestor() { + assertThat(HdmiUtils.pathRelationship(0x0000, 0x1230)) + .isEqualTo(Constants.PATH_RELATIONSHIP_ANCESTOR); + assertThat(HdmiUtils.pathRelationship(0x1000, 0x1230)) + .isEqualTo(Constants.PATH_RELATIONSHIP_ANCESTOR); + assertThat(HdmiUtils.pathRelationship(0x1200, 0x1230)) + .isEqualTo(Constants.PATH_RELATIONSHIP_ANCESTOR); + } + + @Test + public void pathRelationship_descendant() { + assertThat(HdmiUtils.pathRelationship(0x1230, 0x0000)) + .isEqualTo(Constants.PATH_RELATIONSHIP_DESCENDANT); + assertThat(HdmiUtils.pathRelationship(0x1230, 0x1000)) + .isEqualTo(Constants.PATH_RELATIONSHIP_DESCENDANT); + assertThat(HdmiUtils.pathRelationship(0x1230, 0x1200)) + .isEqualTo(Constants.PATH_RELATIONSHIP_DESCENDANT); + } + + @Test + public void pathRelationship_sibling() { + assertThat(HdmiUtils.pathRelationship(0x1000, 0x2000)) + .isEqualTo(Constants.PATH_RELATIONSHIP_SIBLING); + assertThat(HdmiUtils.pathRelationship(0x1200, 0x1100)) + .isEqualTo(Constants.PATH_RELATIONSHIP_SIBLING); + assertThat(HdmiUtils.pathRelationship(0x1230, 0x1220)) + .isEqualTo(Constants.PATH_RELATIONSHIP_SIBLING); + assertThat(HdmiUtils.pathRelationship(0x1234, 0x1233)) + .isEqualTo(Constants.PATH_RELATIONSHIP_SIBLING); + } + + @Test + public void pathRelationship_same() { + assertThat(HdmiUtils.pathRelationship(0x0000, 0x0000)) + .isEqualTo(Constants.PATH_RELATIONSHIP_SAME); + assertThat(HdmiUtils.pathRelationship(0x1234, 0x1234)) + .isEqualTo(Constants.PATH_RELATIONSHIP_SAME); + } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java index 0488745c2434..441cd4b47c94 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java @@ -178,7 +178,7 @@ public class RuleEvaluationEngineTest { .isEqualTo(IntegrityCheckResult.Effect.DENY); } - /** Returns a builder with all fields filled with some dummy data. */ + /** Returns a builder with all fields filled with some placeholder data. */ private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { return new AppInstallMetadata.Builder() .setPackageName("abc") diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index cae7b57580bb..deaeb46c4074 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -505,7 +505,7 @@ public class JobStoreTest { first.getTransientExtras().toString(), second.getTransientExtras().toString()); // Since people can forget to add tests here for new fields, do one last - // sanity check based on bits-on-wire equality. + // validity check based on bits-on-wire equality. final byte[] firstBytes = marshall(first); final byte[] secondBytes = marshall(second); if (!Arrays.equals(firstBytes, secondBytes)) { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index 7d3ec030e7a6..a38745f2a66e 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -406,7 +406,7 @@ public class KeySyncTaskTest { mRecoverableKeyStoreDb.setRecoveryServiceCertPath( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); - // Enter test mode with whitelisted credentials + // Enter test mode with allowlisted credentials when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true); when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any())) .thenReturn(true); @@ -415,7 +415,7 @@ public class KeySyncTaskTest { verify(mTestOnlyInsecureCertificateHelper) .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS)); - // run whitelist checks + // run allowlist checks verify(mTestOnlyInsecureCertificateHelper) .doesCredentialSupportInsecureMode(anyInt(), any()); verify(mTestOnlyInsecureCertificateHelper) @@ -424,7 +424,7 @@ public class KeySyncTaskTest { KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); assertNotNull(keyChainSnapshot); // created snapshot List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys(); - assertThat(applicationKeys).hasSize(0); // non whitelisted key is not included + assertThat(applicationKeys).hasSize(0); // non allowlisted key is not included verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); } @@ -437,7 +437,7 @@ public class KeySyncTaskTest { mRecoverableKeyStoreDb.setRecoveryServiceCertPath( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); - // Enter test mode with non whitelisted credentials + // Enter test mode with non allowlisted credentials when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true); when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any())) .thenReturn(false); diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 58b71d4b702a..f8043fa56f03 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -1396,7 +1396,7 @@ public class NetworkPolicyManagerServiceTest { actualCycleDay = mService.getCycleDayFromCarrierConfig(null, DEFAULT_CYCLE_DAY); assertEquals(DEFAULT_CYCLE_DAY, actualCycleDay); - // Sane, non-default values + // Valid, non-default values assertCycleDayAsExpected(config, 1, true); assertCycleDayAsExpected(config, cal.getMaximum(Calendar.DAY_OF_MONTH), true); assertCycleDayAsExpected(config, cal.getMinimum(Calendar.DAY_OF_MONTH), true); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 1d384e961dc3..7694d096917f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -155,8 +155,8 @@ public class PackageParserTest { @Test public void test_serializePackage() throws Exception { try (PackageParser2 pp = PackageParser2.forParsingFileWithDefaults()) { - ParsedPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, - true /* useCaches */); + AndroidPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, + true /* useCaches */).hideAsFinal(); Parcel p = Parcel.obtain(); pkg.writeToParcel(p, 0 /* flags */); @@ -467,7 +467,7 @@ public class PackageParserTest { ParsedInstrumentation b) { assertComponentsEqual(a, b); - // Sanity check for InstrumentationInfo. + // Validity check for InstrumentationInfo. assertEquals(a.getTargetPackage(), b.getTargetPackage()); assertEquals(a.getTargetProcesses(), b.getTargetProcesses()); assertEquals(a.isHandleProfiling(), b.isHandleProfiling()); @@ -482,7 +482,7 @@ public class PackageParserTest { ) { assertComponentsEqual(a, b); - // Sanity check for ServiceInfo. + // Validity check for ServiceInfo. ServiceInfo aInfo = PackageInfoUtils.generateServiceInfo(aPkg, a, 0, new PackageUserState(), 0, mockPkgSetting(aPkg)); ServiceInfo bInfo = PackageInfoUtils.generateServiceInfo(bPkg, b, 0, @@ -509,7 +509,7 @@ public class PackageParserTest { ) { assertComponentsEqual(a, b); - // Sanity check for ActivityInfo. + // Validity check for ActivityInfo. ActivityInfo aInfo = PackageInfoUtils.generateActivityInfo(aPkg, a, 0, new PackageUserState(), 0, mockPkgSetting(aPkg)); ActivityInfo bInfo = PackageInfoUtils.generateActivityInfo(bPkg, b, 0, @@ -522,7 +522,7 @@ public class PackageParserTest { ParsedPermissionGroup b) { assertComponentsEqual(a, b); - // Sanity check for PermissionGroupInfo. + // Validity check for PermissionGroupInfo. assertEquals(a.getName(), b.getName()); assertEquals(a.getDescriptionRes(), b.getDescriptionRes()); } @@ -591,7 +591,7 @@ public class PackageParserTest { null ) .setUse32BitAbi(true) - .setVolumeUuid("foo3") + .setVolumeUuid("d52ef59a-7def-4541-bf21-4c28ed4b65a0") .addPermission(permission) .addPermissionGroup(new ParsedPermissionGroup()) .addActivity(new ParsedActivity()) @@ -665,13 +665,13 @@ public class PackageParserTest { } if (List.class.isAssignableFrom(fieldType)) { - // Sanity check for list fields: Assume they're non-null and contain precisely + // Validity check for list fields: Assume they're non-null and contain precisely // one element. List<?> list = (List<?>) f.get(pkg); assertNotNull("List was null: " + f, list); assertEquals(1, list.size()); } else if (fieldType.getComponentType() != null) { - // Sanity check for array fields: Assume they're non-null and contain precisely + // Validity check for array fields: Assume they're non-null and contain precisely // one element. Object array = f.get(pkg); assertNotNull(Array.get(array, 0)); diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index 4d8cc4647271..1ca3c7488f32 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -470,11 +470,16 @@ public class ScanTests { private PackageManagerService.ScanResult executeScan( PackageManagerService.ScanRequest scanRequest) throws PackageManagerException { - return PackageManagerService.scanPackageOnlyLI( + PackageManagerService.ScanResult result = PackageManagerService.scanPackageOnlyLI( scanRequest, mMockInjector, false /*isUnderFactoryTest*/, System.currentTimeMillis()); + + // Need to call hideAsFinal to cache derived fields. This is normally done in PMS, but not + // in this cut down flow used for the test. + ((ParsedPackage) result.pkgSetting.pkg).hideAsFinal(); + return result; } private static String createCodePath(String packageName) { diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java index 87979fb00021..8e94544707a8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java @@ -150,7 +150,7 @@ public class UserSystemPackageInstallerTest { ArrayMap<String, Set<String>> r = new ArrayMap<>(); r.put("com.android.package1", new ArraySet<>(Arrays.asList( USER_TYPE_PROFILE_MANAGED, "invalid-garbage2"))); - // com.android.package2 has nothing blacklisted + // com.android.package2 has nothing denylisted r.put("com.android.package3", new ArraySet<>(Arrays.asList("SYSTEM"))); return r; } @@ -196,7 +196,7 @@ public class UserSystemPackageInstallerTest { /** * Test that determineWhitelistedPackagesForUserTypes does not include packages that were never - * whitelisted properly, but does include packages that were whitelisted but then blacklisted. + * allowlisted properly, but does include packages that were allowlisted but then denylisted. */ @Test public void testDetermineWhitelistedPackagesForUserTypes_noNetWhitelisting() { @@ -205,22 +205,22 @@ public class UserSystemPackageInstallerTest { public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeWhitelist() { ArrayMap<String, Set<String>> r = new ArrayMap<>(); r.put("com.android.package1", new ArraySet<>(Arrays.asList("invalid1"))); - // com.android.package2 has no whitelisting + // com.android.package2 has no allowlisting r.put("com.android.package3", new ArraySet<>(Arrays.asList("PROFILE", "FULL"))); r.put("com.android.package4", new ArraySet<>(Arrays.asList("PROFILE"))); r.put("com.android.package5", new ArraySet<>()); - // com.android.package6 has no whitelisting + // com.android.package6 has no allowlisting return r; } @Override public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeBlacklist() { ArrayMap<String, Set<String>> r = new ArrayMap<>(); - // com.android.package1 has no blacklisting + // com.android.package1 has no denylisting r.put("com.android.package2", new ArraySet<>(Arrays.asList("FULL"))); r.put("com.android.package3", new ArraySet<>(Arrays.asList("PROFILE", "FULL"))); r.put("com.android.package4", new ArraySet<>(Arrays.asList("PROFILE", "invalid4"))); - // com.android.package5 has no blacklisting + // com.android.package5 has no denylisting r.put("com.android.package6", new ArraySet<>(Arrays.asList("invalid6"))); return r; } @@ -242,18 +242,18 @@ public class UserSystemPackageInstallerTest { */ @Test public void testShouldInstallPackage() { - final String packageName1 = "pkg1"; // whitelisted - final String packageName2 = "pkg2"; // whitelisted and blacklisted - final String packageName3 = "pkg3"; // whitelisted for a different user type - final String packageName4 = "pkg4"; // not whitelisted nor blacklisted at all + final String packageName1 = "pkg1"; // allowlisted + final String packageName2 = "pkg2"; // allowlisted and denylisted + final String packageName3 = "pkg3"; // allowlisted for a different user type + final String packageName4 = "pkg4"; // not allowlisted nor denylisted at all - // Whitelist: user type bitset for each pkg (for the test, all that matters is 0 vs non-0). + // Allowlist: user type bitset for each pkg (for the test, all that matters is 0 vs non-0). final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>(); pkgBitSetMap.put(packageName1, 0b01L); pkgBitSetMap.put(packageName2, 0L); pkgBitSetMap.put(packageName3, 0b10L); - // Whitelist of pkgs for this specific user, i.e. subset of pkgBitSetMap for this user. + // Allowlist of pkgs for this specific user, i.e. subset of pkgBitSetMap for this user. final Set<String> userWhitelist = new ArraySet<>(); userWhitelist.add(packageName1); @@ -266,7 +266,7 @@ public class UserSystemPackageInstallerTest { final AndroidPackage pkg4 = ((ParsedPackage) PackageImpl.forTesting(packageName4) .hideAsParsed()).hideAsFinal(); - // No implicit whitelist, so only install pkg1. + // No implicit allowlist, so only install pkg1. boolean implicit = false; assertTrue(UserSystemPackageInstaller.shouldInstallPackage( pkg1, pkgBitSetMap, userWhitelist, implicit)); @@ -277,7 +277,7 @@ public class UserSystemPackageInstallerTest { assertFalse(UserSystemPackageInstaller.shouldInstallPackage( pkg4, pkgBitSetMap, userWhitelist, implicit)); - // Use implicit whitelist, so install pkg1 and pkg4 + // Use implicit allowlist, so install pkg1 and pkg4 implicit = true; assertTrue(UserSystemPackageInstaller.shouldInstallPackage( pkg1, pkgBitSetMap, userWhitelist, implicit)); @@ -302,12 +302,12 @@ public class UserSystemPackageInstallerTest { final long maskOfTypeA = 0b0001L; final long maskOfTypeC = 0b0100L; - final String packageName1 = "pkg1"; // whitelisted for user type A - final String packageName2 = "pkg2"; // blacklisted whenever whitelisted - final String packageName3 = "pkg3"; // whitelisted for user type C - final String packageName4 = "pkg4"; // whitelisted for user type A + final String packageName1 = "pkg1"; // allowlisted for user type A + final String packageName2 = "pkg2"; // denylisted whenever allowlisted + final String packageName3 = "pkg3"; // allowlisted for user type C + final String packageName4 = "pkg4"; // allowlisted for user type A - final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>(); // Whitelist: bitset per pkg + final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>(); // Allowlist: bitset per pkg pkgBitSetMap.put(packageName1, maskOfTypeA); pkgBitSetMap.put(packageName2, 0L); pkgBitSetMap.put(packageName3, maskOfTypeC); @@ -368,7 +368,7 @@ public class UserSystemPackageInstallerTest { } // Add auto-generated RRO package to expectedPackages since they are not (supposed to be) - // in the whitelist but they should be installed. + // in the allowlist but they should be installed. for (PackageInfo p : packageInfos) { if (p.isOverlayPackage() && UserSystemPackageInstaller.hasAutoGeneratedRROSuffix(p.packageName) @@ -396,7 +396,7 @@ public class UserSystemPackageInstallerTest { } /** - * Test that overlay package not in whitelist should be installed for all user at Explicit mode. + * Test that overlay package not in allowlist should be installed for all user at Explicit mode. */ @Test public void testInstallOverlayPackagesExplicitMode() { @@ -513,7 +513,7 @@ public class UserSystemPackageInstallerTest { assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode()); } - /** Sets the whitelist mode to the desired value via adb's setprop. */ + /** Sets the allowlist mode to the desired value via adb's setprop. */ private void setUserTypePackageWhitelistMode(int mode) { UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); try { @@ -529,7 +529,7 @@ public class UserSystemPackageInstallerTest { /** @see UserSystemPackageInstaller#mWhitelistedPackagesForUserTypes */ private ArrayMap<String, Long> getNewPackageToWhitelistedBitSetMap() { final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>(); - // "android" is always treated as whitelisted for all types, regardless of the xml file. + // "android" is always treated as allowlisted for all types, regardless of the xml file. pkgBitSetMap.put("android", ~0L); return pkgBitSetMap; } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 8398585ca74a..ff43da6370e8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -836,7 +836,7 @@ public class DexManagerTests { @Test public void testOobPackageSelectionWhitelist() { - // Various whitelist of apps to run in OOB mode. + // Various allowlist of apps to run in OOB mode. final String kWhitelistApp0 = "com.priv.app0"; final String kWhitelistApp1 = "com.priv.app1"; final String kWhitelistApp2 = "com.priv.app2"; @@ -845,17 +845,17 @@ public class DexManagerTests { // Packages that shares the targeting process. final Collection<String> runningPackages = Arrays.asList("com.priv.app1", "com.priv.app2"); - // Feature is off, whitelist does not matter + // Feature is off, allowlist does not matter assertFalse(shouldPackageRunOob(false, kWhitelistApp0, runningPackages)); assertFalse(shouldPackageRunOob(false, kWhitelistApp1, runningPackages)); assertFalse(shouldPackageRunOob(false, "", runningPackages)); assertFalse(shouldPackageRunOob(false, "ALL", runningPackages)); - // Feature is on, app not in whitelist + // Feature is on, app not in allowlist assertFalse(shouldPackageRunOob(true, kWhitelistApp0, runningPackages)); assertFalse(shouldPackageRunOob(true, "", runningPackages)); - // Feature is on, app in whitelist + // Feature is on, app in allowlist assertTrue(shouldPackageRunOob(true, kWhitelistApp1, runningPackages)); assertTrue(shouldPackageRunOob(true, kWhitelistApp2, runningPackages)); assertTrue(shouldPackageRunOob(true, kWhitelistApp1AndApp2, runningPackages)); diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt index d36dcce800eb..fb82b1d3d3f6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt @@ -59,7 +59,9 @@ class AndroidPackageInfoFlagBehaviorTest : AndroidPackageParsingTestBase() { fun appInfo(flag: Int, fieldFunction: (ApplicationInfo) -> List<Any?>) = Param( flag, ApplicationInfo::class.java.simpleName, - ::oldAppInfo, ::newAppInfo, fieldFunction + { pkg, flags -> oldAppInfo(pkg, flags) }, + { pkg, flags -> newAppInfo(pkg, flags) }, + fieldFunction ) } diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt index 0f5f0af8f838..61252c473530 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt @@ -32,6 +32,7 @@ import android.content.pm.ServiceInfo import android.os.Bundle import android.os.Debug import android.os.Environment +import android.os.Process import android.util.SparseArray import androidx.test.platform.app.InstrumentationRegistry import com.android.server.pm.PackageManagerService @@ -54,7 +55,7 @@ open class AndroidPackageParsingTestBase { private const val VERIFY_ALL_APKS = true - /** For auditing memory usage differences */ + // For auditing memory usage differences to /sdcard/AndroidPackageParsingTestBase.hprof private const val DUMP_HPROF_TO_EXTERNAL = false val context: Context = InstrumentationRegistry.getInstrumentation().getContext() @@ -104,10 +105,12 @@ open class AndroidPackageParsingTestBase { @JvmStatic @BeforeClass fun setUpPackages() { + var uid = Process.FIRST_APPLICATION_UID apks.mapNotNull { try { packageParser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false) to - packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false) + packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, + false) } catch (ignored: Exception) { // It is intentional that a failure of either call here will result in failing // both. Having null on one side would mean nothing to compare. Due to the @@ -117,8 +120,15 @@ open class AndroidPackageParsingTestBase { null } }.forEach { (old, new) -> + // Assign an arbitrary UID. This is normally done after parsing completes, inside + // PackageManagerService, but since that code isn't run here, need to mock it. This + // is equivalent to what the system would assign. + old.applicationInfo.uid = uid + new.uid = uid + uid++ + oldPackages += old - newPackages += new + newPackages += new.hideAsFinal() } if (DUMP_HPROF_TO_EXTERNAL) { @@ -131,12 +141,29 @@ open class AndroidPackageParsingTestBase { } } - fun oldAppInfo(pkg: PackageParser.Package, flags: Int = 0): ApplicationInfo? { - return PackageParser.generateApplicationInfo(pkg, flags, dummyUserState, 0) + fun oldAppInfo( + pkg: PackageParser.Package, + flags: Int = 0, + userId: Int = 0 + ): ApplicationInfo? { + return PackageParser.generateApplicationInfo(pkg, flags, dummyUserState, userId) + } + + fun newAppInfo( + pkg: AndroidPackage, + flags: Int = 0, + userId: Int = 0 + ): ApplicationInfo? { + return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, userId, + mockPkgSetting(pkg)) } - fun newAppInfo(pkg: AndroidPackage, flags: Int = 0): ApplicationInfo? { - return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, 0, + fun newAppInfoWithoutState( + pkg: AndroidPackage, + flags: Int = 0, + userId: Int = 0 + ): ApplicationInfo? { + return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, userId, mockPkgSetting(pkg)) } @@ -152,6 +179,7 @@ open class AndroidPackageParsingTestBase { private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> { this.pkg = aPkg + this.appId = aPkg.uid whenever(pkgState) { PackageStateUnserialized() } whenever(readUserState(anyInt())) { dummyUserState } } diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt new file mode 100644 index 000000000000..67b5d683de9a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 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.pm.parsing + +import android.content.pm.ApplicationInfo +import android.content.pm.PackageParser +import android.os.Environment +import android.os.UserHandle +import android.platform.test.annotations.Presubmit +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Test + +/** + * As a performance optimization, the new parsing code builds the user data directories manually + * using string concatenation. This tries to mirror the logic that [Environment] uses, but it is + * still fragile to changes and potentially different device configurations. + * + * This compares the resultant values against the old [PackageParser] outputs as well as + * [ApplicationInfo]'s own [ApplicationInfo.initForUser]. + */ +@Presubmit +class PackageInfoUserFieldsTest : AndroidPackageParsingTestBase() { + + @Test + fun userEnvironmentValues() { + // Specifically use a large user ID to test assumptions about single character IDs + val userId = 110 + + oldPackages.zip(newPackages) + .map { (old, new) -> + (old to oldAppInfo(pkg = old, userId = userId)!!) to + (new to newAppInfo(pkg = new, userId = userId)!!) + } + .forEach { (oldPair, newPair) -> + val (oldPkg, oldInfo) = oldPair + val (newPkg, newInfo) = newPair + + val oldValuesActual = extractActual(oldInfo) + val newValuesActual = extractActual(newInfo) + val oldValuesExpected: Values + val newValuesExpected: Values + + val packageName = oldPkg.packageName + if (packageName == "android") { + val systemDataDir = Environment.getDataSystemDirectory().absolutePath + oldValuesExpected = Values( + uid = UserHandle.getUid(userId, + UserHandle.getAppId(oldPkg.applicationInfo.uid)), + userDe = null, + userCe = null, + dataDir = systemDataDir + ) + newValuesExpected = Values( + uid = UserHandle.getUid(userId, UserHandle.getAppId(newPkg.uid)), + userDe = null, + userCe = null, + dataDir = systemDataDir + ) + } else { + oldValuesExpected = extractExpected(oldInfo, oldInfo.uid, userId) + newValuesExpected = extractExpected(newInfo, newPkg.uid, userId) + } + + // Calls the internal ApplicationInfo logic to compare against. This must be + // done after saving the original values, since this will overwrite them. + oldInfo.initForUser(userId) + newInfo.initForUser(userId) + + val oldInitValues = extractActual(oldInfo) + val newInitValues = extractActual(newInfo) + + // The optimization is also done for the no state API that isn't used by the + // system. This API is still exposed publicly, so for this test we should + // verify it. + val newNoStateValues = extractActual( + newAppInfoWithoutState(newPkg, 0, userId)!!) + + assertAllEquals(packageName, + oldValuesActual, oldValuesExpected, oldInitValues, + newValuesActual, newValuesExpected, newInitValues, newNoStateValues) + } + } + + private fun assertAllEquals(packageName: String, vararg values: Values) { + // Local function to avoid accidentally calling wrong type + fun assertAllEquals(message: String, vararg values: Any?) { + values.forEachIndexed { index, value -> + if (index == 0) return@forEachIndexed + assertWithMessage("$message $index").that(values[0]).isEqualTo(value) + } + } + + assertAllEquals("$packageName mismatched uid", values.map { it.uid }) + assertAllEquals("$packageName mismatched userDe", values.map { it.userDe }) + assertAllEquals("$packageName mismatched userCe", values.map { it.userCe }) + assertAllEquals("$packageName mismatched dataDir", values.map { it.dataDir }) + } + + private fun extractActual(appInfo: ApplicationInfo) = Values( + uid = appInfo.uid, + userDe = appInfo.deviceProtectedDataDir, + userCe = appInfo.credentialProtectedDataDir, + dataDir = appInfo.dataDir + ) + + private fun extractExpected(appInfo: ApplicationInfo, appIdUid: Int, userId: Int): Values { + val userDe = Environment.getDataUserDePackageDirectory(appInfo.volumeUuid, userId, + appInfo.packageName).absolutePath + val userCe = Environment.getDataUserCePackageDirectory(appInfo.volumeUuid, userId, + appInfo.packageName).absolutePath + val dataDir = if (appInfo.isDefaultToDeviceProtectedStorage) { + appInfo.deviceProtectedDataDir + } else { + appInfo.credentialProtectedDataDir + } + + return Values( + uid = UserHandle.getUid(userId, UserHandle.getAppId(appIdUid)), + userDe = userDe, + userCe = userCe, + dataDir = dataDir + ) + } + + data class Values( + val uid: Int, + val userDe: String?, + val userCe: String?, + val dataDir: String? + ) +} diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java new file mode 100644 index 000000000000..3221a4d4f12c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.content.Context; + +import androidx.test.InstrumentationRegistry; + +import com.android.server.SystemService; +import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; +import com.android.server.powerstats.nano.PowerStatsServiceProto; + +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.util.Random; + +/** + * Tests for {@link com.android.server.powerstats.PowerStatsService}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:PowerStatsServiceTest + */ +public class PowerStatsServiceTest { + private static final String TAG = PowerStatsServiceTest.class.getSimpleName(); + private static final String DATA_STORAGE_SUBDIR = "powerstatstest"; + private static final String DATA_STORAGE_FILENAME = "test"; + private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto"; + private static final String RAIL_NAME = "railname"; + private static final String SUBSYS_NAME = "subsysname"; + private static final int POWER_RAIL_COUNT = 8; + + private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); + private PowerStatsService mService; + private File mDataStorageDir; + private TimerTrigger mTimerTrigger; + private PowerStatsLogger mPowerStatsLogger; + + private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() { + @Override + File createDataStoragePath() { + mDataStorageDir = null; + + try { + mDataStorageDir = Files.createTempDirectory(DATA_STORAGE_SUBDIR).toFile(); + } catch (IOException e) { + fail("Could not create temp directory."); + } + + return mDataStorageDir; + } + + @Override + String createDataStorageFilename() { + return DATA_STORAGE_FILENAME; + } + + @Override + IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() { + return new TestPowerStatsHALWrapper(); + } + + @Override + PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, + String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { + mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, dataStorageFilename, + powerStatsHALWrapper); + return mPowerStatsLogger; + } + + @Override + BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) { + return new BatteryTrigger(context, powerStatsLogger, false /* trigger enabled */); + } + + @Override + TimerTrigger createTimerTrigger(Context context, PowerStatsLogger powerStatsLogger) { + mTimerTrigger = new TimerTrigger(context, powerStatsLogger, + false /* trigger enabled */); + return mTimerTrigger; + } + }; + + public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper { + @Override + public PowerStatsData.RailInfo[] readRailInfo() { + PowerStatsData.RailInfo[] railInfoArray = new PowerStatsData.RailInfo[POWER_RAIL_COUNT]; + for (int i = 0; i < POWER_RAIL_COUNT; i++) { + railInfoArray[i] = new PowerStatsData.RailInfo(i, RAIL_NAME + i, SUBSYS_NAME + i, + i); + } + return railInfoArray; + } + + @Override + public PowerStatsData.EnergyData[] readEnergyData() { + PowerStatsData.EnergyData[] energyDataArray = + new PowerStatsData.EnergyData[POWER_RAIL_COUNT]; + for (int i = 0; i < POWER_RAIL_COUNT; i++) { + energyDataArray[i] = new PowerStatsData.EnergyData(i, i, i); + } + return energyDataArray; + } + + @Override + public boolean initialize() { + return true; + } + } + + @Before + public void setUp() { + mService = new PowerStatsService(mContext, mInjector); + } + + @Test + public void testWrittenPowerStatsHALDataMatchesReadIncidentReportData() + throws InterruptedException, IOException { + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Write data to on-device storage. + mTimerTrigger.logPowerStatsData(); + + // The above call puts a message on a handler. Wait for + // it to be processed. + Thread.sleep(100); + + // Write on-device storage to an incident report. + File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME); + FileOutputStream fos = new FileOutputStream(incidentReport); + mPowerStatsLogger.writeToFile(fos.getFD()); + + // Read the incident report in to a byte array. + FileInputStream fis = new FileInputStream(incidentReport); + byte[] fileContent = new byte[(int) incidentReport.length()]; + fis.read(fileContent); + + // Parse the incident data into a PowerStatsServiceProto object. + PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent); + + // Validate the railInfo array matches what was written to on-device storage. + assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT); + for (int i = 0; i < pssProto.railInfo.length; i++) { + assertTrue(pssProto.railInfo[i].index == i); + assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i)); + assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i)); + assertTrue(pssProto.railInfo[i].samplingRate == i); + } + + // Validate the energyData array matches what was written to on-device storage. + assertTrue(pssProto.energyData.length == POWER_RAIL_COUNT); + for (int i = 0; i < pssProto.energyData.length; i++) { + assertTrue(pssProto.energyData[i].index == i); + assertTrue(pssProto.energyData[i].timestampMs == i); + assertTrue(pssProto.energyData[i].energyUws == i); + } + } + + @Test + public void testCorruptOnDeviceStorage() throws IOException { + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Generate random array of bytes to emulate corrupt data. + Random rd = new Random(); + byte[] bytes = new byte[100]; + rd.nextBytes(bytes); + + // Store corrupt data in on-device storage. Add fake timestamp to filename + // to match format expected by FileRotator. + File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234"); + FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile); + onDeviceStorageFos.write(bytes); + onDeviceStorageFos.close(); + + // Write on-device storage to an incident report. + File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME); + FileOutputStream incidentReportFos = new FileOutputStream(incidentReport); + mPowerStatsLogger.writeToFile(incidentReportFos.getFD()); + + // Read the incident report in to a byte array. + FileInputStream fis = new FileInputStream(incidentReport); + byte[] fileContent = new byte[(int) incidentReport.length()]; + fis.read(fileContent); + + // Parse the incident data into a PowerStatsServiceProto object. + PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent); + + // Valid railInfo data is written to the incident report in the call to + // mPowerStatsLogger.writeToFile(). + assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT); + for (int i = 0; i < pssProto.railInfo.length; i++) { + assertTrue(pssProto.railInfo[i].index == i); + assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i)); + assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i)); + assertTrue(pssProto.railInfo[i].samplingRate == i); + } + + // No energyData should be written to the incident report since it + // is all corrupt (random bytes generated above). + assertTrue(pssProto.energyData.length == 0); + } + + @Test + public void testNotEnoughBytesAfterLengthField() throws IOException { + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Create corrupt data. + // Length field is correct, but there is no data following the length. + ByteArrayOutputStream data = new ByteArrayOutputStream(); + data.write(ByteBuffer.allocate(4).putInt(50).array()); + byte[] test = data.toByteArray(); + + // Store corrupt data in on-device storage. Add fake timestamp to filename + // to match format expected by FileRotator. + File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234"); + FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile); + onDeviceStorageFos.write(data.toByteArray()); + onDeviceStorageFos.close(); + + // Write on-device storage to an incident report. + File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME); + FileOutputStream incidentReportFos = new FileOutputStream(incidentReport); + mPowerStatsLogger.writeToFile(incidentReportFos.getFD()); + + // Read the incident report in to a byte array. + FileInputStream fis = new FileInputStream(incidentReport); + byte[] fileContent = new byte[(int) incidentReport.length()]; + fis.read(fileContent); + + // Parse the incident data into a PowerStatsServiceProto object. + PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent); + + // Valid railInfo data is written to the incident report in the call to + // mPowerStatsLogger.writeToFile(). + assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT); + for (int i = 0; i < pssProto.railInfo.length; i++) { + assertTrue(pssProto.railInfo[i].index == i); + assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i)); + assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i)); + assertTrue(pssProto.railInfo[i].samplingRate == i); + } + + // No energyData should be written to the incident report since the + // input buffer had only length and no data. + assertTrue(pssProto.energyData.length == 0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java index 13a3c2f23988..46224cb8f855 100644 --- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java @@ -123,7 +123,7 @@ public class DiskStatsFileLoggerTest extends AndroidTestCase { JSONArray cacheSizes = output.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY); assertThat(cacheSizes.length()).isEqualTo(2); - // We need to do this crazy Set over this because the DiskStatsFileLogger provides no + // We need to do this unexpected Set over this because the DiskStatsFileLogger provides no // guarantee of the ordering of the apps in its output. By using a set, we avoid any order // problems. ArraySet<AppSizeGrouping> apps = new ArraySet<>(); diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index 10981ab49513..17d99e652726 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -184,7 +184,7 @@ public class SystemConfigTest { /** * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_APP_CONFIGS} - * permission flag for the tag: whitelisted-staged-installer. + * permission flag for the tag: allowlisted-staged-installer. */ @Test public void readPermissions_allowAppConfigs_parsesStagedInstallerWhitelist() @@ -204,7 +204,7 @@ public class SystemConfigTest { /** * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_APP_CONFIGS} - * permission flag for the tag: whitelisted-staged-installer. + * permission flag for the tag: allowlisted-staged-installer. */ @Test public void readPermissions_notAllowAppConfigs_wontParseStagedInstallerWhitelist() diff --git a/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java index a787c321fc66..3b963f6697f9 100644 --- a/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; /** - * Sanity test for {@link IconsContentProvider}. + * Validity test for {@link IconsContentProvider}. */ @RunWith(AndroidJUnit4.class) public final class IconsContentProviderTest { diff --git a/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java b/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java index b200293ee916..fa14b24823d0 100644 --- a/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java +++ b/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java @@ -52,7 +52,7 @@ public class TestUtils { * EasyMock-style "strict" mock that throws immediately on any interaction that was not * explicitly allowed. * - * You can allow certain method calls on a whitelist basis by stubbing them e.g. with + * You can allow certain method calls on a allowlist basis by stubbing them e.g. with * {@link Mockito#doAnswer}, {@link Mockito#doNothing}, etc. */ public static <T> T strictMock(Class<T> c) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index e860f2508983..9be31647cf73 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -33,6 +33,8 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.timeout; +import android.app.ActivityOptions; +import android.app.ActivityOptions.SourceInfo; import android.content.Intent; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; @@ -68,6 +70,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { private ActivityRecord mTrampolineActivity; private ActivityRecord mTopActivity; + private ActivityOptions mActivityOptions; private boolean mLaunchTopByTrampoline; @Before @@ -209,6 +212,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { @Test public void testOnReportFullyDrawn() { + mActivityOptions = ActivityOptions.makeBasic(); + mActivityOptions.setSourceInfo(SourceInfo.TYPE_LAUNCHER, SystemClock.uptimeMillis() - 10); onActivityLaunched(mTopActivity); // The activity reports fully drawn before windows drawn, then the fully drawn event will @@ -216,7 +221,10 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { mActivityMetricsLogger.logAppTransitionReportedDrawn(mTopActivity, false); notifyTransitionStarting(mTopActivity); // The pending fully drawn event should send when the actual windows drawn event occurs. - notifyWindowsDrawn(mTopActivity); + final ActivityMetricsLogger.TransitionInfoSnapshot info = notifyWindowsDrawn(mTopActivity); + assertWithMessage("Record start source").that(info.sourceType) + .isEqualTo(SourceInfo.TYPE_LAUNCHER); + assertWithMessage("Record event time").that(info.sourceEventDelayMs).isAtLeast(10); verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong()); verifyOnActivityLaunchFinished(mTopActivity); @@ -251,7 +259,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { } private void notifyActivityLaunched(int resultCode, ActivityRecord activity) { - mActivityMetricsLogger.notifyActivityLaunched(mLaunchingState, resultCode, activity); + mActivityMetricsLogger.notifyActivityLaunched(mLaunchingState, resultCode, activity, + mActivityOptions); } private void notifyTransitionStarting(ActivityRecord activity) { @@ -260,8 +269,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { mActivityMetricsLogger.notifyTransitionStarting(reasons); } - private void notifyWindowsDrawn(ActivityRecord r) { - mActivityMetricsLogger.notifyWindowsDrawn(r, SystemClock.elapsedRealtimeNanos()); + private ActivityMetricsLogger.TransitionInfoSnapshot notifyWindowsDrawn(ActivityRecord r) { + return mActivityMetricsLogger.notifyWindowsDrawn(r, SystemClock.elapsedRealtimeNanos()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java index 56cb447e65b0..f53894ad9ec5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java @@ -102,7 +102,7 @@ public class HighRefreshRateBlacklistTest { final FakeDeviceConfig config = new FakeDeviceConfig(); mBlacklist = new HighRefreshRateBlacklist(r, config); - // First check that the default blacklist is in effect + // First check that the default denylist is in effect assertTrue(mBlacklist.isBlacklisted(APP1)); assertFalse(mBlacklist.isBlacklisted(APP2)); assertFalse(mBlacklist.isBlacklisted(APP3)); @@ -130,7 +130,7 @@ public class HighRefreshRateBlacklistTest { assertTrue(mBlacklist.isBlacklisted(APP2)); assertTrue(mBlacklist.isBlacklisted(APP3)); - // Change an unrelated flag in our namespace and verify that the blacklist is intact + // Change an unrelated flag in our namespace and verify that the denylist is intact config.putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, "someKey", "someValue"); assertFalse(mBlacklist.isBlacklisted(APP1)); assertTrue(mBlacklist.isBlacklisted(APP2)); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index ca3f815698e8..fa2c942b0883 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -219,19 +219,13 @@ public class TaskStackChangedListenerTest { activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch); final int id = activity.getTaskId(); - // Test for onTaskCreated. - waitForCallback(taskCreatedLaunchLatch); + // Test for onTaskCreated and onTaskMovedToFront + waitForCallback(taskMovedToFrontLatch); + assertEquals(0, taskCreatedLaunchLatch.getCount()); assertEquals(id, params[0]); ComponentName componentName = (ComponentName) params[1]; assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName()); - // Test for onTaskMovedToFront. - assertEquals(1, taskMovedToFrontLatch.getCount()); - mService.moveTaskToFront(null, getInstrumentation().getContext().getPackageName(), id, 0, - null); - waitForCallback(taskMovedToFrontLatch); - assertEquals(activity.getTaskId(), params[0]); - // Test for onTaskRemovalStarted. assertEquals(1, taskRemovalStartedLatch.getCount()); assertEquals(1, taskRemovedLatch.getCount()); diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt index 72e11c867a2b..ec8e8e7910e8 100644 --- a/telephony/api/system-current.txt +++ b/telephony/api/system-current.txt @@ -888,6 +888,7 @@ package android.telephony.data { method public int getCause(); method @NonNull public java.util.List<java.net.InetAddress> getDnsAddresses(); method @NonNull public java.util.List<java.net.InetAddress> getGatewayAddresses(); + method public int getHandoverFailureMode(); method public int getId(); method @NonNull public String getInterfaceName(); method public int getLinkStatus(); @@ -899,6 +900,11 @@ package android.telephony.data { method public int getSuggestedRetryTime(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; + field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2; // 0x2 + field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1 + field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3 + field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4 + field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0 field public static final int LINK_STATUS_ACTIVE = 2; // 0x2 field public static final int LINK_STATUS_DORMANT = 1; // 0x1 field public static final int LINK_STATUS_INACTIVE = 0; // 0x0 @@ -912,6 +918,7 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setCause(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setDnsAddresses(@NonNull java.util.List<java.net.InetAddress>); method @NonNull public android.telephony.data.DataCallResponse.Builder setGatewayAddresses(@NonNull java.util.List<java.net.InetAddress>); + method @NonNull public android.telephony.data.DataCallResponse.Builder setHandoverFailureMode(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setId(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@NonNull String); method @NonNull public android.telephony.data.DataCallResponse.Builder setLinkStatus(int); diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 242c2e979571..5ead8decdb63 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -67,6 +67,47 @@ public final class DataCallResponse implements Parcelable { /** Indicates the data connection is active with physical link up. */ public static final int LINK_STATUS_ACTIVE = 2; + /** {@hide} */ + @IntDef(prefix = "HANDOVER_FAILURE_MODE_", value = { + HANDOVER_FAILURE_MODE_LEGACY, + HANDOVER_FAILURE_MODE_DO_FALLBACK, + HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER, + HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface HandoverFailureMode {} + + /** + * Data handover failure mode is unknown. + */ + public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; + + /** + * Perform fallback to the source data transport on data handover failure using + * the legacy logic, which is fallback if the fail cause is + * {@link DataFailCause#HANDOFF_PREFERENCE_CHANGED}. + */ + public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; + + /** + * Perform fallback to the source data transport on data handover failure. + */ + public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2; + + /** + * Do not perform fallback to the source data transport on data handover failure. + * Frameworks should keep retrying handover by sending + * {@link DataService#REQUEST_REASON_HANDOVER} request to the underlying {@link DataService}. + */ + public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; + + /** + * Do not perform fallback to the source transport on data handover failure. + * Frameworks should retry setup a new data connection by sending + * {@link DataService#REQUEST_REASON_NORMAL} request to the underlying {@link DataService}. + */ + public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; + private final @DataFailureCause int mCause; private final int mSuggestedRetryTime; private final int mId; @@ -80,6 +121,7 @@ public final class DataCallResponse implements Parcelable { private final int mMtu; private final int mMtuV4; private final int mMtuV6; + private final @HandoverFailureMode int mHandoverFailureMode; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -126,14 +168,15 @@ public final class DataCallResponse implements Parcelable { mPcscfAddresses = (pcscfAddresses == null) ? new ArrayList<>() : new ArrayList<>(pcscfAddresses); mMtu = mMtuV4 = mMtuV6 = mtu; + mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; } - /** @hide */ private DataCallResponse(@DataFailureCause int cause, int suggestedRetryTime, int id, @LinkStatus int linkStatus, @ProtocolType int protocolType, @Nullable String interfaceName, @Nullable List<LinkAddress> addresses, @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, - @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) { + @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, + @HandoverFailureMode int handoverFailureMode) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -151,6 +194,7 @@ public final class DataCallResponse implements Parcelable { mMtu = mtu; mMtuV4 = mtuV4; mMtuV6 = mtuV6; + mHandoverFailureMode = handoverFailureMode; } /** @hide */ @@ -173,6 +217,7 @@ public final class DataCallResponse implements Parcelable { mMtu = source.readInt(); mMtuV4 = source.readInt(); mMtuV6 = source.readInt(); + mHandoverFailureMode = source.readInt(); } /** @@ -262,6 +307,13 @@ public final class DataCallResponse implements Parcelable { return mMtuV6; } + /** + * @return The data handover failure mode. + */ + public @HandoverFailureMode int getHandoverFailureMode() { + return mHandoverFailureMode; + } + @NonNull @Override public String toString() { @@ -339,6 +391,7 @@ public final class DataCallResponse implements Parcelable { dest.writeInt(mMtu); dest.writeInt(mMtuV4); dest.writeInt(mMtuV6); + dest.writeInt(mHandoverFailureMode); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -395,6 +448,8 @@ public final class DataCallResponse implements Parcelable { private int mMtuV6; + private @HandoverFailureMode int mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; + /** * Default constructor for Builder. */ @@ -553,6 +608,17 @@ public final class DataCallResponse implements Parcelable { } /** + * Set data handover failure mode for the data call response. + * + * @param failureMode Handover failure mode. + * @return The same instance of the builder. + */ + public @NonNull Builder setHandoverFailureMode(@HandoverFailureMode int failureMode) { + mHandoverFailureMode = failureMode; + return this; + } + + /** * Build the DataCallResponse. * * @return the DataCallResponse object. @@ -560,7 +626,7 @@ public final class DataCallResponse implements Parcelable { public @NonNull DataCallResponse build() { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, - mPcscfAddresses, mMtu, mMtuV4, mMtuV6); + mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode); } } } diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp index ae01170a6894..b78f48ce7f17 100644 --- a/tools/aapt2/Resource.cpp +++ b/tools/aapt2/Resource.cpp @@ -96,8 +96,6 @@ StringPiece to_string(ResourceType type) { return "styleable"; case ResourceType::kTransition: return "transition"; - case ResourceType::kUnknown: - return "unknown"; case ResourceType::kXml: return "xml"; } diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h index 22e667be7b93..8fe0eb3b51bf 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -66,11 +66,6 @@ enum class ResourceType { kStyle, kStyleable, kTransition, - - // Not a parsed type. It is only used when loading resource tables that may have modified type - // names - kUnknown, - kXml, }; diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index d84ca3d92f67..118a76ff73a8 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -2334,11 +2334,15 @@ int LinkCommand::Action(const std::vector<std::string>& args) { } // Populate some default no-compress extensions that are already compressed. - options_.extensions_to_not_compress.insert( - {".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg", - ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", - ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", - ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"}); + options_.extensions_to_not_compress.insert({ + // Image extensions + ".jpg", ".jpeg", ".png", ".gif", ".webp", + // Audio extensions + ".wav", ".mp2", ".mp3", ".ogg", ".aac", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy", + ".xmf", ".amr", ".awb", + // Audio/video extensions + ".mpg", ".mpeg", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".wma", ".wmv", + ".webm", ".mkv"}); // Turn off auto versioning for static-libs. if (context.GetPackageType() == PackageType::kStaticLib) { diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index f362744c0942..cccd9faa9b39 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -352,15 +352,15 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, config.copyFromDtoH(type->config); const std::string type_str = util::GetString(type_pool_, type->id - 1); - - // Be lenient on the name of the type if the table is lenient on resource validation. - auto parsed_type = ResourceType::kUnknown; - if (const ResourceType* parsed = ParseResourceType(type_str)) { - parsed_type = *parsed; - } else if (table_->GetValidateResources()) { - diag_->Error(DiagMessage(source_) << "invalid type name '" << type_str << "' for type with ID " - << (int) type->id); - return false; + const ResourceType* parsed_type = ParseResourceType(type_str); + if (!parsed_type) { + // Be lenient on the name of the type if the table is lenient on resource validation. + bool log_error = table_->GetValidateResources(); + if (log_error) { + diag_->Error(DiagMessage(source_) << "invalid type name '" << type_str + << "' for type with ID " << type->id); + } + return !log_error; } TypeVariant tv(type); @@ -370,9 +370,8 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, continue; } - const ResourceName name(package->name, parsed_type, + const ResourceName name(package->name, *parsed_type, util::GetString(key_pool_, util::DeviceToHost32(entry->key.index))); - const ResourceId res_id(package->id.value(), type->id, static_cast<uint16_t>(it.index())); std::unique_ptr<Value> resource_value; diff --git a/tools/powerstats/Android.bp b/tools/powerstats/Android.bp new file mode 100644 index 000000000000..af41144167a9 --- /dev/null +++ b/tools/powerstats/Android.bp @@ -0,0 +1,10 @@ +java_binary_host { + name: "PowerStatsServiceProtoParser", + manifest: "PowerStatsServiceProtoParser_manifest.txt", + srcs: [ + "*.java", + ], + static_libs: [ + "platformprotos", + ], +} diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java new file mode 100644 index 000000000000..8ab302a50662 --- /dev/null +++ b/tools/powerstats/PowerStatsServiceProtoParser.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 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.powerstats; + +import java.io.FileInputStream; +import java.io.IOException; + +/** + * This class implements a utility to parse ODPM data out + * of incident reports contained in bugreports. The data + * is output to STDOUT in csv format. + */ +public class PowerStatsServiceProtoParser { + private static void printRailInfo(PowerStatsServiceProto proto) { + String csvHeader = new String(); + for (int i = 0; i < proto.getRailInfoCount(); i++) { + RailInfoProto railInfo = proto.getRailInfo(i); + csvHeader += "Index" + "," + + "Timestamp" + "," + + railInfo.getRailName() + "/" + railInfo.getSubsysName() + ","; + } + System.out.println(csvHeader); + } + + private static void printEnergyData(PowerStatsServiceProto proto) { + int railInfoCount = proto.getRailInfoCount(); + + if (railInfoCount > 0) { + int energyDataCount = proto.getEnergyDataCount(); + int energyDataSetCount = energyDataCount / railInfoCount; + + for (int i = 0; i < energyDataSetCount; i++) { + String csvRow = new String(); + for (int j = 0; j < railInfoCount; j++) { + EnergyDataProto energyData = proto.getEnergyData(i * railInfoCount + j); + csvRow += energyData.getIndex() + "," + + energyData.getTimestampMs() + "," + + energyData.getEnergyUws() + ","; + } + System.out.println(csvRow); + } + } else { + System.out.println("Error: railInfoCount is zero"); + } + } + + private static void generateCsvFile(String pathToIncidentReport) { + try { + IncidentReportProto irProto = + IncidentReportProto.parseFrom(new FileInputStream(pathToIncidentReport)); + + if (irProto.hasIncidentReport()) { + PowerStatsServiceProto pssProto = irProto.getIncidentReport(); + printRailInfo(pssProto); + printEnergyData(pssProto); + } else { + System.out.println("Incident report not found. Exiting."); + } + } catch (IOException e) { + System.out.println("Unable to open incident report file: " + pathToIncidentReport); + System.out.println(e); + } + } + + /** + * This is the entry point to parse the ODPM data out of incident reports. + * It requires one argument which is the path to the incident_report.proto + * file captured in a bugreport. + * + * @param args Path to incident_report.proto passed in from command line. + */ + public static void main(String[] args) { + if (args.length > 0) { + generateCsvFile(args[0]); + } else { + System.err.println("Usage: PowerStatsServiceProtoParser <incident_report.proto>"); + System.err.println("Missing path to incident_report.proto. Exiting."); + System.exit(1); + } + } +} diff --git a/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt b/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt new file mode 100644 index 000000000000..5df12118ce80 --- /dev/null +++ b/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt @@ -0,0 +1 @@ +Main-class: com.android.server.powerstats.PowerStatsServiceProtoParser diff --git a/wifi/java/android/net/wifi/nl80211/NativeScanResult.java b/wifi/java/android/net/wifi/nl80211/NativeScanResult.java index 35cf45fe36ed..7a9b34b16058 100644 --- a/wifi/java/android/net/wifi/nl80211/NativeScanResult.java +++ b/wifi/java/android/net/wifi/nl80211/NativeScanResult.java @@ -157,7 +157,9 @@ public final class NativeScanResult implements Parcelable { BSS_CAPABILITY_RADIO_MANAGEMENT, BSS_CAPABILITY_DSSS_OFDM, BSS_CAPABILITY_DELAYED_BLOCK_ACK, - BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK + BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK, + BSS_CAPABILITY_DMG_ESS, + BSS_CAPABILITY_DMG_IBSS }) public @interface BssCapabilityBits { } |