diff options
96 files changed, 4047 insertions, 2914 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..84a8a1d5c7a6 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4075,7 +4075,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 +4086,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 +4097,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); } } 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/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 94c2305e36f6..038f4c35bab6 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; } /** 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/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/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/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/Process.java b/core/java/android/os/Process.java index efea9537c4cf..d7393cadb121 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; @@ -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/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/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/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/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 050c1c4b4df5..ac08d96ab303 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. --> 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/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/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/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/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..a403f4521aa0 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -4015,7 +4015,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 +4026,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 +4037,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); } } 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/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/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 5ce40a837663..a6a39034c5eb 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> 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/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/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/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/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/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index bd590d317910..bc79a6a5817b 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); } } }; 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/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/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/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/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index a4486d7b5898..bd4fc135d2bb 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; @@ -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/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/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/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 999bbc9ecf4e..5850dc012226 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); @@ -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 @@ -19863,7 +19863,7 @@ public class PackageManagerService extends IPackageManager.Stub final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId); final ArrayList<PreferredActivity> existing = pir.findFilters(filter); if (removeExisting && existing != null) { - mSettings.removeFiltersLPw(pir, filter, existing); + Settings.removeFilters(pir, filter, existing); } pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); scheduleWritePackageRestrictionsLocked(userId); @@ -19964,7 +19964,7 @@ public class PackageManagerService extends IPackageManager.Stub } } if (existing != null) { - mSettings.removeFiltersLPw(pir, filter, existing); + Settings.removeFilters(pir, filter, existing); } } } @@ -21827,7 +21827,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 +23707,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 +23808,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 +25824,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/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 74bc49dd9108..f801702f7ddd 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3183,7 +3183,7 @@ public final class Settings { } } - void removeFiltersLPw(@NonNull PreferredIntentResolver pir, + static void removeFilters(@NonNull PreferredIntentResolver pir, @NonNull IntentFilter filter, @NonNull List<PreferredActivity> existing) { if (PackageManagerService.DEBUG_PREFERRED) { Slog.i(TAG, existing.size() + " preferred matches for:"); @@ -3405,7 +3405,7 @@ public final class Settings { final PreferredIntentResolver pir = editPreferredActivitiesLPw(userId); final List<PreferredActivity> existing = pir.findFilters(filter); if (existing != null) { - removeFiltersLPw(pir, filter, existing); + removeFilters(pir, filter, existing); } PreferredActivity pa = new PreferredActivity(filter, systemMatch, set, cn, true); pir.addFilter(pa); 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/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..f319bf495e8b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -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/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/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/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/Task.java b/services/core/java/com/android/server/wm/Task.java index 15a44e8ab2f8..9172897b4869 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5938,31 +5938,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, 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/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/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/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 8759077ea0ad..3a8d9c3e9ff5 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))); 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/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/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 { } |