diff options
184 files changed, 8343 insertions, 2247 deletions
diff --git a/Android.bp b/Android.bp index 015e59fe9d08..a5bbb1825ae1 100644 --- a/Android.bp +++ b/Android.bp @@ -463,6 +463,17 @@ java_library { "telecomm/java/com/android/internal/telecom/IInCallService.aidl", "telecomm/java/com/android/internal/telecom/ITelecomService.aidl", "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl", "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl", "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl", @@ -553,7 +564,7 @@ java_library { ], aidl: { - local_include_dirs: [ + export_include_dirs: [ // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS "core/java", "graphics/java", diff --git a/Android.mk b/Android.mk index d4a1dcf891d7..103536297838 100644 --- a/Android.mk +++ b/Android.mk @@ -278,7 +278,7 @@ framework_base_android_test_mock_src_files := \ $(call all-java-files-under, test-mock/src/android/test/mock) framework_base_android_test_runner_src_files := \ - $(call all-java-files-under, test-runner/src) + $(call all-java-files-under, test-runner/src/junit) # Find all files in specific directories (relative to frameworks/base) # to document and check apis diff --git a/api/current.txt b/api/current.txt index a01de4f81c6a..8a09b82bf770 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6418,6 +6418,7 @@ package android.app.admin { method public boolean isResetPasswordTokenActive(android.content.ComponentName); method public boolean isSecurityLoggingEnabled(android.content.ComponentName); method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String); + method public boolean isUsingUnifiedPassword(android.content.ComponentName); method public void lockNow(); method public void lockNow(int); method public boolean logoutUser(android.content.ComponentName); @@ -7028,6 +7029,7 @@ package android.app.slice { field public static final java.lang.String HINT_NO_TINT = "no_tint"; field public static final java.lang.String HINT_PARTIAL = "partial"; field public static final java.lang.String HINT_SELECTED = "selected"; + field public static final java.lang.String HINT_SUMMARY = "summary"; field public static final java.lang.String HINT_TITLE = "title"; field public static final java.lang.String SUBTYPE_COLOR = "color"; field public static final java.lang.String SUBTYPE_MESSAGE = "message"; @@ -7090,6 +7092,20 @@ package android.app.slice { field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp"; } + public class SliceManager { + method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri); + method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>); + method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>); + method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler); + method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor); + method public void unpinSlice(android.net.Uri); + method public void unregisterSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback); + } + + public static abstract interface SliceManager.SliceCallback { + method public abstract void onSliceUpdated(android.app.slice.Slice); + } + public abstract class SliceProvider extends android.content.ContentProvider { ctor public SliceProvider(); method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); @@ -7098,6 +7114,8 @@ package android.app.slice { method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>); method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri); method public android.net.Uri onMapIntentToUri(android.content.Intent); + method public void onSlicePinned(android.net.Uri); + method public void onSliceUnpinned(android.net.Uri); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal); @@ -8119,6 +8137,94 @@ package android.bluetooth { method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int); } + public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile { + method public boolean connect(android.bluetooth.BluetoothDevice); + method public boolean disconnect(android.bluetooth.BluetoothDevice); + method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method public int getConnectionState(android.bluetooth.BluetoothDevice); + method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]); + method public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceCallback); + method public boolean replyReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]); + method public boolean reportError(android.bluetooth.BluetoothDevice, byte); + method public boolean sendReport(android.bluetooth.BluetoothDevice, int, byte[]); + method public boolean unregisterApp(); + field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; + field public static final byte ERROR_RSP_INVALID_PARAM = 4; // 0x4 + field public static final byte ERROR_RSP_INVALID_RPT_ID = 2; // 0x2 + field public static final byte ERROR_RSP_NOT_READY = 1; // 0x1 + field public static final byte ERROR_RSP_SUCCESS = 0; // 0x0 + field public static final byte ERROR_RSP_UNKNOWN = 14; // 0xe + field public static final byte ERROR_RSP_UNSUPPORTED_REQ = 3; // 0x3 + field public static final byte PROTOCOL_BOOT_MODE = 0; // 0x0 + field public static final byte PROTOCOL_REPORT_MODE = 1; // 0x1 + field public static final byte REPORT_TYPE_FEATURE = 3; // 0x3 + field public static final byte REPORT_TYPE_INPUT = 1; // 0x1 + field public static final byte REPORT_TYPE_OUTPUT = 2; // 0x2 + field public static final byte SUBCLASS1_COMBO = -64; // 0xffffffc0 + field public static final byte SUBCLASS1_KEYBOARD = 64; // 0x40 + field public static final byte SUBCLASS1_MOUSE = -128; // 0xffffff80 + field public static final byte SUBCLASS1_NONE = 0; // 0x0 + field public static final byte SUBCLASS2_CARD_READER = 6; // 0x6 + field public static final byte SUBCLASS2_DIGITIZER_TABLET = 5; // 0x5 + field public static final byte SUBCLASS2_GAMEPAD = 2; // 0x2 + field public static final byte SUBCLASS2_JOYSTICK = 1; // 0x1 + field public static final byte SUBCLASS2_REMOTE_CONTROL = 3; // 0x3 + field public static final byte SUBCLASS2_SENSING_DEVICE = 4; // 0x4 + field public static final byte SUBCLASS2_UNCATEGORIZED = 0; // 0x0 + } + + public final class BluetoothHidDeviceAppQosSettings implements android.os.Parcelable { + ctor public BluetoothHidDeviceAppQosSettings(int, int, int, int, int, int); + method public int describeContents(); + method public int[] toArray(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppQosSettings> CREATOR; + field public static final int MAX = -1; // 0xffffffff + field public static final int SERVICE_BEST_EFFORT = 1; // 0x1 + field public static final int SERVICE_GUARANTEED = 2; // 0x2 + field public static final int SERVICE_NO_TRAFFIC = 0; // 0x0 + field public final int delayVariation; + field public final int latency; + field public final int peakBandwidth; + field public final int serviceType; + field public final int tokenBucketSize; + field public final int tokenRate; + } + + public static class BluetoothHidDeviceAppQosSettings.Builder { + ctor public BluetoothHidDeviceAppQosSettings.Builder(); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings build(); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder delayVariation(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder latency(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder peakBandwidth(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder serviceType(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenBucketSize(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenRate(int); + } + + public final class BluetoothHidDeviceAppSdpSettings implements android.os.Parcelable { + ctor public BluetoothHidDeviceAppSdpSettings(java.lang.String, java.lang.String, java.lang.String, byte, byte[]); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR; + field public final java.lang.String description; + field public final byte[] descriptors; + field public final java.lang.String name; + field public final java.lang.String provider; + field public final byte subclass; + } + + public abstract class BluetoothHidDeviceCallback { + ctor public BluetoothHidDeviceCallback(); + method public void onAppStatusChanged(android.bluetooth.BluetoothDevice, boolean); + method public void onConnectionStateChanged(android.bluetooth.BluetoothDevice, int); + method public void onGetReport(android.bluetooth.BluetoothDevice, byte, byte, int); + method public void onIntrData(android.bluetooth.BluetoothDevice, byte, byte[]); + method public void onSetProtocol(android.bluetooth.BluetoothDevice, byte); + method public void onSetReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]); + method public void onVirtualCableUnplug(android.bluetooth.BluetoothDevice); + } + public final class BluetoothManager { method public android.bluetooth.BluetoothAdapter getAdapter(); method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int); @@ -8138,6 +8244,7 @@ package android.bluetooth { field public static final int GATT_SERVER = 8; // 0x8 field public static final int HEADSET = 1; // 0x1 field public static final int HEALTH = 3; // 0x3 + field public static final int HID_DEVICE = 19; // 0x13 field public static final int SAP = 10; // 0xa field public static final int STATE_CONNECTED = 2; // 0x2 field public static final int STATE_CONNECTING = 1; // 0x1 @@ -25919,12 +26026,12 @@ package android.net { } public final class IpSecManager { + method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException; + method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; - method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException; - method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; } public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException { @@ -32123,6 +32230,7 @@ package android.os { field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_SMS = "no_sms"; field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs"; + field public static final java.lang.String DISALLOW_UNIFIED_PASSWORD = "no_unified_password"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone"; field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer"; @@ -41300,41 +41408,6 @@ package android.telephony.mbms { package android.test { - public abstract deprecated class ActivityInstrumentationTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase { - ctor public ActivityInstrumentationTestCase(java.lang.String, java.lang.Class<T>); - ctor public ActivityInstrumentationTestCase(java.lang.String, java.lang.Class<T>, boolean); - method public T getActivity(); - method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception; - } - - public abstract deprecated class ActivityInstrumentationTestCase2<T extends android.app.Activity> extends android.test.ActivityTestCase { - ctor public deprecated ActivityInstrumentationTestCase2(java.lang.String, java.lang.Class<T>); - ctor public ActivityInstrumentationTestCase2(java.lang.Class<T>); - method public T getActivity(); - method public void setActivityInitialTouchMode(boolean); - method public void setActivityIntent(android.content.Intent); - } - - public abstract deprecated class ActivityTestCase extends android.test.InstrumentationTestCase { - ctor public ActivityTestCase(); - method protected android.app.Activity getActivity(); - method protected void scrubClass(java.lang.Class<?>) throws java.lang.IllegalAccessException; - method protected void setActivity(android.app.Activity); - } - - public abstract deprecated class ActivityUnitTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase { - ctor public ActivityUnitTestCase(java.lang.Class<T>); - method public T getActivity(); - method public int getFinishedActivityRequest(); - method public int getRequestedOrientation(); - method public android.content.Intent getStartedActivityIntent(); - method public int getStartedActivityRequest(); - method public boolean isFinishCalled(); - method public void setActivityContext(android.content.Context); - method public void setApplication(android.app.Application); - method protected T startActivity(android.content.Intent, android.os.Bundle, java.lang.Object); - } - public deprecated class AndroidTestCase extends junit.framework.TestCase { ctor public AndroidTestCase(); method public void assertActivityRequiresPermission(java.lang.String, java.lang.String, java.lang.String); @@ -41347,46 +41420,6 @@ package android.test { field protected android.content.Context mContext; } - public deprecated class AndroidTestRunner extends junit.runner.BaseTestRunner { - ctor public AndroidTestRunner(); - method public void addTestListener(junit.framework.TestListener); - method public void clearTestListeners(); - method protected junit.framework.TestResult createTestResult(); - method public java.util.List<junit.framework.TestCase> getTestCases(); - method public java.lang.String getTestClassName(); - method public junit.framework.TestResult getTestResult(); - method protected java.lang.Class loadSuiteClass(java.lang.String) throws java.lang.ClassNotFoundException; - method protected void runFailed(java.lang.String); - method public void runTest(); - method public void runTest(junit.framework.TestResult); - method public void setContext(android.content.Context); - method public deprecated void setInstrumentaiton(android.app.Instrumentation); - method public void setInstrumentation(android.app.Instrumentation); - method public void setTest(junit.framework.Test); - method public void setTestClassName(java.lang.String, java.lang.String); - method public void testEnded(java.lang.String); - method public void testFailed(int, junit.framework.Test, java.lang.Throwable); - method public void testStarted(java.lang.String); - } - - public abstract deprecated class ApplicationTestCase<T extends android.app.Application> extends android.test.AndroidTestCase { - ctor public ApplicationTestCase(java.lang.Class<T>); - method protected final void createApplication(); - method public T getApplication(); - method public android.content.Context getSystemContext(); - method protected final void terminateApplication(); - method public final void testApplicationTestCaseSetUpProperly() throws java.lang.Exception; - } - - public deprecated class AssertionFailedError extends java.lang.Error { - ctor public AssertionFailedError(); - ctor public AssertionFailedError(java.lang.String); - } - - public deprecated class ComparisonFailure extends android.test.AssertionFailedError { - ctor public ComparisonFailure(java.lang.String, java.lang.String, java.lang.String); - } - public abstract deprecated class FlakyTest implements java.lang.annotation.Annotation { } @@ -41403,25 +41436,6 @@ package android.test { method public void sendRepeatedKeys(int...); } - public deprecated class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider { - ctor public InstrumentationTestRunner(); - method public junit.framework.TestSuite getAllTests(); - method protected android.test.AndroidTestRunner getAndroidTestRunner(); - method public android.os.Bundle getArguments(); - method public java.lang.ClassLoader getLoader(); - method public junit.framework.TestSuite getTestSuite(); - field public static final java.lang.String REPORT_KEY_NAME_CLASS = "class"; - field public static final java.lang.String REPORT_KEY_NAME_TEST = "test"; - field public static final java.lang.String REPORT_KEY_NUM_CURRENT = "current"; - field public static final java.lang.String REPORT_KEY_NUM_TOTAL = "numtests"; - field public static final java.lang.String REPORT_KEY_STACK = "stack"; - field public static final java.lang.String REPORT_VALUE_ID = "InstrumentationTestRunner"; - field public static final int REPORT_VALUE_RESULT_ERROR = -1; // 0xffffffff - field public static final int REPORT_VALUE_RESULT_FAILURE = -2; // 0xfffffffe - field public static final int REPORT_VALUE_RESULT_OK = 0; // 0x0 - field public static final int REPORT_VALUE_RESULT_START = 1; // 0x1 - } - public deprecated class InstrumentationTestSuite extends junit.framework.TestSuite { ctor public InstrumentationTestSuite(android.app.Instrumentation); ctor public InstrumentationTestSuite(java.lang.String, android.app.Instrumentation); @@ -41429,55 +41443,6 @@ package android.test { method public void addTestSuite(java.lang.Class); } - public deprecated class IsolatedContext extends android.content.ContextWrapper { - ctor public IsolatedContext(android.content.ContentResolver, android.content.Context); - method public java.util.List<android.content.Intent> getAndClearBroadcastIntents(); - } - - public class LoaderTestCase extends android.test.AndroidTestCase { - ctor public LoaderTestCase(); - method public <T> T getLoaderResultSynchronously(android.content.Loader<T>); - } - - public final deprecated class MoreAsserts { - method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Object); - method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Class<?>); - method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String, java.lang.String); - method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String); - method public static void assertContentsInAnyOrder(java.lang.String, java.lang.Iterable<?>, java.lang.Object...); - method public static void assertContentsInAnyOrder(java.lang.Iterable<?>, java.lang.Object...); - method public static void assertContentsInOrder(java.lang.String, java.lang.Iterable<?>, java.lang.Object...); - method public static void assertContentsInOrder(java.lang.Iterable<?>, java.lang.Object...); - method public static void assertEmpty(java.lang.String, java.lang.Iterable<?>); - method public static void assertEmpty(java.lang.Iterable<?>); - method public static void assertEmpty(java.lang.String, java.util.Map<?, ?>); - method public static void assertEmpty(java.util.Map<?, ?>); - method public static void assertEquals(java.lang.String, byte[], byte[]); - method public static void assertEquals(byte[], byte[]); - method public static void assertEquals(java.lang.String, int[], int[]); - method public static void assertEquals(int[], int[]); - method public static void assertEquals(java.lang.String, double[], double[]); - method public static void assertEquals(double[], double[]); - method public static void assertEquals(java.lang.String, java.lang.Object[], java.lang.Object[]); - method public static void assertEquals(java.lang.Object[], java.lang.Object[]); - method public static void assertEquals(java.lang.String, java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>); - method public static void assertEquals(java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>); - method public static java.util.regex.MatchResult assertMatchesRegex(java.lang.String, java.lang.String, java.lang.String); - method public static java.util.regex.MatchResult assertMatchesRegex(java.lang.String, java.lang.String); - method public static void assertNotContainsRegex(java.lang.String, java.lang.String, java.lang.String); - method public static void assertNotContainsRegex(java.lang.String, java.lang.String); - method public static void assertNotEmpty(java.lang.String, java.lang.Iterable<?>); - method public static void assertNotEmpty(java.lang.Iterable<?>); - method public static void assertNotEmpty(java.lang.String, java.util.Map<?, ?>); - method public static void assertNotEmpty(java.util.Map<?, ?>); - method public static void assertNotEqual(java.lang.String, java.lang.Object, java.lang.Object); - method public static void assertNotEqual(java.lang.Object, java.lang.Object); - method public static void assertNotMatchesRegex(java.lang.String, java.lang.String, java.lang.String); - method public static void assertNotMatchesRegex(java.lang.String, java.lang.String); - method public static void checkEqualsAndHashCodeMethods(java.lang.String, java.lang.Object, java.lang.Object, boolean); - method public static void checkEqualsAndHashCodeMethods(java.lang.Object, java.lang.Object, boolean); - } - public abstract deprecated interface PerformanceTestCase { method public abstract boolean isPerformanceOnly(); method public abstract int startPerformance(android.test.PerformanceTestCase.Intermediates); @@ -41491,119 +41456,9 @@ package android.test { method public abstract void startTiming(boolean); } - public abstract deprecated class ProviderTestCase<T extends android.content.ContentProvider> extends android.test.InstrumentationTestCase { - ctor public ProviderTestCase(java.lang.Class<T>, java.lang.String); - method public android.test.mock.MockContentResolver getMockContentResolver(); - method public android.test.IsolatedContext getMockContext(); - method public T getProvider(); - method public static <T extends android.content.ContentProvider> android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException; - } - - public abstract class ProviderTestCase2<T extends android.content.ContentProvider> extends android.test.AndroidTestCase { - ctor public ProviderTestCase2(java.lang.Class<T>, java.lang.String); - method public android.test.mock.MockContentResolver getMockContentResolver(); - method public android.test.IsolatedContext getMockContext(); - method public T getProvider(); - method public static <T extends android.content.ContentProvider> android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.String, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException; - } - - public deprecated class RenamingDelegatingContext extends android.content.ContextWrapper { - ctor public RenamingDelegatingContext(android.content.Context, java.lang.String); - ctor public RenamingDelegatingContext(android.content.Context, android.content.Context, java.lang.String); - method public java.lang.String getDatabasePrefix(); - method public void makeExistingFilesAndDbsAccessible(); - method public static <T extends android.content.ContentProvider> T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException; - method public static <T extends android.content.ContentProvider> T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String, boolean) throws java.lang.IllegalAccessException, java.lang.InstantiationException; - } - - public abstract deprecated class ServiceTestCase<T extends android.app.Service> extends android.test.AndroidTestCase { - ctor public ServiceTestCase(java.lang.Class<T>); - method protected android.os.IBinder bindService(android.content.Intent); - method public android.app.Application getApplication(); - method public T getService(); - method public android.content.Context getSystemContext(); - method public void setApplication(android.app.Application); - method protected void setupService(); - method protected void shutdownService(); - method protected void startService(android.content.Intent); - method public void testServiceTestCaseSetUpProperly() throws java.lang.Exception; - } - - public abstract deprecated class SingleLaunchActivityTestCase<T extends android.app.Activity> extends android.test.InstrumentationTestCase { - ctor public SingleLaunchActivityTestCase(java.lang.String, java.lang.Class<T>); - method public T getActivity(); - method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception; - } - - public deprecated class SyncBaseInstrumentation extends android.test.InstrumentationTestCase { - ctor public SyncBaseInstrumentation(); - method protected void cancelSyncsandDisableAutoSync(); - method protected void syncProvider(android.net.Uri, java.lang.String, java.lang.String) throws java.lang.Exception; - } - - public abstract deprecated interface TestSuiteProvider { - method public abstract junit.framework.TestSuite getTestSuite(); - } - - public deprecated class TouchUtils { - ctor public TouchUtils(); - method public static void clickView(android.test.InstrumentationTestCase, android.view.View); - method public static deprecated void drag(android.test.ActivityInstrumentationTestCase, float, float, float, float, int); - method public static void drag(android.test.InstrumentationTestCase, float, float, float, float, int); - method public static deprecated void dragQuarterScreenDown(android.test.ActivityInstrumentationTestCase); - method public static void dragQuarterScreenDown(android.test.InstrumentationTestCase, android.app.Activity); - method public static deprecated void dragQuarterScreenUp(android.test.ActivityInstrumentationTestCase); - method public static void dragQuarterScreenUp(android.test.InstrumentationTestCase, android.app.Activity); - method public static deprecated int dragViewBy(android.test.ActivityInstrumentationTestCase, android.view.View, int, int, int); - method public static deprecated int dragViewBy(android.test.InstrumentationTestCase, android.view.View, int, int, int); - method public static deprecated int dragViewTo(android.test.ActivityInstrumentationTestCase, android.view.View, int, int, int); - method public static int dragViewTo(android.test.InstrumentationTestCase, android.view.View, int, int, int); - method public static deprecated void dragViewToBottom(android.test.ActivityInstrumentationTestCase, android.view.View); - method public static void dragViewToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.View); - method public static deprecated void dragViewToBottom(android.test.ActivityInstrumentationTestCase, android.view.View, int); - method public static void dragViewToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.View, int); - method public static deprecated void dragViewToTop(android.test.ActivityInstrumentationTestCase, android.view.View); - method public static deprecated void dragViewToTop(android.test.ActivityInstrumentationTestCase, android.view.View, int); - method public static void dragViewToTop(android.test.InstrumentationTestCase, android.view.View); - method public static void dragViewToTop(android.test.InstrumentationTestCase, android.view.View, int); - method public static deprecated int dragViewToX(android.test.ActivityInstrumentationTestCase, android.view.View, int, int); - method public static int dragViewToX(android.test.InstrumentationTestCase, android.view.View, int, int); - method public static deprecated int dragViewToY(android.test.ActivityInstrumentationTestCase, android.view.View, int, int); - method public static int dragViewToY(android.test.InstrumentationTestCase, android.view.View, int, int); - method public static deprecated void longClickView(android.test.ActivityInstrumentationTestCase, android.view.View); - method public static void longClickView(android.test.InstrumentationTestCase, android.view.View); - method public static deprecated void scrollToBottom(android.test.ActivityInstrumentationTestCase, android.view.ViewGroup); - method public static void scrollToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.ViewGroup); - method public static deprecated void scrollToTop(android.test.ActivityInstrumentationTestCase, android.view.ViewGroup); - method public static void scrollToTop(android.test.InstrumentationTestCase, android.app.Activity, android.view.ViewGroup); - method public static void tapView(android.test.InstrumentationTestCase, android.view.View); - method public static void touchAndCancelView(android.test.InstrumentationTestCase, android.view.View); - } - public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation { } - public deprecated class ViewAsserts { - method public static void assertBaselineAligned(android.view.View, android.view.View); - method public static void assertBottomAligned(android.view.View, android.view.View); - method public static void assertBottomAligned(android.view.View, android.view.View, int); - method public static void assertGroupContains(android.view.ViewGroup, android.view.View); - method public static void assertGroupIntegrity(android.view.ViewGroup); - method public static void assertGroupNotContains(android.view.ViewGroup, android.view.View); - method public static void assertHasScreenCoordinates(android.view.View, android.view.View, int, int); - method public static void assertHorizontalCenterAligned(android.view.View, android.view.View); - method public static void assertLeftAligned(android.view.View, android.view.View); - method public static void assertLeftAligned(android.view.View, android.view.View, int); - method public static void assertOffScreenAbove(android.view.View, android.view.View); - method public static void assertOffScreenBelow(android.view.View, android.view.View); - method public static void assertOnScreen(android.view.View, android.view.View); - method public static void assertRightAligned(android.view.View, android.view.View); - method public static void assertRightAligned(android.view.View, android.view.View, int); - method public static void assertTopAligned(android.view.View, android.view.View); - method public static void assertTopAligned(android.view.View, android.view.View, int); - method public static void assertVerticalCenterAligned(android.view.View, android.view.View); - } - } package android.test.mock { @@ -41899,37 +41754,6 @@ package android.test.mock { } -package android.test.suitebuilder { - - public deprecated class TestMethod { - ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>); - ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>); - ctor public TestMethod(junit.framework.TestCase); - method public junit.framework.TestCase createTest() throws java.lang.IllegalAccessException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException; - method public <T extends java.lang.annotation.Annotation> T getAnnotation(java.lang.Class<T>); - method public java.lang.Class<? extends junit.framework.TestCase> getEnclosingClass(); - method public java.lang.String getEnclosingClassname(); - method public java.lang.String getName(); - } - - public deprecated class TestSuiteBuilder { - ctor public TestSuiteBuilder(java.lang.Class); - ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader); - method public final junit.framework.TestSuite build(); - method public android.test.suitebuilder.TestSuiteBuilder excludePackages(java.lang.String...); - method protected java.lang.String getSuiteName(); - method public final android.test.suitebuilder.TestSuiteBuilder includeAllPackagesUnderHere(); - method public android.test.suitebuilder.TestSuiteBuilder includePackages(java.lang.String...); - method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String); - } - - public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase { - ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception); - method public void testSuiteConstructionFailed(); - } - -} - package android.test.suitebuilder.annotation { public abstract deprecated class LargeTest implements java.lang.annotation.Annotation { @@ -47067,6 +46891,7 @@ package android.view { method public int getScaledEdgeSlop(); method public int getScaledFadingEdgeLength(); method public float getScaledHorizontalScrollFactor(); + method public int getScaledHoverSlop(); method public int getScaledMaximumDrawingCacheSize(); method public int getScaledMaximumFlingVelocity(); method public int getScaledMinimumFlingVelocity(); diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index e8b408328181..4777dcd32377 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -317,7 +317,7 @@ void AnomalyTracker::informAlarmsFired(const uint64_t& timestampNs, for (const auto& kv : matchedAlarms) { declareAnomaly(timestampNs /* TODO: , kv.first */); mAlarms.erase(kv.first); - firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. + firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. } } diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index e52b2739c991..164f88f3df59 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -29,6 +29,12 @@ namespace android { namespace os { namespace statsd { +using std::map; +using std::pair; +using std::set; +using std::string; +using std::vector; + #define STATS_SERVICE_DIR "/data/misc/stats-service" using android::base::StringPrintf; @@ -41,8 +47,14 @@ ConfigManager::~ConfigManager() { } void ConfigManager::Startup() { - StorageManager::readConfigFromDisk(mConfigs); - + map<ConfigKey, StatsdConfig> configsFromDisk; + StorageManager::readConfigFromDisk(configsFromDisk); + // TODO(b/70667694): Make the configs from disk be used. And remove the fake config, + // and tests shouldn't call this Startup(), maybe call StartupForTest() so we don't read + // configs from disk for tests. + // for (const auto& pair : configsFromDisk) { + // UpdateConfig(pair.first, pair.second); + //} // this should be called from StatsService when it receives a statsd_config UpdateConfig(ConfigKey(1000, "fake"), build_fake_config()); } @@ -52,9 +64,8 @@ void ConfigManager::AddListener(const sp<ConfigListener>& listener) { } void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) { - // Add to map - mConfigs[key] = config; - // Why doesn't this work? mConfigs.insert({key, config}); + // Add to set + mConfigs.insert(key); // Save to disk update_saved_configs(key, config); @@ -74,7 +85,7 @@ void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { } void ConfigManager::RemoveConfig(const ConfigKey& key) { - unordered_map<ConfigKey, StatsdConfig>::iterator it = mConfigs.find(key); + auto it = mConfigs.find(key); if (it != mConfigs.end()) { // Remove from map mConfigs.erase(it); @@ -100,9 +111,9 @@ void ConfigManager::RemoveConfigs(int uid) { for (auto it = mConfigs.begin(); it != mConfigs.end();) { // Remove from map - if (it->first.GetUid() == uid) { - removed.push_back(it->first); - mConfigReceivers.erase(it->first); + if (it->GetUid() == uid) { + removed.push_back(*it); + mConfigReceivers.erase(*it); it = mConfigs.erase(it); } else { it++; @@ -123,10 +134,10 @@ void ConfigManager::RemoveAllConfigs() { for (auto it = mConfigs.begin(); it != mConfigs.end();) { // Remove from map - removed.push_back(it->first); - auto receiverIt = mConfigReceivers.find(it->first); + removed.push_back(*it); + auto receiverIt = mConfigReceivers.find(*it); if (receiverIt != mConfigReceivers.end()) { - mConfigReceivers.erase(it->first); + mConfigReceivers.erase(*it); } it = mConfigs.erase(it); } @@ -143,7 +154,7 @@ void ConfigManager::RemoveAllConfigs() { vector<ConfigKey> ConfigManager::GetAllConfigKeys() const { vector<ConfigKey> ret; for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) { - ret.push_back(it->first); + ret.push_back(*it); } return ret; } @@ -160,15 +171,13 @@ const pair<string, string> ConfigManager::GetConfigReceiver(const ConfigKey& key void ConfigManager::Dump(FILE* out) { fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size()); fprintf(out, " uid name\n"); - for (unordered_map<ConfigKey, StatsdConfig>::const_iterator it = mConfigs.begin(); - it != mConfigs.end(); it++) { - fprintf(out, " %6d %s\n", it->first.GetUid(), it->first.GetName().c_str()); - auto receiverIt = mConfigReceivers.find(it->first); + for (const auto& key : mConfigs) { + fprintf(out, " %6d %s\n", key.GetUid(), key.GetName().c_str()); + auto receiverIt = mConfigReceivers.find(key); if (receiverIt != mConfigReceivers.end()) { fprintf(out, " -> received by %s, %s\n", receiverIt->second.first.c_str(), receiverIt->second.second.c_str()); } - // TODO: Print the contents of the config too. } } @@ -227,7 +236,8 @@ StatsdConfig build_fake_config() { metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); // Anomaly threshold for screen-on count. - Alert* alert = config.add_alert(); + // TODO(b/70627390): Uncomment once the bug is fixed. + /*Alert* alert = config.add_alert(); alert->set_name("ALERT_1"); alert->set_metric_name("METRIC_1"); alert->set_number_of_buckets(6); @@ -235,7 +245,7 @@ StatsdConfig build_fake_config() { alert->set_refractory_period_secs(30); Alert::IncidentdDetails* details = alert->mutable_incidentd_details(); details->add_section(12); - details->add_section(13); + details->add_section(13);*/ // Count process state changes, slice by uid. metric = config.add_count_metric(); @@ -246,6 +256,8 @@ StatsdConfig build_fake_config() { keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY); // Anomaly threshold for background count. + // TODO(b/70627390): Uncomment once the bug is fixed. + /* alert = config.add_alert(); alert->set_name("ALERT_2"); alert->set_metric_name("METRIC_2"); @@ -254,7 +266,7 @@ StatsdConfig build_fake_config() { alert->set_refractory_period_secs(20); details = alert->mutable_incidentd_details(); details->add_section(14); - details->add_section(15); + details->add_section(15);*/ // Count process state changes, slice by uid, while SCREEN_IS_OFF metric = config.add_count_metric(); @@ -326,6 +338,8 @@ StatsdConfig build_fake_config() { durationMetric->set_what("SCREEN_IS_ON"); // Anomaly threshold for background count. + // TODO(b/70627390): Uncomment once the bug is fixed. + /* alert = config.add_alert(); alert->set_name("ALERT_8"); alert->set_metric_name("METRIC_8"); @@ -333,7 +347,7 @@ StatsdConfig build_fake_config() { alert->set_trigger_if_sum_gt(2000000000); // 2 seconds alert->set_refractory_period_secs(120); details = alert->mutable_incidentd_details(); - details->add_section(-1); + details->add_section(-1);*/ // Value metric to count KERNEL_WAKELOCK when screen turned on ValueMetric* valueMetric = config.add_value_metric(); diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h index 90ea6c0ca064..ea42a3595d98 100644 --- a/cmds/statsd/src/config/ConfigManager.h +++ b/cmds/statsd/src/config/ConfigManager.h @@ -19,8 +19,9 @@ #include "config/ConfigKey.h" #include "config/ConfigListener.h" +#include <map> +#include <set> #include <string> -#include <unordered_map> #include <stdio.h> @@ -28,13 +29,7 @@ namespace android { namespace os { namespace statsd { -using android::RefBase; -using std::string; -using std::unordered_map; -using std::vector; -using std::pair; - -// Util function to Hard code a test metric for counting screen on events. +// Util function to build a hard coded config with test metrics. StatsdConfig build_fake_config(); /** @@ -43,7 +38,7 @@ StatsdConfig build_fake_config(); * TODO: Store the configs persistently too. * TODO: Dump method for debugging. */ -class ConfigManager : public virtual RefBase { +class ConfigManager : public virtual android::RefBase { public: ConfigManager(); virtual ~ConfigManager(); @@ -68,17 +63,17 @@ public: /** * Sets the broadcast receiver for a configuration key. */ - void SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls); + void SetConfigReceiver(const ConfigKey& key, const std::string& pkg, const std::string& cls); /** * Returns the package name and class name representing the broadcast receiver for this config. */ - const pair<string, string> GetConfigReceiver(const ConfigKey& key) const; + const std::pair<std::string, std::string> GetConfigReceiver(const ConfigKey& key) const; /** * Returns all config keys registered. */ - vector<ConfigKey> GetAllConfigKeys() const; + std::vector<ConfigKey> GetAllConfigKeys() const; /** * Erase any broadcast receiver associated with this config key. @@ -121,18 +116,18 @@ private: /** * The Configs that have been set. Each config should */ - unordered_map<ConfigKey, StatsdConfig> mConfigs; + std::set<ConfigKey> mConfigs; /** * Each config key can be subscribed by up to one receiver, specified as the package name and * class name. */ - unordered_map<ConfigKey, pair<string, string>> mConfigReceivers; + std::map<ConfigKey, std::pair<std::string, std::string>> mConfigReceivers; /** * The ConfigListeners that will be told about changes. */ - vector<sp<ConfigListener>> mListeners; + std::vector<sp<ConfigListener>> mListeners; }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index ae297d9eca0d..7b865c2752d5 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -66,7 +66,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric const int conditionIndex, const sp<ConditionWizard>& wizard, const uint64_t startTimeNs) - : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) { + : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; @@ -92,18 +92,18 @@ CountMetricProducer::~CountMetricProducer() { } void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { - VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); + VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); } void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); + protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); - VLOG("metric %s dump report now...", mMetric.name().c_str()); + VLOG("metric %s dump report now...", mName.c_str()); for (const auto& counter : mPastBuckets) { const HashableDimensionKey& hashableKey = counter.first; @@ -160,7 +160,7 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { - VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); + VLOG("Metric %s onConditionChanged", mName.c_str()); mCondition = conditionMet; } @@ -172,11 +172,10 @@ bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedCounter->size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("CountMetric %s dropping data for dimension key %s", mMetric.name().c_str(), + ALOGE("CountMetric %s dropping data for dimension key %s", mName.c_str(), newKey.c_str()); return true; } @@ -218,7 +217,7 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( mCurrentSlicedCounter->find(eventKey)->second); } - VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(), + VLOG("metric %s %s->%lld", mName.c_str(), eventKey.c_str(), (long long)(*mCurrentSlicedCounter)[eventKey]); } @@ -237,7 +236,7 @@ void CountMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { info.mCount = counter.second; auto& bucketList = mPastBuckets[counter.first]; bucketList.push_back(info); - VLOG("metric %s, dump key value: %s -> %lld", mMetric.name().c_str(), counter.first.c_str(), + VLOG("metric %s, dump key value: %s -> %lld", mName.c_str(), counter.first.c_str(), (long long)counter.second); } @@ -250,7 +249,7 @@ void CountMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; mCurrentBucketNum += numBucketsForward; - VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(), + VLOG("metric %s: new bucket start time: %lld", mName.c_str(), (long long)mCurrentBucketStartTimeNs); } diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 8a17169724ca..59995d2d845e 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -76,8 +76,6 @@ private: // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& newEventTime); - const CountMetric mMetric; - // TODO: Add a lock to mPastBuckets. std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index c26879812f63..6afbe458305c 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -68,8 +68,8 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat const sp<ConditionWizard>& wizard, const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs) - : MetricProducer(key, startTimeNs, conditionIndex, wizard), - mMetric(metric), + : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard), + mAggregationType(metric.aggregation_type()), mStartIndex(startIndex), mStopIndex(stopIndex), mStopAllIndex(stopAllIndex), @@ -114,20 +114,20 @@ sp<AnomalyTracker> DurationMetricProducer::createAnomalyTracker(const Alert &ale unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( const HashableDimensionKey& eventKey) const { - switch (mMetric.aggregation_type()) { + switch (mAggregationType) { case DurationMetric_AggregationType_SUM: return make_unique<OringDurationTracker>( - mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested, + mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested, mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); case DurationMetric_AggregationType_MAX_SPARSE: return make_unique<MaxDurationTracker>( - mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested, + mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested, mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); } } void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { - VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); + VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); flushIfNeededLocked(eventTime); // Now for each of the on-going event, check if the condition has changed for them. for (auto& pair : mCurrentSlicedDuration) { @@ -137,7 +137,7 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { - VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); + VLOG("Metric %s onConditionChanged", mName.c_str()); mCondition = conditionMet; flushIfNeededLocked(eventTime); // TODO: need to populate the condition change time from the event which triggers the condition @@ -151,11 +151,11 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); + protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS); - VLOG("metric %s dump report now...", mMetric.name().c_str()); + VLOG("metric %s dump report now...", mName.c_str()); for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; @@ -236,11 +236,10 @@ bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newK // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedDuration.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("DurationMetric %s dropping data for dimension key %s", mMetric.name().c_str(), + ALOGE("DurationMetric %s dropping data for dimension key %s", mName.c_str(), newKey.c_str()); return true; } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 14504c1f2eff..e7aca7f42375 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -75,7 +75,7 @@ private: // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& eventTime); - const DurationMetric mMetric; + const DurationMetric_AggregationType mAggregationType; // Index of the SimpleAtomMatcher which defines the start. const size_t mStartIndex; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index bcecf163e243..4752997d6d15 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -55,7 +55,7 @@ EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric const int conditionIndex, const sp<ConditionWizard>& wizard, const uint64_t startTimeNs) - : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) { + : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) { if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), metric.links().end()); @@ -98,12 +98,12 @@ std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& pr void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); + protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); size_t bufferSize = mProto->size(); - VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize); + VLOG("metric %s dump report now... proto size: %zu ", mName.c_str(), bufferSize); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(*mProto); protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS, @@ -115,7 +115,7 @@ void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { - VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); + VLOG("Metric %s onConditionChanged", mName.c_str()); mCondition = conditionMet; } diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 49ba9d863d20..d720ead50bad 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -67,8 +67,6 @@ private: // Internal function to calculate the current used bytes. size_t byteSizeLocked() const override; - const EventMetric mMetric; - // Maps to a EventMetricDataWrapper. Storing atom events in ProtoOutputStream // is more space efficient than storing LogEvent. std::unique_ptr<android::util::ProtoOutputStream> mProto; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index fffb2bfd7031..ae9b86fc3c9d 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -67,8 +67,8 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric const int conditionIndex, const sp<ConditionWizard>& wizard, const int pullTagId, const int64_t startTimeNs) - : MetricProducer(key, startTimeNs, conditionIndex, wizard), - mMetric(metric), + : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard), + mGaugeField(metric.gauge_field()), mPullTagId(pullTagId) { if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; @@ -104,11 +104,11 @@ GaugeMetricProducer::~GaugeMetricProducer() { void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { - VLOG("gauge metric %s dump report now...", mMetric.name().c_str()); + VLOG("gauge metric %s dump report now...", mName.c_str()); flushIfNeededLocked(dumpTimeNs); - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); + protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS); @@ -166,7 +166,7 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { - VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); + VLOG("Metric %s onConditionChanged", mName.c_str()); flushIfNeededLocked(eventTime); mCondition = conditionMet; @@ -193,12 +193,12 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, } void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { - VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); + VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); } int64_t GaugeMetricProducer::getGauge(const LogEvent& event) { status_t err = NO_ERROR; - int64_t val = event.GetLong(mMetric.gauge_field(), &err); + int64_t val = event.GetLong(mGaugeField, &err); if (err == NO_ERROR) { return val; } else { @@ -222,11 +222,10 @@ bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedBucket->size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("GaugeMetric %s dropping data for dimension key %s", mMetric.name().c_str(), + ALOGE("GaugeMetric %s dropping data for dimension key %s", mName.c_str(), newKey.c_str()); return true; } @@ -289,8 +288,8 @@ void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { info.mGauge = slice.second; auto& bucketList = mPastBuckets[slice.first]; bucketList.push_back(info); - VLOG("gauge metric %s, dump key value: %s -> %lld", mMetric.name().c_str(), - slice.first.c_str(), (long long)slice.second); + VLOG("gauge metric %s, dump key value: %s -> %lld", mName.c_str(), slice.first.c_str(), + (long long)slice.second); } // Reset counters @@ -304,7 +303,7 @@ void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; mCurrentBucketNum += numBucketsForward; - VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(), + VLOG("metric %s: new bucket start time: %lld", mName.c_str(), (long long)mCurrentBucketStartTimeNs); } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index ee4f40c97d58..6e6f2bbdf945 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -87,7 +87,7 @@ private: // The default bucket size for gauge metric is 1 second. static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000; - const GaugeMetric mMetric; + const int32_t mGaugeField; StatsPullerManager mStatsPullerManager; // tagId for pulled data. -1 if this is not pulled diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 269bd435e32d..d4a2195a798c 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -38,9 +38,10 @@ namespace statsd { // be a no-op. class MetricProducer : public virtual PackageInfoListener { public: - MetricProducer(const ConfigKey& key, const int64_t startTimeNs, const int conditionIndex, - const sp<ConditionWizard>& wizard) - : mConfigKey(key), + MetricProducer(const std::string& name, const ConfigKey& key, const int64_t startTimeNs, + const int conditionIndex, const sp<ConditionWizard>& wizard) + : mName(name), + mConfigKey(key), mStartTimeNs(startTimeNs), mCurrentBucketStartTimeNs(startTimeNs), mCurrentBucketNum(0), @@ -108,6 +109,8 @@ protected: android::util::ProtoOutputStream* protoOutput) = 0; virtual size_t byteSizeLocked() const = 0; + const std::string mName; + const ConfigKey mConfigKey; // The start time for the current in memory metrics data. diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index b0f0135f4121..3d0e20c54b71 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -50,11 +50,6 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config) mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap); - // TODO: add alert size. - // no matter whether this config is valid, log it in the stats. - StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(), - mAllConditionTrackers.size(), - mAllAtomMatchers.size(), 0, mConfigValid); // Guardrail. Reject the config if it's too big. if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig || mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig || @@ -62,6 +57,12 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config) ALOGE("This config is too big! Reject!"); mConfigValid = false; } + + // TODO: add alert size. + // no matter whether this config is valid, log it in the stats. + StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(), + mAllConditionTrackers.size(), + mAllAtomMatchers.size(), 0, mConfigValid); } MetricsManager::~MetricsManager() { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index aabe5af698ec..9400a1c435eb 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -73,13 +73,13 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric const sp<ConditionWizard>& wizard, const int pullTagId, const uint64_t startTimeNs, shared_ptr<StatsPullerManager> statsPullerManager) - : MetricProducer(key, startTimeNs, conditionIndex, wizard), - mMetric(metric), + : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard), + mValueField(metric.value_field()), mStatsPullerManager(statsPullerManager), mPullTagId(pullTagId) { // TODO: valuemetric for pushed events may need unlimited bucket length if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { - mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000; + mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; } else { mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000; } @@ -118,14 +118,14 @@ ValueMetricProducer::~ValueMetricProducer() { } void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { - VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); + VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); } void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { - VLOG("metric %s dump report now...", mMetric.name().c_str()); + VLOG("metric %s dump report now...", mName.c_str()); flushIfNeededLocked(dumpTimeNs); - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); + protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS); @@ -175,7 +175,7 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->end(protoToken); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); - VLOG("metric %s dump report now...", mMetric.name().c_str()); + VLOG("metric %s dump report now...", mName.c_str()); mPastBuckets.clear(); mStartTimeNs = mCurrentBucketStartTimeNs; // TODO: Clear mDimensionKeyMap once the report is dumped. @@ -194,8 +194,7 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, const u if (mPullTagId != -1) { if (mCondition == true) { - mStatsPullerManager->RegisterReceiver(mPullTagId, this, - mMetric.bucket().bucket_size_millis()); + mStatsPullerManager->RegisterReceiver(mPullTagId, this, mBucketSizeNs / 1000 / 1000); } else if (mCondition == false) { mStatsPullerManager->UnRegisterReceiver(mPullTagId, this); } @@ -216,7 +215,7 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, const u void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { std::lock_guard<std::mutex> lock(mMutex); - if (mCondition == true || !mMetric.has_condition()) { + if (mCondition == true || mConditionTrackerIndex < 0) { if (allData.size() == 0) { return; } @@ -247,11 +246,10 @@ bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) } if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedBucket.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("ValueMetric %s dropping data for dimension key %s", mMetric.name().c_str(), + ALOGE("ValueMetric %s dropping data for dimension key %s", mName.c_str(), newKey.c_str()); return true; } @@ -300,7 +298,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( long ValueMetricProducer::get_value(const LogEvent& event) { status_t err = NO_ERROR; - long val = event.GetLong(mMetric.value_field(), &err); + long val = event.GetLong(mValueField, &err); if (err == NO_ERROR) { return val; } else { @@ -342,7 +340,7 @@ void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { if (numBucketsForward > 1) { VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); } - VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(), + VLOG("metric %s: new bucket start time: %lld", mName.c_str(), (long long)mCurrentBucketStartTimeNs); } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 4c49927d9203..62e5d52b2b11 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -74,7 +74,7 @@ private: // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& eventTime); - const ValueMetric mMetric; + const int32_t mValueField; std::shared_ptr<StatsPullerManager> mStatsPullerManager; diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 3a4dfdaaf54d..9919abf7532a 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -23,13 +23,14 @@ #include <android-base/file.h> #include <dirent.h> -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_MESSAGE; - namespace android { namespace os { namespace statsd { +using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_MESSAGE; +using std::map; + #define STATS_SERVICE_DIR "/data/misc/stats-service" // for ConfigMetricsReportList @@ -170,7 +171,7 @@ void StorageManager::appendConfigMetricsReport(const char* path, ProtoOutputStre } } -void StorageManager::readConfigFromDisk(unordered_map<ConfigKey, StatsdConfig>& configsMap) { +void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) { unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir); if (dir == NULL) { VLOG("no default config on disk"); diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index 4c9abe58dbea..caf5b8b42887 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -66,7 +66,7 @@ public: /** * Call to load the saved configs from disk. */ - static void readConfigFromDisk(unordered_map<ConfigKey, StatsdConfig>& configsMap); + static void readConfigFromDisk(std::map<ConfigKey, StatsdConfig>& configsMap); }; } // namespace statsd diff --git a/cmds/wm/Android.mk b/cmds/wm/Android.mk index 3f3795fc4460..693c6e77d317 100644 --- a/cmds/wm/Android.mk +++ b/cmds/wm/Android.mk @@ -3,11 +3,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_MODULE := wm -include $(BUILD_JAVA_LIBRARY) - -include $(CLEAR_VARS) LOCAL_MODULE := wm LOCAL_SRC_FILES := wm LOCAL_MODULE_CLASS := EXECUTABLES diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java deleted file mode 100644 index 8defb331e289..000000000000 --- a/cmds/wm/src/com/android/commands/wm/Wm.java +++ /dev/null @@ -1,296 +0,0 @@ -/* -** -** Copyright 2013, 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.commands.wm; - -import android.content.Context; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.util.AndroidException; -import android.util.DisplayMetrics; -import android.system.Os; -import android.view.Display; -import android.view.IWindowManager; -import com.android.internal.os.BaseCommand; - -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.DataInputStream; -import java.io.PrintStream; -import java.lang.Runtime; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Wm extends BaseCommand { - - private IWindowManager mWm; - - /** - * Command-line entry point. - * - * @param args The command-line arguments - */ - public static void main(String[] args) { - (new Wm()).run(args); - } - - @Override - public void onShowUsage(PrintStream out) { - out.println( - "usage: wm [subcommand] [options]\n" + - " wm size [reset|WxH|WdpxHdp]\n" + - " wm density [reset|DENSITY]\n" + - " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" + - " wm scaling [off|auto]\n" + - " wm screen-capture [userId] [true|false]\n" + - "\n" + - "wm size: return or override display size.\n" + - " width and height in pixels unless suffixed with 'dp'.\n" + - "\n" + - "wm density: override display density.\n" + - "\n" + - "wm overscan: set overscan area for display.\n" + - "\n" + - "wm scaling: set display scaling mode.\n" + - "\n" + - "wm screen-capture: enable/disable screen capture.\n" + - "\n" + - "wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " + - "necessary.\n" + - "\n" + - "wm surface-trace: log surface commands to stdout in a binary format.\n" - ); - } - - @Override - public void onRun() throws Exception { - mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService( - Context.WINDOW_SERVICE)); - if (mWm == null) { - System.err.println(NO_SYSTEM_ERROR_CODE); - throw new AndroidException("Can't connect to window manager; is the system running?"); - } - - String op = nextArgRequired(); - - if (op.equals("size")) { - runDisplaySize(); - } else if (op.equals("density")) { - runDisplayDensity(); - } else if (op.equals("overscan")) { - runDisplayOverscan(); - } else if (op.equals("scaling")) { - runDisplayScaling(); - } else if (op.equals("screen-capture")) { - runSetScreenCapture(); - } else if (op.equals("dismiss-keyguard")) { - runDismissKeyguard(); - } else if (op.equals("surface-trace")) { - runSurfaceTrace(); - } else { - showError("Error: unknown command '" + op + "'"); - return; - } - } - - private void runSurfaceTrace() throws Exception { - ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(FileDescriptor.out); - mWm.enableSurfaceTrace(pfd); - - try { - // No one is going to wake us up, we are just waiting on SIGINT. Otherwise - // the WM can happily continue writing to our stdout. - synchronized (this) { - this.wait(); - } - } finally { - mWm.disableSurfaceTrace(); - } - } - - private void runSetScreenCapture() throws Exception { - String userIdStr = nextArg(); - String enableStr = nextArg(); - int userId; - boolean disable; - - try { - userId = Integer.parseInt(userIdStr); - } catch (NumberFormatException e) { - System.err.println("Error: bad number " + e); - return; - } - - disable = !Boolean.parseBoolean(enableStr); - - try { - mWm.setScreenCaptureDisabled(userId, disable); - } catch (RemoteException e) { - System.err.println("Error: Can't set screen capture " + e); - } - } - - private void runDisplaySize() throws Exception { - String size = nextArg(); - int w, h; - if (size == null) { - Point initialSize = new Point(); - Point baseSize = new Point(); - try { - mWm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize); - mWm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize); - System.out.println("Physical size: " + initialSize.x + "x" + initialSize.y); - if (!initialSize.equals(baseSize)) { - System.out.println("Override size: " + baseSize.x + "x" + baseSize.y); - } - } catch (RemoteException e) { - } - return; - } else if ("reset".equals(size)) { - w = h = -1; - } else { - int div = size.indexOf('x'); - if (div <= 0 || div >= (size.length()-1)) { - System.err.println("Error: bad size " + size); - return; - } - String wstr = size.substring(0, div); - String hstr = size.substring(div+1); - try { - w = parseDimension(wstr); - h = parseDimension(hstr); - } catch (NumberFormatException e) { - System.err.println("Error: bad number " + e); - return; - } - } - - try { - if (w >= 0 && h >= 0) { - // TODO(multidisplay): For now Configuration only applies to main screen. - mWm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h); - } else { - mWm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY); - } - } catch (RemoteException e) { - } - } - - private void runDisplayDensity() throws Exception { - String densityStr = nextArg(); - int density; - if (densityStr == null) { - try { - int initialDensity = mWm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY); - int baseDensity = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY); - System.out.println("Physical density: " + initialDensity); - if (initialDensity != baseDensity) { - System.out.println("Override density: " + baseDensity); - } - } catch (RemoteException e) { - } - return; - } else if ("reset".equals(densityStr)) { - density = -1; - } else { - try { - density = Integer.parseInt(densityStr); - } catch (NumberFormatException e) { - System.err.println("Error: bad number " + e); - return; - } - if (density < 72) { - System.err.println("Error: density must be >= 72"); - return; - } - } - - try { - if (density > 0) { - // TODO(multidisplay): For now Configuration only applies to main screen. - mWm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density, - UserHandle.USER_CURRENT); - } else { - mWm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, - UserHandle.USER_CURRENT); - } - } catch (RemoteException e) { - } - } - - private void runDisplayOverscan() throws Exception { - String overscanStr = nextArgRequired(); - Rect rect = new Rect(); - if ("reset".equals(overscanStr)) { - rect.set(0, 0, 0, 0); - } else { - final Pattern FLATTENED_PATTERN = Pattern.compile( - "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)"); - Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr); - if (!matcher.matches()) { - System.err.println("Error: bad rectangle arg: " + overscanStr); - return; - } - rect.left = Integer.parseInt(matcher.group(1)); - rect.top = Integer.parseInt(matcher.group(2)); - rect.right = Integer.parseInt(matcher.group(3)); - rect.bottom = Integer.parseInt(matcher.group(4)); - } - - try { - mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom); - } catch (RemoteException e) { - } - } - - private void runDisplayScaling() throws Exception { - String scalingStr = nextArgRequired(); - if ("auto".equals(scalingStr)) { - mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0); - } else if ("off".equals(scalingStr)) { - mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1); - } else { - System.err.println("Error: scaling must be 'auto' or 'off'"); - } - } - - private void runDismissKeyguard() throws Exception { - mWm.dismissKeyguard(null /* callback */); - } - - private int parseDimension(String s) throws NumberFormatException { - if (s.endsWith("px")) { - return Integer.parseInt(s.substring(0, s.length() - 2)); - } - if (s.endsWith("dp")) { - int density; - try { - density = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY); - } catch (RemoteException e) { - density = DisplayMetrics.DENSITY_DEFAULT; - } - return Integer.parseInt(s.substring(0, s.length() - 2)) * density / - DisplayMetrics.DENSITY_DEFAULT; - } - return Integer.parseInt(s); - } -} diff --git a/cmds/wm/wm b/cmds/wm/wm index 16d6bd649ef2..cb45be20c24a 100755 --- a/cmds/wm/wm +++ b/cmds/wm/wm @@ -1,7 +1,2 @@ #!/system/bin/sh -# Script to start "wm" on the device, which has a very rudimentary -# shell. -# -base=/system -export CLASSPATH=$base/framework/wm.jar -exec app_process $base/bin com.android.commands.wm.Wm "$@" +cmd window "$@" diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ef446c2d72c3..da57efa94599 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -377,7 +377,7 @@ public final class ActivityThread extends ClientTransactionHandler { ActivityInfo activityInfo; CompatibilityInfo compatInfo; - public LoadedApk packageInfo; + public LoadedApk loadedApk; List<ResultInfo> pendingResults; List<ReferrerIntent> pendingIntents; @@ -420,7 +420,7 @@ public final class ActivityThread extends ClientTransactionHandler { this.isForward = isForward; this.profilerInfo = profilerInfo; this.overrideConfig = overrideConfig; - this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo, + this.loadedApk = client.getLoadedApkNoCheck(activityInfo.applicationInfo, compatInfo); init(); } @@ -602,7 +602,7 @@ public final class ActivityThread extends ClientTransactionHandler { } static final class AppBindData { - LoadedApk info; + LoadedApk loadedApk; String processName; ApplicationInfo appInfo; List<ProviderInfo> providers; @@ -1774,13 +1774,13 @@ public final class ActivityThread extends ClientTransactionHandler { return mH; } - public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - int flags) { - return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); + public final LoadedApk getLoadedApkForPackageName(String packageName, + CompatibilityInfo compatInfo, int flags) { + return getLoadedApkForPackageName(packageName, compatInfo, flags, UserHandle.myUserId()); } - public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - int flags, int userId) { + public final LoadedApk getLoadedApkForPackageName(String packageName, + CompatibilityInfo compatInfo, int flags, int userId) { final boolean differentUser = (UserHandle.myUserId() != userId); synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; @@ -1793,13 +1793,13 @@ public final class ActivityThread extends ClientTransactionHandler { ref = mResourcePackages.get(packageName); } - LoadedApk packageInfo = ref != null ? ref.get() : null; - //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo); - //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir - // + ": " + packageInfo.mResources.getAssets().isUpToDate()); - if (packageInfo != null && (packageInfo.mResources == null - || packageInfo.mResources.getAssets().isUpToDate())) { - if (packageInfo.isSecurityViolation() + LoadedApk loadedApk = ref != null ? ref.get() : null; + //Slog.i(TAG, "getLoadedApkForPackageName " + packageName + ": " + loadedApk); + //if (loadedApk != null) Slog.i(TAG, "isUptoDate " + loadedApk.mResDir + // + ": " + loadedApk.mResources.getAssets().isUpToDate()); + if (loadedApk != null && (loadedApk.mResources == null + || loadedApk.mResources.getAssets().isUpToDate())) { + if (loadedApk.isSecurityViolation() && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) { throw new SecurityException( "Requesting code from " + packageName @@ -1807,7 +1807,7 @@ public final class ActivityThread extends ClientTransactionHandler { + mBoundApplication.processName + "/" + mBoundApplication.appInfo.uid); } - return packageInfo; + return loadedApk; } } @@ -1822,13 +1822,13 @@ public final class ActivityThread extends ClientTransactionHandler { } if (ai != null) { - return getPackageInfo(ai, compatInfo, flags); + return getLoadedApk(ai, compatInfo, flags); } return null; } - public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, + public final LoadedApk getLoadedApk(ApplicationInfo ai, CompatibilityInfo compatInfo, int flags) { boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; boolean securityViolation = includeCode && ai.uid != 0 @@ -1850,17 +1850,17 @@ public final class ActivityThread extends ClientTransactionHandler { throw new SecurityException(msg); } } - return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode, + return getLoadedApk(ai, compatInfo, null, securityViolation, includeCode, registerPackage); } @Override - public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, + public final LoadedApk getLoadedApkNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { - return getPackageInfo(ai, compatInfo, null, false, true, false); + return getLoadedApk(ai, compatInfo, null, false, true, false); } - public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) { + public final LoadedApk peekLoadedApk(String packageName, boolean includeCode) { synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; if (includeCode) { @@ -1872,7 +1872,7 @@ public final class ActivityThread extends ClientTransactionHandler { } } - private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, + private LoadedApk getLoadedApk(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode, boolean registerPackage) { final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); @@ -1887,35 +1887,35 @@ public final class ActivityThread extends ClientTransactionHandler { ref = mResourcePackages.get(aInfo.packageName); } - LoadedApk packageInfo = ref != null ? ref.get() : null; - if (packageInfo == null || (packageInfo.mResources != null - && !packageInfo.mResources.getAssets().isUpToDate())) { + LoadedApk loadedApk = ref != null ? ref.get() : null; + if (loadedApk == null || (loadedApk.mResources != null + && !loadedApk.mResources.getAssets().isUpToDate())) { if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package " : "Loading resource-only package ") + aInfo.packageName + " (in " + (mBoundApplication != null ? mBoundApplication.processName : null) + ")"); - packageInfo = + loadedApk = new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); if (mSystemThread && "android".equals(aInfo.packageName)) { - packageInfo.installSystemApplicationInfo(aInfo, - getSystemContext().mPackageInfo.getClassLoader()); + loadedApk.installSystemApplicationInfo(aInfo, + getSystemContext().mLoadedApk.getClassLoader()); } if (differentUser) { // Caching not supported across users } else if (includeCode) { mPackages.put(aInfo.packageName, - new WeakReference<LoadedApk>(packageInfo)); + new WeakReference<LoadedApk>(loadedApk)); } else { mResourcePackages.put(aInfo.packageName, - new WeakReference<LoadedApk>(packageInfo)); + new WeakReference<LoadedApk>(loadedApk)); } } - return packageInfo; + return loadedApk; } } @@ -2627,8 +2627,8 @@ public final class ActivityThread extends ClientTransactionHandler { /** Core implementation of activity launch. */ private Activity performLaunchActivity(ActivityClientRecord r) { ActivityInfo aInfo = r.activityInfo; - if (r.packageInfo == null) { - r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, + if (r.loadedApk == null) { + r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } @@ -2665,15 +2665,15 @@ public final class ActivityThread extends ClientTransactionHandler { } try { - Application app = r.packageInfo.makeApplication(false, mInstrumentation); + Application app = r.loadedApk.makeApplication(false, mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() - + ", pkg=" + r.packageInfo.getPackageName() + + ", pkg=" + r.loadedApk.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() - + ", dir=" + r.packageInfo.getAppDir()); + + ", dir=" + r.loadedApk.getAppDir()); if (activity != null) { CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); @@ -2813,7 +2813,7 @@ public final class ActivityThread extends ClientTransactionHandler { } ContextImpl appContext = ContextImpl.createActivityContext( - this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); + this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig); final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); // For debugging purposes, if the activity's package name contains the value of @@ -2821,7 +2821,7 @@ public final class ActivityThread extends ClientTransactionHandler { // its content on a secondary display if there is one. String pkgName = SystemProperties.get("debug.second-display.pkg"); if (pkgName != null && !pkgName.isEmpty() - && r.packageInfo.mPackageName.contains(pkgName)) { + && r.loadedApk.mPackageName.contains(pkgName)) { for (int id : dm.getDisplayIds()) { if (id != Display.DEFAULT_DISPLAY) { Display display = @@ -3141,7 +3141,7 @@ public final class ActivityThread extends ClientTransactionHandler { String component = data.intent.getComponent().getClassName(); - LoadedApk packageInfo = getPackageInfoNoCheck( + LoadedApk loadedApk = getLoadedApkNoCheck( data.info.applicationInfo, data.compatInfo); IActivityManager mgr = ActivityManager.getService(); @@ -3150,7 +3150,7 @@ public final class ActivityThread extends ClientTransactionHandler { BroadcastReceiver receiver; ContextImpl context; try { - app = packageInfo.makeApplication(false, mInstrumentation); + app = loadedApk.makeApplication(false, mInstrumentation); context = (ContextImpl) app.getBaseContext(); if (data.info.splitName != null) { context = (ContextImpl) context.createContextForSplit(data.info.splitName); @@ -3159,7 +3159,7 @@ public final class ActivityThread extends ClientTransactionHandler { data.intent.setExtrasClassLoader(cl); data.intent.prepareToEnterProcess(); data.setExtrasClassLoader(cl); - receiver = packageInfo.getAppFactory() + receiver = loadedApk.getAppFactory() .instantiateReceiver(cl, data.info.name, data.intent); } catch (Exception e) { if (DEBUG_BROADCAST) Slog.i(TAG, @@ -3175,9 +3175,9 @@ public final class ActivityThread extends ClientTransactionHandler { TAG, "Performing receive of " + data.intent + ": app=" + app + ", appName=" + app.getPackageName() - + ", pkg=" + packageInfo.getPackageName() + + ", pkg=" + loadedApk.getPackageName() + ", comp=" + data.intent.getComponent().toShortString() - + ", dir=" + packageInfo.getAppDir()); + + ", dir=" + loadedApk.getAppDir()); sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); @@ -3222,8 +3222,8 @@ public final class ActivityThread extends ClientTransactionHandler { unscheduleGcIdler(); // instantiate the BackupAgent class named in the manifest - LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo); - String packageName = packageInfo.mPackageName; + LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo); + String packageName = loadedApk.mPackageName; if (packageName == null) { Slog.d(TAG, "Asked to create backup agent for nonexistent package"); return; @@ -3249,11 +3249,11 @@ public final class ActivityThread extends ClientTransactionHandler { try { if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname); - java.lang.ClassLoader cl = packageInfo.getClassLoader(); + java.lang.ClassLoader cl = loadedApk.getClassLoader(); agent = (BackupAgent) cl.loadClass(classname).newInstance(); // set up the agent's context - ContextImpl context = ContextImpl.createAppContext(this, packageInfo); + ContextImpl context = ContextImpl.createAppContext(this, loadedApk); context.setOuterContext(agent); agent.attach(context); @@ -3289,8 +3289,8 @@ public final class ActivityThread extends ClientTransactionHandler { private void handleDestroyBackupAgent(CreateBackupAgentData data) { if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data); - LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo); - String packageName = packageInfo.mPackageName; + LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo); + String packageName = loadedApk.mPackageName; BackupAgent agent = mBackupAgents.get(packageName); if (agent != null) { try { @@ -3310,12 +3310,12 @@ public final class ActivityThread extends ClientTransactionHandler { // we are back active so skip it. unscheduleGcIdler(); - LoadedApk packageInfo = getPackageInfoNoCheck( + LoadedApk loadedApk = getLoadedApkNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { - java.lang.ClassLoader cl = packageInfo.getClassLoader(); - service = packageInfo.getAppFactory() + java.lang.ClassLoader cl = loadedApk.getClassLoader(); + service = loadedApk.getAppFactory() .instantiateService(cl, data.info.name, data.intent); } catch (Exception e) { if (!mInstrumentation.onException(service, e)) { @@ -3328,10 +3328,10 @@ public final class ActivityThread extends ClientTransactionHandler { try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); - ContextImpl context = ContextImpl.createAppContext(this, packageInfo); + ContextImpl context = ContextImpl.createAppContext(this, loadedApk); context.setOuterContext(service); - Application app = packageInfo.makeApplication(false, mInstrumentation); + Application app = loadedApk.makeApplication(false, mInstrumentation); service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService()); service.onCreate(); @@ -4195,11 +4195,11 @@ public final class ActivityThread extends ClientTransactionHandler { } private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) { - LoadedApk apk = peekPackageInfo(data.pkg, false); + LoadedApk apk = peekLoadedApk(data.pkg, false); if (apk != null) { apk.setCompatibilityInfo(data.info); } - apk = peekPackageInfo(data.pkg, true); + apk = peekLoadedApk(data.pkg, true); if (apk != null) { apk.setCompatibilityInfo(data.info); } @@ -4693,7 +4693,7 @@ public final class ActivityThread extends ClientTransactionHandler { if (a != null) { Configuration thisConfig = applyConfigCompatMainThread( mCurDefaultDisplayDpi, newConfig, - ar.packageInfo.getCompatibilityInfo()); + ar.loadedApk.getCompatibilityInfo()); if (!ar.activity.mFinished && (allActivities || !ar.paused)) { // If the activity is currently resumed, its configuration // needs to change right now. @@ -5179,7 +5179,7 @@ public final class ActivityThread extends ClientTransactionHandler { } final void handleDispatchPackageBroadcast(int cmd, String[] packages) { - boolean hasPkgInfo = false; + boolean hasLoadedApk = false; switch (cmd) { case ApplicationThreadConstants.PACKAGE_REMOVED: case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL: @@ -5190,14 +5190,14 @@ public final class ActivityThread extends ClientTransactionHandler { } synchronized (mResourcesManager) { for (int i = packages.length - 1; i >= 0; i--) { - if (!hasPkgInfo) { + if (!hasLoadedApk) { WeakReference<LoadedApk> ref = mPackages.get(packages[i]); if (ref != null && ref.get() != null) { - hasPkgInfo = true; + hasLoadedApk = true; } else { ref = mResourcePackages.get(packages[i]); if (ref != null && ref.get() != null) { - hasPkgInfo = true; + hasLoadedApk = true; } } } @@ -5217,21 +5217,21 @@ public final class ActivityThread extends ClientTransactionHandler { synchronized (mResourcesManager) { for (int i = packages.length - 1; i >= 0; i--) { WeakReference<LoadedApk> ref = mPackages.get(packages[i]); - LoadedApk pkgInfo = ref != null ? ref.get() : null; - if (pkgInfo != null) { - hasPkgInfo = true; + LoadedApk loadedApk = ref != null ? ref.get() : null; + if (loadedApk != null) { + hasLoadedApk = true; } else { ref = mResourcePackages.get(packages[i]); - pkgInfo = ref != null ? ref.get() : null; - if (pkgInfo != null) { - hasPkgInfo = true; + loadedApk = ref != null ? ref.get() : null; + if (loadedApk != null) { + hasLoadedApk = true; } } // If the package is being replaced, yet it still has a valid // LoadedApk object, the package was updated with _DONT_KILL. // Adjust it's internal references to the application info and // resources. - if (pkgInfo != null) { + if (loadedApk != null) { try { final String packageName = packages[i]; final ApplicationInfo aInfo = @@ -5245,13 +5245,13 @@ public final class ActivityThread extends ClientTransactionHandler { if (ar.activityInfo.applicationInfo.packageName .equals(packageName)) { ar.activityInfo.applicationInfo = aInfo; - ar.packageInfo = pkgInfo; + ar.loadedApk = loadedApk; } } } final List<String> oldPaths = sPackageManager.getPreviousCodePaths(packageName); - pkgInfo.updateApplicationInfo(aInfo, oldPaths); + loadedApk.updateApplicationInfo(aInfo, oldPaths); } catch (RemoteException e) { } } @@ -5260,7 +5260,7 @@ public final class ActivityThread extends ClientTransactionHandler { break; } } - ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo); + ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk); } final void handleLowMemory() { @@ -5464,7 +5464,7 @@ public final class ActivityThread extends ClientTransactionHandler { applyCompatConfiguration(mCurDefaultDisplayDpi); } - data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); + data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo); /** * Switch this process to density compatibility mode if needed. @@ -5508,7 +5508,7 @@ public final class ActivityThread extends ClientTransactionHandler { // XXX should have option to change the port. Debug.changeDebugPort(8100); if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) { - Slog.w(TAG, "Application " + data.info.getPackageName() + Slog.w(TAG, "Application " + data.loadedApk.getPackageName() + " is waiting for the debugger on port 8100..."); IActivityManager mgr = ActivityManager.getService(); @@ -5527,7 +5527,7 @@ public final class ActivityThread extends ClientTransactionHandler { } } else { - Slog.w(TAG, "Application " + data.info.getPackageName() + Slog.w(TAG, "Application " + data.loadedApk.getPackageName() + " can be debugged on port 8100..."); } } @@ -5575,14 +5575,14 @@ public final class ActivityThread extends ClientTransactionHandler { mInstrumentationAppDir = ii.sourceDir; mInstrumentationSplitAppDirs = ii.splitSourceDirs; mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii); - mInstrumentedAppDir = data.info.getAppDir(); - mInstrumentedSplitAppDirs = data.info.getSplitAppDirs(); - mInstrumentedLibDir = data.info.getLibDir(); + mInstrumentedAppDir = data.loadedApk.getAppDir(); + mInstrumentedSplitAppDirs = data.loadedApk.getSplitAppDirs(); + mInstrumentedLibDir = data.loadedApk.getLibDir(); } else { ii = null; } - final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); + final ContextImpl appContext = ContextImpl.createAppContext(this, data.loadedApk); updateLocaleListFromAppContext(appContext, mResourcesManager.getConfiguration().getLocales()); @@ -5626,9 +5626,9 @@ public final class ActivityThread extends ClientTransactionHandler { } ii.copyTo(instrApp); instrApp.initForUser(UserHandle.myUserId()); - final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, + final LoadedApk loadedApk = getLoadedApk(instrApp, data.compatInfo, appContext.getClassLoader(), false, true, false); - final ContextImpl instrContext = ContextImpl.createAppContext(this, pi); + final ContextImpl instrContext = ContextImpl.createAppContext(this, loadedApk); try { final ClassLoader cl = instrContext.getClassLoader(); @@ -5673,7 +5673,7 @@ public final class ActivityThread extends ClientTransactionHandler { try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. - app = data.info.makeApplication(data.restrictedBackupMode, null); + app = data.loadedApk.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; // don't bring up providers in restricted mode; they may depend on the @@ -5727,7 +5727,7 @@ public final class ActivityThread extends ClientTransactionHandler { final int preloadedFontsResource = info.metaData.getInt( ApplicationInfo.METADATA_PRELOADED_FONTS, 0); if (preloadedFontsResource != 0) { - data.info.getResources().preloadFonts(preloadedFontsResource); + data.loadedApk.getResources().preloadFonts(preloadedFontsResource); } } } catch (RemoteException e) { @@ -6185,12 +6185,12 @@ public final class ActivityThread extends ClientTransactionHandler { try { final java.lang.ClassLoader cl = c.getClassLoader(); - LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); - if (packageInfo == null) { + LoadedApk loadedApk = peekLoadedApk(ai.packageName, true); + if (loadedApk == null) { // System startup case. - packageInfo = getSystemContext().mPackageInfo; + loadedApk = getSystemContext().mLoadedApk; } - localProvider = packageInfo.getAppFactory() + localProvider = loadedApk.getAppFactory() .instantiateProvider(cl, info.name); provider = localProvider.getIContentProvider(); if (provider == null) { @@ -6339,8 +6339,8 @@ public final class ActivityThread extends ClientTransactionHandler { mInstrumentation = new Instrumentation(); mInstrumentation.basicInit(this); ContextImpl context = ContextImpl.createAppContext( - this, getSystemContext().mPackageInfo); - mInitialApplication = context.mPackageInfo.makeApplication(true, null); + this, getSystemContext().mLoadedApk); + mInitialApplication = context.mLoadedApk.makeApplication(true, null); mInitialApplication.onCreate(); } catch (Exception e) { throw new RuntimeException( diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index c9435746f287..50e3f0a9a133 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -160,7 +160,7 @@ public class AppOpsManager { public static final int OP_WRITE_ICC_SMS = 22; /** @hide */ public static final int OP_WRITE_SETTINGS = 23; - /** @hide */ + /** @hide Required to draw on top of other apps. */ public static final int OP_SYSTEM_ALERT_WINDOW = 24; /** @hide */ public static final int OP_ACCESS_NOTIFICATIONS = 25; @@ -260,8 +260,10 @@ public class AppOpsManager { public static final int OP_REQUEST_DELETE_PACKAGES = 72; /** @hide Bind an accessibility service. */ public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73; + /** @hide Interact with the system UI via an Accessibility Service */ + public static final int OP_PERFORM_ACCESSIBILITY_ACTION = 74; /** @hide */ - public static final int _NUM_OP = 74; + public static final int _NUM_OP = 75; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -506,6 +508,7 @@ public class AppOpsManager { OP_CHANGE_WIFI_STATE, OP_REQUEST_DELETE_PACKAGES, OP_BIND_ACCESSIBILITY_SERVICE, + OP_PERFORM_ACCESSIBILITY_ACTION, }; /** @@ -587,6 +590,7 @@ public class AppOpsManager { null, // OP_CHANGE_WIFI_STATE null, // OP_REQUEST_DELETE_PACKAGES null, // OP_BIND_ACCESSIBILITY_SERVICE + null, // OP_PERFORM_ACCESSIBILITY_ACTION }; /** @@ -668,6 +672,7 @@ public class AppOpsManager { "CHANGE_WIFI_STATE", "REQUEST_DELETE_PACKAGES", "BIND_ACCESSIBILITY_SERVICE", + "OP_PERFORM_ACCESSIBILITY_ACTION", }; /** @@ -749,6 +754,7 @@ public class AppOpsManager { Manifest.permission.CHANGE_WIFI_STATE, Manifest.permission.REQUEST_DELETE_PACKAGES, Manifest.permission.BIND_ACCESSIBILITY_SERVICE, + null, // no permission for OP_PERFORM_ACCESSIBILITY_ACTION }; /** @@ -831,6 +837,7 @@ public class AppOpsManager { null, // OP_CHANGE_WIFI_STATE null, // REQUEST_DELETE_PACKAGES null, // OP_BIND_ACCESSIBILITY_SERVICE + null, // OP_PERFORM_ACCESSIBILITY_ACTION }; /** @@ -912,6 +919,7 @@ public class AppOpsManager { false, // OP_CHANGE_WIFI_STATE false, // OP_REQUEST_DELETE_PACKAGES false, // OP_BIND_ACCESSIBILITY_SERVICE + false, // OP_PERFORM_ACCESSIBILITY_ACTION }; /** @@ -992,6 +1000,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // OP_CHANGE_WIFI_STATE AppOpsManager.MODE_ALLOWED, // REQUEST_DELETE_PACKAGES AppOpsManager.MODE_ALLOWED, // OP_BIND_ACCESSIBILITY_SERVICE + AppOpsManager.MODE_ALLOWED, // OP_PERFORM_ACCESSIBILITY_ACTION }; /** @@ -1076,6 +1085,7 @@ public class AppOpsManager { false, // OP_CHANGE_WIFI_STATE false, // OP_REQUEST_DELETE_PACKAGES false, // OP_BIND_ACCESSIBILITY_SERVICE + false, // OP_PERFORM_ACCESSIBILITY_ACTION }; /** diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 156df36a600c..5822f5c8f6c1 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -187,7 +187,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { */ /* package */ final void attach(Context context) { attachBaseContext(context); - mLoadedApk = ContextImpl.getImpl(context).mPackageInfo; + mLoadedApk = ContextImpl.getImpl(context).mLoadedApk; } /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 1dbdb59ebcce..8641a21a0b3c 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1381,7 +1381,7 @@ public class ApplicationPackageManager extends PackageManager { sameUid ? app.sourceDir : app.publicSourceDir, sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY, - mContext.mPackageInfo); + mContext.mLoadedApk); if (r != null) { return r; } diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index ef66af0c60f4..45c0e0cdfb25 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -110,7 +110,7 @@ public abstract class ClientTransactionHandler { PendingTransactionActions pendingActions); /** Get package info. */ - public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, + public abstract LoadedApk getLoadedApkNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo); /** Deliver app configuration change notification. */ diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index a2de0f44962a..165343058a2a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -159,7 +159,7 @@ class ContextImpl extends Context { private ArrayMap<String, File> mSharedPrefsPaths; final @NonNull ActivityThread mMainThread; - final @NonNull LoadedApk mPackageInfo; + final @NonNull LoadedApk mLoadedApk; private @Nullable ClassLoader mClassLoader; private final @Nullable IBinder mActivityToken; @@ -257,8 +257,8 @@ class ContextImpl extends Context { @Override public Context getApplicationContext() { - return (mPackageInfo != null) ? - mPackageInfo.getApplication() : mMainThread.getApplication(); + return (mLoadedApk != null) ? + mLoadedApk.getApplication() : mMainThread.getApplication(); } @Override @@ -302,15 +302,15 @@ class ContextImpl extends Context { @Override public ClassLoader getClassLoader() { - return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader()); + return mClassLoader != null ? mClassLoader : (mLoadedApk != null ? mLoadedApk.getClassLoader() : ClassLoader.getSystemClassLoader()); } @Override public String getPackageName() { - if (mPackageInfo != null) { - return mPackageInfo.getPackageName(); + if (mLoadedApk != null) { + return mLoadedApk.getPackageName(); } - // No mPackageInfo means this is a Context for the system itself, + // No mLoadedApk means this is a Context for the system itself, // and this here is its name. return "android"; } @@ -329,24 +329,24 @@ class ContextImpl extends Context { @Override public ApplicationInfo getApplicationInfo() { - if (mPackageInfo != null) { - return mPackageInfo.getApplicationInfo(); + if (mLoadedApk != null) { + return mLoadedApk.getApplicationInfo(); } throw new RuntimeException("Not supported in system context"); } @Override public String getPackageResourcePath() { - if (mPackageInfo != null) { - return mPackageInfo.getResDir(); + if (mLoadedApk != null) { + return mLoadedApk.getResDir(); } throw new RuntimeException("Not supported in system context"); } @Override public String getPackageCodePath() { - if (mPackageInfo != null) { - return mPackageInfo.getAppDir(); + if (mLoadedApk != null) { + return mLoadedApk.getAppDir(); } throw new RuntimeException("Not supported in system context"); } @@ -356,7 +356,7 @@ class ContextImpl extends Context { // At least one application in the world actually passes in a null // name. This happened to work because when we generated the file name // we would stringify it to "null.xml". Nice. - if (mPackageInfo.getApplicationInfo().targetSdkVersion < + if (mLoadedApk.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) { if (name == null) { name = "null"; @@ -1098,11 +1098,11 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); IIntentReceiver rd = null; if (resultReceiver != null) { - if (mPackageInfo != null) { + if (mLoadedApk != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( resultReceiver, getOuterContext(), scheduler, mMainThread.getInstrumentation(), false); } else { @@ -1202,11 +1202,11 @@ class ContextImpl extends Context { Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { IIntentReceiver rd = null; if (resultReceiver != null) { - if (mPackageInfo != null) { + if (mLoadedApk != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( resultReceiver, getOuterContext(), scheduler, mMainThread.getInstrumentation(), false); } else { @@ -1256,11 +1256,11 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); IIntentReceiver rd = null; if (resultReceiver != null) { - if (mPackageInfo != null) { + if (mLoadedApk != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( resultReceiver, getOuterContext(), scheduler, mMainThread.getInstrumentation(), false); } else { @@ -1338,11 +1338,11 @@ class ContextImpl extends Context { Bundle initialExtras) { IIntentReceiver rd = null; if (resultReceiver != null) { - if (mPackageInfo != null) { + if (mLoadedApk != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( resultReceiver, getOuterContext(), scheduler, mMainThread.getInstrumentation(), false); } else { @@ -1419,11 +1419,11 @@ class ContextImpl extends Context { Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; if (receiver != null) { - if (mPackageInfo != null && context != null) { + if (mLoadedApk != null && context != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } else { @@ -1450,8 +1450,8 @@ class ContextImpl extends Context { @Override public void unregisterReceiver(BroadcastReceiver receiver) { - if (mPackageInfo != null) { - IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher( + if (mLoadedApk != null) { + IIntentReceiver rd = mLoadedApk.forgetReceiverDispatcher( getOuterContext(), receiver); try { ActivityManager.getService().unregisterReceiver(rd); @@ -1584,7 +1584,7 @@ class ContextImpl extends Context { @Override public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler, int flags) { - return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); + return mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags); } /** @hide */ @@ -1606,16 +1606,16 @@ class ContextImpl extends Context { if (conn == null) { throw new IllegalArgumentException("connection is null"); } - if (mPackageInfo != null) { - sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); + if (mLoadedApk != null) { + sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags); } else { throw new RuntimeException("Not supported in system context"); } validateServiceIntent(service); try { IBinder token = getActivityToken(); - if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null - && mPackageInfo.getApplicationInfo().targetSdkVersion + if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null + && mLoadedApk.getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } @@ -1639,8 +1639,8 @@ class ContextImpl extends Context { if (conn == null) { throw new IllegalArgumentException("connection is null"); } - if (mPackageInfo != null) { - IServiceConnection sd = mPackageInfo.forgetServiceDispatcher( + if (mLoadedApk != null) { + IServiceConnection sd = mLoadedApk.forgetServiceDispatcher( getOuterContext(), conn); try { ActivityManager.getService().unbindService(sd); @@ -1985,40 +1985,20 @@ class ContextImpl extends Context { } } - private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName, - int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) { - final String[] splitResDirs; - final ClassLoader classLoader; - try { - splitResDirs = pi.getSplitPaths(splitName); - classLoader = pi.getSplitClassLoader(splitName); - } catch (NameNotFoundException e) { - throw new RuntimeException(e); - } - return ResourcesManager.getInstance().getResources(activityToken, - pi.getResDir(), - splitResDirs, - pi.getOverlayDirs(), - pi.getApplicationInfo().sharedLibraryFiles, - displayId, - overrideConfig, - compatInfo, - classLoader); - } - @Override public Context createApplicationContext(ApplicationInfo application, int flags) throws NameNotFoundException { - LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(), + LoadedApk loadedApk = mMainThread.getLoadedApk(application, + mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE); - if (pi != null) { - ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, + if (loadedApk != null) { + ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, new UserHandle(UserHandle.getUserId(application.uid)), flags, null); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - c.setResources(createResources(mActivityToken, pi, null, displayId, null, + c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null, getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; @@ -2042,20 +2022,21 @@ class ContextImpl extends Context { if (packageName.equals("system") || packageName.equals("android")) { // The system resources are loaded in every application, so we can safely copy // the context without reloading Resources. - return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user, + return new ContextImpl(this, mMainThread, mLoadedApk, null, mActivityToken, user, flags, null); } - LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), + LoadedApk loadedApk = mMainThread.getLoadedApkForPackageName(packageName, + mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); - if (pi != null) { - ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user, + if (loadedApk != null) { + ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, user, flags, null); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - c.setResources(createResources(mActivityToken, pi, null, displayId, null, + c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null, getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; @@ -2069,30 +2050,21 @@ class ContextImpl extends Context { @Override public Context createContextForSplit(String splitName) throws NameNotFoundException { - if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) { + if (!mLoadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) { // All Splits are always loaded. return this; } - final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName); - final String[] paths = mPackageInfo.getSplitPaths(splitName); + final ClassLoader classLoader = mLoadedApk.getSplitClassLoader(splitName); - final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName, + final ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, splitName, mActivityToken, mUser, mFlags, classLoader); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - context.setResources(ResourcesManager.getInstance().getResources( - mActivityToken, - mPackageInfo.getResDir(), - paths, - mPackageInfo.getOverlayDirs(), - mPackageInfo.getApplicationInfo().sharedLibraryFiles, - displayId, - null, - mPackageInfo.getCompatibilityInfo(), - classLoader)); + context.setResources(mLoadedApk.getOrCreateResourcesForSplit(splitName, + mActivityToken, displayId)); return context; } @@ -2102,11 +2074,11 @@ class ContextImpl extends Context { throw new IllegalArgumentException("overrideConfiguration must not be null"); } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, + ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser, mFlags, mClassLoader); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, + context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId, overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo())); return context; } @@ -2117,11 +2089,11 @@ class ContextImpl extends Context { throw new IllegalArgumentException("display must not be null"); } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, + ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser, mFlags, mClassLoader); final int displayId = display.getDisplayId(); - context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, + context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId, null, getDisplayAdjustments(displayId).getCompatibilityInfo())); context.mDisplay = display; return context; @@ -2131,7 +2103,7 @@ class ContextImpl extends Context { public Context createDeviceProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE) | Context.CONTEXT_DEVICE_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser, + return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser, flags, mClassLoader); } @@ -2139,7 +2111,7 @@ class ContextImpl extends Context { public Context createCredentialProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE) | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser, + return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser, flags, mClassLoader); } @@ -2188,14 +2160,14 @@ class ContextImpl extends Context { @Override public File getDataDir() { - if (mPackageInfo != null) { + if (mLoadedApk != null) { File res = null; if (isCredentialProtectedStorage()) { - res = mPackageInfo.getCredentialProtectedDataDirFile(); + res = mLoadedApk.getCredentialProtectedDataDirFile(); } else if (isDeviceProtectedStorage()) { - res = mPackageInfo.getDeviceProtectedDataDirFile(); + res = mLoadedApk.getDeviceProtectedDataDirFile(); } else { - res = mPackageInfo.getDataDirFile(); + res = mLoadedApk.getDataDirFile(); } if (res != null) { @@ -2246,10 +2218,10 @@ class ContextImpl extends Context { } static ContextImpl createSystemContext(ActivityThread mainThread) { - LoadedApk packageInfo = new LoadedApk(mainThread); - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, + LoadedApk loadedApk = new LoadedApk(mainThread); + ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0, null); - context.setResources(packageInfo.getResources()); + context.setResources(loadedApk.getResources()); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetrics()); return context; @@ -2260,35 +2232,35 @@ class ContextImpl extends Context { * Make sure that the created system UI context shares the same LoadedApk as the system context. */ static ContextImpl createSystemUiContext(ContextImpl systemContext) { - final LoadedApk packageInfo = systemContext.mPackageInfo; - ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null, + final LoadedApk loadedApk = systemContext.mLoadedApk; + ContextImpl context = new ContextImpl(null, systemContext.mMainThread, loadedApk, null, null, null, 0, null); - context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null, - packageInfo.getCompatibilityInfo())); + context.setResources(loadedApk.createResources(null, null, Display.DEFAULT_DISPLAY, null, + loadedApk.getCompatibilityInfo())); return context; } - static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { - if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, + static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) { + if (loadedApk == null) throw new IllegalArgumentException("loadedApk"); + ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0, null); - context.setResources(packageInfo.getResources()); + context.setResources(loadedApk.getResources()); return context; } static ContextImpl createActivityContext(ActivityThread mainThread, - LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId, + LoadedApk loadedApk, ActivityInfo activityInfo, IBinder activityToken, int displayId, Configuration overrideConfiguration) { - if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); + if (loadedApk == null) throw new IllegalArgumentException("loadedApk"); - String[] splitDirs = packageInfo.getSplitResDirs(); - ClassLoader classLoader = packageInfo.getClassLoader(); + String[] splitDirs = loadedApk.getSplitResDirs(); + ClassLoader classLoader = loadedApk.getClassLoader(); - if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) { + if (loadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies"); try { - classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName); - splitDirs = packageInfo.getSplitPaths(activityInfo.splitName); + classLoader = loadedApk.getSplitClassLoader(activityInfo.splitName); + splitDirs = loadedApk.getSplitPaths(activityInfo.splitName); } catch (NameNotFoundException e) { // Nothing above us can handle a NameNotFoundException, better crash. throw new RuntimeException(e); @@ -2297,14 +2269,14 @@ class ContextImpl extends Context { } } - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName, + ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName, activityToken, null, 0, classLoader); // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY) - ? packageInfo.getCompatibilityInfo() + ? loadedApk.getCompatibilityInfo() : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; final ResourcesManager resourcesManager = ResourcesManager.getInstance(); @@ -2312,10 +2284,10 @@ class ContextImpl extends Context { // Create the base resources for which all configuration contexts for this Activity // will be rebased upon. context.setResources(resourcesManager.createBaseActivityResources(activityToken, - packageInfo.getResDir(), + loadedApk.getResDir(), splitDirs, - packageInfo.getOverlayDirs(), - packageInfo.getApplicationInfo().sharedLibraryFiles, + loadedApk.getOverlayDirs(), + loadedApk.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, compatInfo, @@ -2326,7 +2298,7 @@ class ContextImpl extends Context { } private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread, - @NonNull LoadedApk packageInfo, @Nullable String splitName, + @NonNull LoadedApk loadedApk, @Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user, int flags, @Nullable ClassLoader classLoader) { mOuterContext = this; @@ -2335,10 +2307,10 @@ class ContextImpl extends Context { // location for application. if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE | Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) { - final File dataDir = packageInfo.getDataDirFile(); - if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) { + final File dataDir = loadedApk.getDataDirFile(); + if (Objects.equals(dataDir, loadedApk.getCredentialProtectedDataDirFile())) { flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE; - } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) { + } else if (Objects.equals(dataDir, loadedApk.getDeviceProtectedDataDirFile())) { flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE; } } @@ -2352,7 +2324,7 @@ class ContextImpl extends Context { } mUser = user; - mPackageInfo = packageInfo; + mLoadedApk = loadedApk; mSplitName = splitName; mClassLoader = classLoader; mResourcesManager = ResourcesManager.getInstance(); @@ -2363,8 +2335,8 @@ class ContextImpl extends Context { setResources(container.mResources); mDisplay = container.mDisplay; } else { - mBasePackageName = packageInfo.mPackageName; - ApplicationInfo ainfo = packageInfo.getApplicationInfo(); + mBasePackageName = loadedApk.mPackageName; + ApplicationInfo ainfo = loadedApk.getApplicationInfo(); if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) { // Special case: system components allow themselves to be loaded in to other // processes. For purposes of app ops, we must then consider the context as @@ -2387,7 +2359,7 @@ class ContextImpl extends Context { } void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { - mPackageInfo.installSystemApplicationInfo(info, classLoader); + mLoadedApk.installSystemApplicationInfo(info, classLoader); } final void scheduleFinalCleanup(String who, String what) { @@ -2396,7 +2368,7 @@ class ContextImpl extends Context { final void performFinalCleanup(String who, String what) { //Log.i(TAG, "Cleanup up context: " + this); - mPackageInfo.removeContextRegistrations(getOuterContext(), who, what); + mLoadedApk.removeContextRegistrations(getOuterContext(), who, what); } final Context getReceiverRestrictedContext() { diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 490b2bf39f53..41324d0813b8 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1209,10 +1209,10 @@ public class Instrumentation { } private AppComponentFactory getFactory(String pkg) { - LoadedApk apk = mThread.peekPackageInfo(pkg, true); + LoadedApk loadedApk = mThread.peekLoadedApk(pkg, true); // This is in the case of starting up "android". - if (apk == null) apk = mThread.getSystemContext().mPackageInfo; - return apk.getAppFactory(); + if (loadedApk == null) loadedApk = mThread.getSystemContext().mLoadedApk; + return loadedApk.getAppFactory(); } private void prePerformCreate(Activity activity) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index ab00a7ddde55..26f498087786 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -31,6 +31,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.split.SplitDependencyLoader; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; @@ -48,15 +49,13 @@ import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.Log; +import android.util.LogPrinter; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayAdjustments; - import com.android.internal.util.ArrayUtils; - import dalvik.system.VMRuntime; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -968,14 +967,78 @@ public final class LoadedApk { throw new AssertionError("null split not found"); } - mResources = ResourcesManager.getInstance().getResources(null, mResDir, - splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, - Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(), + mResources = ResourcesManager.getInstance().getResources( + null, + mResDir, + splitPaths, + mOverlayDirs, + mApplicationInfo.sharedLibraryFiles, + Display.DEFAULT_DISPLAY, + null, + getCompatibilityInfo(), getClassLoader()); } return mResources; } + public Resources getOrCreateResourcesForSplit(@NonNull String splitName, + @Nullable IBinder activityToken, int displayId) throws NameNotFoundException { + return ResourcesManager.getInstance().getResources( + activityToken, + mResDir, + getSplitPaths(splitName), + mOverlayDirs, + mApplicationInfo.sharedLibraryFiles, + displayId, + null, + getCompatibilityInfo(), + getSplitClassLoader(splitName)); + } + + /** + * Creates the top level resources for the given package. Will return an existing + * Resources if one has already been created. + */ + public Resources getOrCreateTopLevelResources(@NonNull ApplicationInfo appInfo) { + // Request for this app, short circuit + if (appInfo.uid == Process.myUid()) { + return getResources(); + } + + // Get resources for a different package + return ResourcesManager.getInstance().getResources( + null, + appInfo.publicSourceDir, + appInfo.splitPublicSourceDirs, + appInfo.resourceDirs, + appInfo.sharedLibraryFiles, + Display.DEFAULT_DISPLAY, + null, + getCompatibilityInfo(), + getClassLoader()); + } + + public Resources createResources(IBinder activityToken, String splitName, + int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) { + final String[] splitResDirs; + final ClassLoader classLoader; + try { + splitResDirs = getSplitPaths(splitName); + classLoader = getSplitClassLoader(splitName); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + return ResourcesManager.getInstance().getResources(activityToken, + mResDir, + splitResDirs, + mOverlayDirs, + mApplicationInfo.sharedLibraryFiles, + displayId, + overrideConfig, + compatInfo, + classLoader); + } + public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { if (mApplication != null) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 3c4036bc39aa..89df421efcc1 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -64,6 +64,9 @@ import android.security.AttestedKeyPair; import android.security.Credentials; import android.security.KeyChain; import android.security.KeyChainException; +import android.security.keymaster.KeymasterCertificateChain; +import android.security.keystore.AttestationUtils; +import android.security.keystore.KeyAttestationException; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.ParcelableKeyGenParameterSpec; import android.service.restrictions.RestrictionsReceiver; @@ -2662,6 +2665,28 @@ public class DevicePolicyManager { } /** + * When called by a profile owner of a managed profile returns true if the profile uses unified + * challenge with its parent user. + * + * <strong>Note: This method is not concerned with password quality and will return false if + * the profile has empty password as a separate challenge. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @throws SecurityException if {@code admin} is not a profile owner of a managed profile. + * @see UserManager#DISALLOW_UNIFIED_PASSWORD + */ + public boolean isUsingUnifiedPassword(@NonNull ComponentName admin) { + if (mService != null) { + try { + return mService.isUsingUnifiedPassword(admin); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return true; + } + + /** * Determine whether the current profile password the user has set is sufficient * to meet the policy requirements (e.g. quality, minimum length) that have been * requested by the admins of the parent user and its profiles. @@ -4005,15 +4030,27 @@ public class DevicePolicyManager { try { final ParcelableKeyGenParameterSpec parcelableSpec = new ParcelableKeyGenParameterSpec(keySpec); + KeymasterCertificateChain attestationChain = new KeymasterCertificateChain(); final boolean success = mService.generateKeyPair( - admin, mContext.getPackageName(), algorithm, parcelableSpec); + admin, mContext.getPackageName(), algorithm, parcelableSpec, attestationChain); if (!success) { Log.e(TAG, "Error generating key via DevicePolicyManagerService."); return null; } - final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias()); - return new AttestedKeyPair(keyPair, null); + final String alias = keySpec.getKeystoreAlias(); + final KeyPair keyPair = KeyChain.getKeyPair(mContext, alias); + Certificate[] outputChain = null; + try { + if (AttestationUtils.isChainValid(attestationChain)) { + outputChain = AttestationUtils.parseCertificateChain(attestationChain); + } + } catch (KeyAttestationException e) { + Log.e(TAG, "Error parsing attestation chain for alias " + alias, e); + mService.removeKeyPair(admin, mContext.getPackageName(), alias); + return null; + } + return new AttestedKeyPair(keyPair, outputChain); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (KeyChainException e) { diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 014d7b9c5ad7..912820818a78 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -36,6 +36,7 @@ import android.os.Bundle; import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.UserHandle; +import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.ParcelableKeyGenParameterSpec; import java.util.List; @@ -79,6 +80,7 @@ interface IDevicePolicyManager { boolean isActivePasswordSufficient(int userHandle, boolean parent); boolean isProfileActivePasswordSufficientForParent(int userHandle); + boolean isUsingUnifiedPassword(in ComponentName admin); int getCurrentFailedPasswordAttempts(int userHandle, boolean parent); int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent); @@ -166,7 +168,9 @@ interface IDevicePolicyManager { in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess, boolean isUserSelectable); boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias); - boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec); + boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, + in ParcelableKeyGenParameterSpec keySpec, + out KeymasterCertificateChain attestationChain); void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback); void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes); diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java index 073d28cfa27f..8304c1c5a2d8 100644 --- a/core/java/android/app/servertransaction/PendingTransactionActions.java +++ b/core/java/android/app/servertransaction/PendingTransactionActions.java @@ -134,7 +134,7 @@ public class PendingTransactionActions { Bundle.dumpStats(pw, mPersistentState); if (ex instanceof TransactionTooLargeException - && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) { + && mActivity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) { Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex); return; } diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index b13067ee97e3..e61e8b290519 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -125,6 +125,11 @@ public final class Slice implements Parcelable { */ public static final String HINT_HIDDEN = "hidden"; /** + * Hint indicating this content should be shown instead of the normal content when the slice + * is in small format. + */ + public static final String HINT_SUMMARY = "summary"; + /** * Hint to indicate that this content has a toggle action associated with it. To indicate that * the toggle is on, use {@link #HINT_SELECTED}. When the toggle state changes, the intent * associated with it will be sent along with an extra {@link #EXTRA_TOGGLE_STATE} which can be diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index f8e19c12b810..0c5f225d515e 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -16,24 +16,37 @@ package android.app.slice; +import android.annotation.NonNull; import android.annotation.SystemService; -import android.app.slice.ISliceListener.Stub; import android.content.Context; import android.net.Uri; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import android.util.ArrayMap; +import android.util.Pair; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executor; /** - * @hide + * Class to handle interactions with {@link Slice}s. + * <p> + * The SliceManager manages permissions and pinned state for slices. */ @SystemService(Context.SLICE_SERVICE) public class SliceManager { private final ISliceManager mService; private final Context mContext; + private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup = + new ArrayMap<>(); + /** + * @hide + */ public SliceManager(Context context, Handler handler) throws ServiceNotFoundException { mContext = context; mService = ISliceManager.Stub.asInterface( @@ -41,38 +54,142 @@ public class SliceManager { } /** + * Adds a callback to a specific slice uri. + * <p> + * This is a convenience that performs a few slice actions at once. It will put + * the slice in a pinned state since there is a callback attached. It will also + * listen for content changes, when a content change observes, the android system + * will bind the new slice and provide it to all registered {@link SliceCallback}s. + * + * @param uri The uri of the slice being listened to. + * @param callback The listener that should receive the callbacks. + * @param specs The list of supported {@link SliceSpec}s of the callback. + * @see SliceProvider#onSlicePinned(Uri) + */ + public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback, + @NonNull List<SliceSpec> specs) { + registerSliceCallback(uri, callback, specs, Handler.getMain()); + } + + /** + * Adds a callback to a specific slice uri. + * <p> + * This is a convenience that performs a few slice actions at once. It will put + * the slice in a pinned state since there is a callback attached. It will also + * listen for content changes, when a content change observes, the android system + * will bind the new slice and provide it to all registered {@link SliceCallback}s. + * + * @param uri The uri of the slice being listened to. + * @param callback The listener that should receive the callbacks. + * @param specs The list of supported {@link SliceSpec}s of the callback. + * @see SliceProvider#onSlicePinned(Uri) */ - public void addSliceListener(Uri uri, SliceListener listener, SliceSpec[] specs) { + public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback, + @NonNull List<SliceSpec> specs, Handler handler) { try { - mService.addSliceListener(uri, mContext.getPackageName(), listener.mStub, specs); + mService.addSliceListener(uri, mContext.getPackageName(), + getListener(uri, callback, new ISliceListener.Stub() { + @Override + public void onSliceUpdated(Slice s) throws RemoteException { + handler.post(() -> callback.onSliceUpdated(s)); + } + }), specs.toArray(new SliceSpec[specs.size()])); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Adds a callback to a specific slice uri. + * <p> + * This is a convenience that performs a few slice actions at once. It will put + * the slice in a pinned state since there is a callback attached. It will also + * listen for content changes, when a content change observes, the android system + * will bind the new slice and provide it to all registered {@link SliceCallback}s. + * + * @param uri The uri of the slice being listened to. + * @param callback The listener that should receive the callbacks. + * @param specs The list of supported {@link SliceSpec}s of the callback. + * @see SliceProvider#onSlicePinned(Uri) */ - public void removeSliceListener(Uri uri, SliceListener listener) { + public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback, + @NonNull List<SliceSpec> specs, Executor executor) { try { - mService.removeSliceListener(uri, mContext.getPackageName(), listener.mStub); + mService.addSliceListener(uri, mContext.getPackageName(), + getListener(uri, callback, new ISliceListener.Stub() { + @Override + public void onSliceUpdated(Slice s) throws RemoteException { + executor.execute(() -> callback.onSliceUpdated(s)); + } + }), specs.toArray(new SliceSpec[specs.size()])); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + private ISliceListener getListener(Uri uri, SliceCallback callback, + ISliceListener listener) { + Pair<Uri, SliceCallback> key = new Pair<>(uri, callback); + if (mListenerLookup.containsKey(key)) { + try { + mService.removeSliceListener(uri, mContext.getPackageName(), + mListenerLookup.get(key)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + mListenerLookup.put(key, listener); + return listener; + } + /** + * Removes a callback for a specific slice uri. + * <p> + * Removes the app from the pinned state (if there are no other apps/callbacks pinning it) + * in addition to removing the callback. + * + * @param uri The uri of the slice being listened to + * @param callback The listener that should no longer receive callbacks. + * @see #registerSliceCallback */ - public void pinSlice(Uri uri, SliceSpec[] specs) { + public void unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) { try { - mService.pinSlice(mContext.getPackageName(), uri, specs); + mService.removeSliceListener(uri, mContext.getPackageName(), + mListenerLookup.remove(new Pair<>(uri, callback))); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Ensures that a slice is in a pinned state. + * <p> + * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices + * they still care about after a reboot. + * + * @param uri The uri of the slice being pinned. + * @param specs The list of supported {@link SliceSpec}s of the callback. + * @see SliceProvider#onSlicePinned(Uri) */ - public void unpinSlice(Uri uri) { + public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) { + try { + mService.pinSlice(mContext.getPackageName(), uri, + specs.toArray(new SliceSpec[specs.size()])); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove a pin for a slice. + * <p> + * If the slice has no other pins/callbacks then the slice will be unpinned. + * + * @param uri The uri of the slice being unpinned. + * @see #pinSlice + * @see SliceProvider#onSliceUnpinned(Uri) + */ + public void unpinSlice(@NonNull Uri uri) { try { mService.unpinSlice(mContext.getPackageName(), uri); } catch (RemoteException e) { @@ -81,6 +198,7 @@ public class SliceManager { } /** + * @hide */ public boolean hasSliceAccess() { try { @@ -91,41 +209,31 @@ public class SliceManager { } /** + * Get the current set of specs for a pinned slice. + * <p> + * This is the set of specs supported for a specific pinned slice. It will take + * into account all clients and returns only specs supported by all. + * @see SliceSpec */ - public SliceSpec[] getPinnedSpecs(Uri uri) { + public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) { try { - return mService.getPinnedSpecs(uri, mContext.getPackageName()); + return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Class that listens to changes in {@link Slice}s. */ - public abstract static class SliceListener { - private final Handler mHandler; - - /** - */ - public SliceListener() { - this(Handler.getMain()); - } + public interface SliceCallback { /** + * Called when slice is updated. + * + * @param s The updated slice. + * @see #registerSliceCallback */ - public SliceListener(Handler h) { - mHandler = h; - } - - /** - */ - public abstract void onSliceUpdated(Slice s); - - private final ISliceListener.Stub mStub = new Stub() { - @Override - public void onSliceUpdated(Slice s) throws RemoteException { - mHandler.post(() -> SliceListener.this.onSliceUpdated(s)); - } - }; + void onSliceUpdated(Slice s); } } diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index 7dcd2fead73f..8483931ceaec 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -151,13 +151,33 @@ public abstract class SliceProvider extends ContentProvider { } /** - * @hide + * Called to inform an app that a slice has been pinned. + * <p> + * Pinning is a way that slice hosts use to notify apps of which slices + * they care about updates for. When a slice is pinned the content is + * expected to be relatively fresh and kept up to date. + * <p> + * Being pinned does not provide any escalated privileges for the slice + * provider. So apps should do things such as turn on syncing or schedule + * a job in response to a onSlicePinned. + * <p> + * Pinned state is not persisted through a reboot, and apps can expect a + * new call to onSlicePinned for any slices that should remain pinned + * after a reboot occurs. + * + * @param sliceUri The uri of the slice being unpinned. + * @see #onSliceUnpinned(Uri) */ public void onSlicePinned(Uri sliceUri) { } /** - * @hide + * Called to inform an app that a slices is no longer pinned. + * <p> + * This means that no other apps on the device care about updates to this + * slice anymore and therefore it is not important to be updated. Any syncs + * or jobs related to this slice should be cancelled. + * @see #onSlicePinned(Uri) */ public void onSliceUnpinned(Uri sliceUri) { } diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index 4a0048673c28..f38e462eab4f 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -35,8 +35,6 @@ import java.util.List; * * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. - * - * <p>{@hide} */ public final class BluetoothHidDevice implements BluetoothProfile { diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 4609d52df0c0..c05df2d23e45 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -26,8 +26,6 @@ import android.os.Parcelable; * registration. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @@ -46,10 +44,8 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS - * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see <a - * href="https://www.bluetooth.com/specifications/profiles-overview"> - * https://www.bluetooth.com/specifications/profiles-overview </a> Bluetooth HID Specfication - * v1.1.1 Section 5.2 and Appendix D } + * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. + * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters. * * @param serviceType L2CAP service type * @param tokenRate L2CAP token rate diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 2da64e5a5023..562c559eddc4 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -28,8 +28,6 @@ import java.util.Arrays; * Android device can be discovered as a Bluetooth HID Device. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java index 6ed19654b4c5..bd19955b4e62 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -24,8 +24,6 @@ import android.util.Log; * registration. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public abstract class BluetoothHidDeviceCallback { diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index ebbc710922c2..df2028a55351 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -153,8 +153,6 @@ public interface BluetoothProfile { /** * HID Device - * - * @hide */ public static final int HID_DEVICE = 19; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2bbcfffbe946..e2fd82d1f3e1 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1559,7 +1559,7 @@ public class PackageParser { throws PackageParserException { final String apkPath = apkFile.getAbsolutePath(); - boolean untrusted = (parseFlags & PARSE_IS_SYSTEM_DIR) == 0; + boolean systemDir = (parseFlags & PARSE_IS_SYSTEM_DIR) != 0; int minSignatureScheme = ApkSignatureVerifier.VERSION_JAR_SIGNATURE_SCHEME; if ((parseFlags & PARSE_IS_EPHEMERAL) != 0 || pkg.applicationInfo.isStaticSharedLibrary()) { // must use v2 signing scheme @@ -1567,7 +1567,7 @@ public class PackageParser { } try { ApkSignatureVerifier.Result verified = - ApkSignatureVerifier.verify(apkPath, minSignatureScheme, untrusted); + ApkSignatureVerifier.verify(apkPath, minSignatureScheme, systemDir); if (pkg.mCertificates == null) { pkg.mCertificates = verified.certs; pkg.mSignatures = verified.sigs; diff --git a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java new file mode 100644 index 000000000000..e02e68dced95 --- /dev/null +++ b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 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.database.sqlite; + +import android.app.ActivityThread; +import android.app.Application; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.KeyValueListParser; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Helper class for accessing + * {@link Settings.Global#SQLITE_COMPATIBILITY_WAL_FLAGS global compatibility WAL settings}. + * + * <p>The value of {@link Settings.Global#SQLITE_COMPATIBILITY_WAL_FLAGS} is cached on first access + * for consistent behavior across all connections opened in the process. + * @hide + */ +public class SQLiteCompatibilityWalFlags { + + private static final String TAG = "SQLiteCompatibilityWalFlags"; + + private static volatile boolean sInitialized = true; // Temporarily disable flags + private static volatile boolean sFlagsSet; + private static volatile boolean sCompatibilityWalSupported; + private static volatile String sWALSyncMode; + // This flag is used to avoid recursive initialization due to circular dependency on Settings + private static volatile boolean sCallingGlobalSettings; + + /** + * @hide + */ + @VisibleForTesting + public static boolean areFlagsSet() { + initIfNeeded(); + return sFlagsSet; + } + + /** + * @hide + */ + @VisibleForTesting + public static boolean isCompatibilityWalSupported() { + initIfNeeded(); + return sCompatibilityWalSupported; + } + + /** + * @hide + */ + @VisibleForTesting + public static String getWALSyncMode() { + initIfNeeded(); + return sWALSyncMode; + } + + private static void initIfNeeded() { + if (sInitialized || sCallingGlobalSettings) { + return; + } + ActivityThread activityThread = ActivityThread.currentActivityThread(); + Application app = activityThread == null ? null : activityThread.getApplication(); + String flags = null; + if (app == null) { + Log.w(TAG, "Cannot read global setting " + + Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS + " - " + + "Application state not available"); + } else { + try { + sCallingGlobalSettings = true; + flags = Settings.Global.getString(app.getContentResolver(), + Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS); + } finally { + sCallingGlobalSettings = false; + } + } + + init(flags); + } + + /** + * @hide + */ + @VisibleForTesting + public static void init(String flags) { + if (TextUtils.isEmpty(flags)) { + sInitialized = true; + return; + } + KeyValueListParser parser = new KeyValueListParser(','); + try { + parser.setString(flags); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Setting has invalid format: " + flags, e); + sInitialized = true; + return; + } + sCompatibilityWalSupported = parser.getBoolean("compatibility_wal_supported", + SQLiteGlobal.isCompatibilityWalSupported()); + sWALSyncMode = parser.getString("wal_syncmode", SQLiteGlobal.getWALSyncMode()); + Log.i(TAG, "Read compatibility WAL flags: compatibility_wal_supported=" + + sCompatibilityWalSupported + ", wal_syncmode=" + sWALSyncMode); + sFlagsSet = true; + sInitialized = true; + } + + /** + * @hide + */ + @VisibleForTesting + public static void reset() { + sInitialized = false; + sFlagsSet = false; + sCompatibilityWalSupported = false; + sWALSyncMode = null; + } +} diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 2c93a7fe26cd..7717b8d36123 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -296,7 +296,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen && mConfiguration.syncMode == null && mConfiguration.useCompatibilityWal; if (walEnabled || useCompatibilityWal) { setJournalMode("WAL"); - setSyncMode(SQLiteGlobal.getWALSyncMode()); + if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) { + setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode()); + } else { + setSyncMode(SQLiteGlobal.getWALSyncMode()); + } } else { setJournalMode(mConfiguration.journalMode == null ? SQLiteGlobal.getDefaultJournalMode() : mConfiguration.journalMode); diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index 5adb11964c8c..b211700328b9 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -1094,6 +1094,12 @@ public final class SQLiteConnectionPool implements Closeable { printer.println(" Open: " + mIsOpen); printer.println(" Max connections: " + mMaxConnectionPoolSize); printer.println(" Total execution time: " + mTotalExecutionTimeCounter); + if (SQLiteCompatibilityWalFlags.areFlagsSet()) { + printer.println(" Compatibility WAL settings: compatibility_wal_supported=" + + SQLiteCompatibilityWalFlags + .isCompatibilityWalSupported() + ", wal_syncmode=" + + SQLiteCompatibilityWalFlags.getWALSyncMode()); + } if (mConfiguration.isLookasideConfigSet()) { printer.println(" Lookaside config: sz=" + mConfiguration.lookasideSlotSize + " cnt=" + mConfiguration.lookasideSlotCount); diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 09bb9c69dc09..c1c0812e129e 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -289,6 +289,10 @@ public final class SQLiteDatabase extends SQLiteClosable { mConfigurationLocked.journalMode = journalMode; mConfigurationLocked.syncMode = syncMode; mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported(); + if (!mConfigurationLocked.isInMemoryDb() && SQLiteCompatibilityWalFlags.areFlagsSet()) { + mConfigurationLocked.useCompatibilityWal = SQLiteCompatibilityWalFlags + .isCompatibilityWalSupported(); + } } @Override diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 1d66dc6d939f..5b89f54b5050 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -271,7 +271,7 @@ public final class ContextHubManager { throw new UnsupportedOperationException("TODO: Implement this"); } - /* + /** * Helper function to generate a stub for a non-query transaction callback. * * @param transaction the transaction to unblock when complete @@ -297,7 +297,7 @@ public final class ContextHubManager { }; } - /* + /** * Helper function to generate a stub for a query transaction callback. * * @param transaction the transaction to unblock when complete @@ -392,7 +392,17 @@ public final class ContextHubManager { */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public ContextHubTransaction<Void> enableNanoApp(ContextHubInfo hubInfo, long nanoAppId) { - throw new UnsupportedOperationException("TODO: Implement this"); + ContextHubTransaction<Void> transaction = + new ContextHubTransaction<>(ContextHubTransaction.TYPE_ENABLE_NANOAPP); + IContextHubTransactionCallback callback = createTransactionCallback(transaction); + + try { + mService.enableNanoApp(hubInfo.getId(), callback, nanoAppId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + return transaction; } /** @@ -407,7 +417,17 @@ public final class ContextHubManager { */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public ContextHubTransaction<Void> disableNanoApp(ContextHubInfo hubInfo, long nanoAppId) { - throw new UnsupportedOperationException("TODO: Implement this"); + ContextHubTransaction<Void> transaction = + new ContextHubTransaction<>(ContextHubTransaction.TYPE_DISABLE_NANOAPP); + IContextHubTransactionCallback callback = createTransactionCallback(transaction); + + try { + mService.disableNanoApp(hubInfo.getId(), callback, nanoAppId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + return transaction; } /** diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl index 628ebc7d4579..db5bd36e1109 100644 --- a/core/java/android/hardware/location/IContextHubService.aidl +++ b/core/java/android/hardware/location/IContextHubService.aidl @@ -70,6 +70,16 @@ interface IContextHubService { int contextHubId, in IContextHubTransactionCallback transactionCallback, long nanoAppId); + // Enables a nanoapp at the specified hub + void enableNanoApp( + int contextHubId, in IContextHubTransactionCallback transactionCallback, + long nanoAppId); + + // Disables a nanoapp at the specified hub + void disableNanoApp( + int contextHubId, in IContextHubTransactionCallback transactionCallback, + long nanoAppId); + // Queries for a list of nanoapps void queryNanoApps(int contextHubId, in IContextHubTransactionCallback transactionCallback); } diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 4f4361f6f1eb..4d54e31b8c5d 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -161,7 +161,8 @@ public class RadioManager { private final Set<Integer> mSupportedIdentifierTypes; @NonNull private final Map<String, String> mVendorInfo; - ModuleProperties(int id, String serviceName, int classId, String implementor, + /** @hide */ + public ModuleProperties(int id, String serviceName, int classId, String implementor, String product, String version, String serial, int numTuners, int numAudioSources, boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported, @ProgramSelector.ProgramType int[] supportedProgramTypes, diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl index 0b1ea98f2e12..d9b57db18071 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/core/java/android/net/IIpSecService.aidl @@ -30,7 +30,7 @@ import android.os.ParcelFileDescriptor; */ interface IIpSecService { - IpSecSpiResponse reserveSecurityParameterIndex( + IpSecSpiResponse allocateSecurityParameterIndex( int direction, in String remoteAddress, int requestedSpi, in IBinder binder); void releaseSecurityParameterIndex(int resourceId); diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index a9e60ec88a8e..6a4b8914780c 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -46,7 +46,7 @@ import java.net.Socket; * to create a VPN should use {@link VpnService}. * * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the - * Internet Protocol</a> + * Internet Protocol</a> */ @SystemService(Context.IPSEC_SERVICE) public final class IpSecManager { @@ -59,8 +59,7 @@ public final class IpSecManager { * * @hide */ - @TestApi - public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; + @TestApi public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; /** @hide */ public interface Status { @@ -78,7 +77,7 @@ public final class IpSecManager { * <p>The combination of remote {@code InetAddress} and SPI must be unique across all apps on * one device. If this error is encountered, a new SPI is required before a transform may be * created. This error can be avoided by calling {@link - * IpSecManager#reserveSecurityParameterIndex}. + * IpSecManager#allocateSecurityParameterIndex}. */ public static final class SpiUnavailableException extends AndroidException { private final int mSpi; @@ -121,7 +120,7 @@ public final class IpSecManager { * This class represents a reserved SPI. * * <p>Objects of this type are used to track reserved security parameter indices. They can be - * obtained by calling {@link IpSecManager#reserveSecurityParameterIndex} and must be released + * obtained by calling {@link IpSecManager#allocateSecurityParameterIndex} and must be released * by calling {@link #close()} when they are no longer needed. */ public static final class SecurityParameterIndex implements AutoCloseable { @@ -170,7 +169,7 @@ public final class IpSecManager { mRemoteAddress = remoteAddress; try { IpSecSpiResponse result = - mService.reserveSecurityParameterIndex( + mService.allocateSecurityParameterIndex( direction, remoteAddress.getHostAddress(), spi, new Binder()); if (result == null) { @@ -228,7 +227,7 @@ public final class IpSecManager { * for this user * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved */ - public SecurityParameterIndex reserveSecurityParameterIndex( + public SecurityParameterIndex allocateSecurityParameterIndex( int direction, InetAddress remoteAddress) throws ResourceUnavailableException { try { return new SecurityParameterIndex( @@ -255,7 +254,7 @@ public final class IpSecManager { * for this user * @throws SpiUnavailableException indicating that the requested SPI could not be reserved */ - public SecurityParameterIndex reserveSecurityParameterIndex( + public SecurityParameterIndex allocateSecurityParameterIndex( int direction, InetAddress remoteAddress, int requestedSpi) throws SpiUnavailableException, ResourceUnavailableException { if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) { @@ -273,16 +272,18 @@ public final class IpSecManager { * unprotected traffic can resume on that socket. * * <p>For security reasons, the destination address of any traffic on the socket must match the - * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any + * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any * other IP address will result in an IOException. In addition, reads and writes on the socket * will throw IOException if the user deactivates the transform (by calling {@link * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}. * - * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform - * will be removed. However, inbound traffic on the old transform will continue to be decrypted - * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap - * allows rekey procedures where both transforms are valid until both endpoints are using the - * new transform and all in-flight packets have been received. + * <h4>Rekey Procedure</h4> + * + * <p>When applying a new tranform to a socket, the previous transform will be removed. However, + * inbound traffic on the old transform will continue to be decrypted until that transform is + * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures + * where both transforms are valid until both endpoints are using the new transform and all + * in-flight packets have been received. * * @param socket a stream socket * @param transform a transport mode {@code IpSecTransform} @@ -310,11 +311,13 @@ public final class IpSecManager { * will throw IOException if the user deactivates the transform (by calling {@link * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}. * - * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform - * will be removed. However, inbound traffic on the old transform will continue to be decrypted - * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap - * allows rekey procedures where both transforms are valid until both endpoints are using the - * new transform and all in-flight packets have been received. + * <h4>Rekey Procedure</h4> + * + * <p>When applying a new tranform to a socket, the previous transform will be removed. However, + * inbound traffic on the old transform will continue to be decrypted until that transform is + * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures + * where both transforms are valid until both endpoints are using the new transform and all + * in-flight packets have been received. * * @param socket a datagram socket * @param transform a transport mode {@code IpSecTransform} @@ -342,11 +345,13 @@ public final class IpSecManager { * will throw IOException if the user deactivates the transform (by calling {@link * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}. * - * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform - * will be removed. However, inbound traffic on the old transform will continue to be decrypted - * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap - * allows rekey procedures where both transforms are valid until both endpoints are using the - * new transform and all in-flight packets have been received. + * <h4>Rekey Procedure</h4> + * + * <p>When applying a new tranform to a socket, the previous transform will be removed. However, + * inbound traffic on the old transform will continue to be decrypted until that transform is + * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures + * where both transforms are valid until both endpoints are using the new transform and all + * in-flight packets have been received. * * @param socket a socket file descriptor * @param transform a transport mode {@code IpSecTransform} @@ -379,7 +384,8 @@ public final class IpSecManager { * Applications should probably not use this API directly. Instead, they should use {@link * VpnService} to provide VPN capability in a more generic fashion. * - * TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked. + * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked. + * * @param net a {@link Network} that will be tunneled via IP Sec. * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform. * @hide @@ -469,7 +475,8 @@ public final class IpSecManager { * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is * lost, all traffic will drop. * - * TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked. + * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked. + * * @param net a network that currently has transform applied to it. * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given * network diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index cda4ec762caf..7cd742b417a4 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -47,7 +47,7 @@ import java.net.InetAddress; * system resources. * * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the - * Internet Protocol</a> + * Internet Protocol</a> */ public final class IpSecTransform implements AutoCloseable { private static final String TAG = "IpSecTransform"; @@ -116,8 +116,7 @@ public final class IpSecTransform implements AutoCloseable { } /** - * Checks the result status and throws an appropriate exception if - * the status is not Status.OK. + * Checks the result status and throws an appropriate exception if the status is not Status.OK. */ private void checkResultStatus(int status) throws IOException, IpSecManager.ResourceUnavailableException, @@ -267,9 +266,7 @@ public final class IpSecTransform implements AutoCloseable { return; } - /** - * This class is used to build {@link IpSecTransform} objects. - */ + /** This class is used to build {@link IpSecTransform} objects. */ public static class Builder { private Context mContext; private IpSecConfig mConfig; @@ -339,7 +336,7 @@ public final class IpSecTransform implements AutoCloseable { * * <p>Because IPsec operates at the IP layer, this 32-bit identifier uniquely identifies * packets to a given destination address. To prevent SPI collisions, values should be - * reserved by calling {@link IpSecManager#reserveSecurityParameterIndex}. + * reserved by calling {@link IpSecManager#allocateSecurityParameterIndex}. * * <p>If the SPI and algorithms are omitted for one direction, traffic in that direction * will not be encrypted or authenticated. @@ -374,10 +371,9 @@ public final class IpSecTransform implements AutoCloseable { * <p>This allows IPsec traffic to pass through a NAT. * * @see <a href="https://tools.ietf.org/html/rfc3948">RFC 3948, UDP Encapsulation of IPsec - * ESP Packets</a> + * ESP Packets</a> * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.23">RFC 7296 section 2.23, - * NAT Traversal of IKEv2</a> - * + * NAT Traversal of IKEv2</a> * @param localSocket a socket for sending and receiving encapsulated traffic * @param remotePort the UDP port number of the remote host that will send and receive * encapsulated traffic. In the case of IKEv2, this should be port 4500. @@ -402,7 +398,6 @@ public final class IpSecTransform implements AutoCloseable { * * @param intervalSeconds the maximum number of seconds between keepalive packets. Must be * between 20s and 3600s. - * * @hide */ @SystemApi @@ -418,7 +413,6 @@ public final class IpSecTransform implements AutoCloseable { * will not affect any network traffic until it has been applied to one or more sockets. * * @see IpSecManager#applyTransportModeTransform - * * @param remoteAddress the remote {@code InetAddress} of traffic on sockets that will use * this transform * @throws IllegalArgumentException indicating that a particular combination of transform @@ -453,8 +447,8 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform buildTunnelModeTransform( InetAddress localAddress, InetAddress remoteAddress) { - //FIXME: argument validation here - //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); + // FIXME: argument validation here + // throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); mConfig.setLocalAddress(localAddress.getHostAddress()); mConfig.setRemoteAddress(remoteAddress.getHostAddress()); mConfig.setMode(MODE_TUNNEL); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index fb60bbb483b1..3504142a81c9 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -774,6 +774,25 @@ public class UserManager { public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock"; /** + * Specifies that the managed profile is not allowed to have unified lock screen challenge with + * the primary user. + * + * <p><strong>Note:</strong> Setting this restriction alone doesn't automatically set a + * separate challenge. Profile owner can ask the user to set a new password using + * {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD} and verify it using + * {@link DevicePolicyManager#isUsingUnifiedPassword(ComponentName)}. + * + * <p>Can be set by profile owners. It only has effect on managed profiles when set by managed + * profile owner. Has no effect on non-managed profiles or users. + * <p>Key for user restrictions. + * <p>Type: Boolean + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password"; + + /** * Allows apps in the parent profile to handle web links from the managed profile. * * This user restriction has an effect only in a managed profile. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4ecbdf587600..abc16cec269d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11145,6 +11145,19 @@ public final class Settings { "notification_snooze_options"; /** + * Configuration flags for SQLite Compatibility WAL. Encoded as a key-value list, separated + * by commas. E.g.: compatibility_wal_supported=true, wal_syncmode=OFF + * + * Supported keys: + * compatibility_wal_supported (boolean) + * wal_syncmode (String) + * + * @hide + */ + public static final String SQLITE_COMPATIBILITY_WAL_FLAGS = + "sqlite_compatibility_wal_flags"; + + /** * Enable GNSS Raw Measurements Full Tracking? * 0 = no * 1 = yes diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl new file mode 100644 index 000000000000..fe13179cab47 --- /dev/null +++ b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 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.security.recoverablekeystore; + +/* @hide */ +parcelable KeyDerivationParameters; diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java new file mode 100644 index 000000000000..2205c416921d --- /dev/null +++ b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 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.security.recoverablekeystore; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Collection of parameters which define a key derivation function. + * Supports + * + * <ul> + * <li>SHA256 + * <li>Argon2id + * </ul> + * @hide + */ +public final class KeyDerivationParameters implements Parcelable { + private final int mAlgorithm; + private byte[] mSalt; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ALGORITHM_SHA256, ALGORITHM_ARGON2ID}) + public @interface KeyDerivationAlgorithm { + } + + /** + * Salted SHA256 + */ + public static final int ALGORITHM_SHA256 = 1; + + /** + * Argon2ID + */ + // TODO: add Argon2ID support. + public static final int ALGORITHM_ARGON2ID = 2; + + /** + * Creates instance of the class to to derive key using salted SHA256 hash. + */ + public KeyDerivationParameters createSHA256Parameters(@NonNull byte[] salt) { + return new KeyDerivationParameters(ALGORITHM_SHA256, salt); + } + + private KeyDerivationParameters(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) { + mAlgorithm = algorithm; + mSalt = Preconditions.checkNotNull(salt); + } + + /** + * Gets algorithm. + */ + public @KeyDerivationAlgorithm int getAlgorithm() { + return mAlgorithm; + } + + /** + * Gets salt. + */ + public @NonNull byte[] getSalt() { + return mSalt; + } + + public static final Parcelable.Creator<KeyDerivationParameters> CREATOR = + new Parcelable.Creator<KeyDerivationParameters>() { + public KeyDerivationParameters createFromParcel(Parcel in) { + return new KeyDerivationParameters(in); + } + + public KeyDerivationParameters[] newArray(int length) { + return new KeyDerivationParameters[length]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mAlgorithm); + out.writeByteArray(mSalt); + } + + protected KeyDerivationParameters(Parcel in) { + mAlgorithm = in.readInt(); + mSalt = in.createByteArray(); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl new file mode 100644 index 000000000000..1058463aa561 --- /dev/null +++ b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 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.security.keystore.recoverablekeystore; + +/* @hide */ +parcelable KeyEntryRecoveryData; diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java new file mode 100644 index 000000000000..80f5aa71acd8 --- /dev/null +++ b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 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.security.recoverablekeystore; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + + +/** + * Helper class with data necessary recover a single application key, given a recovery key. + * + * <ul> + * <li>Alias - Keystore alias of the key. + * <li>Encrypted key material. + * </ul> + * + * Note that Application info is not included. Recovery Agent can only make its own keys + * recoverable. + * + * @hide + */ +public final class KeyEntryRecoveryData implements Parcelable { + private final byte[] mAlias; + // The only supported format is AES-256 symmetric key. + private final byte[] mEncryptedKeyMaterial; + + public KeyEntryRecoveryData(@NonNull byte[] alias, @NonNull byte[] encryptedKeyMaterial) { + mAlias = Preconditions.checkNotNull(alias); + mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial); + } + + /** + * Application-specific alias of the key. + * @see java.security.KeyStore.aliases + */ + public @NonNull byte[] getAlias() { + return mAlias; + } + + /** + * Encrypted key material encrypted by recovery key. + */ + public @NonNull byte[] getEncryptedKeyMaterial() { + return mEncryptedKeyMaterial; + } + + public static final Parcelable.Creator<KeyEntryRecoveryData> CREATOR = + new Parcelable.Creator<KeyEntryRecoveryData>() { + public KeyEntryRecoveryData createFromParcel(Parcel in) { + return new KeyEntryRecoveryData(in); + } + + public KeyEntryRecoveryData[] newArray(int length) { + return new KeyEntryRecoveryData[length]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeByteArray(mAlias); + out.writeByteArray(mEncryptedKeyMaterial); + } + + protected KeyEntryRecoveryData(Parcel in) { + mAlias = in.createByteArray(); + mEncryptedKeyMaterial = in.createByteArray(); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl new file mode 100644 index 000000000000..bd760516c6c3 --- /dev/null +++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 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.security.recoverablekeystore; + +/* @hide */ +parcelable KeyStoreRecoveryData; diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java new file mode 100644 index 000000000000..087f7a25688d --- /dev/null +++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 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.security.recoverablekeystore; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.util.List; + +/** + * Helper class which returns data necessary to recover keys. + * Contains + * + * <ul> + * <li>Snapshot version. + * <li>Recovery metadata with UI and key derivation parameters. + * <li>List of application keys encrypted by recovery key. + * <li>Encrypted recovery key. + * </ul> + * + * @hide + */ +public final class KeyStoreRecoveryData implements Parcelable { + private final int mSnapshotVersion; + private final List<KeyStoreRecoveryMetadata> mRecoveryMetadata; + private final List<KeyEntryRecoveryData> mApplicationKeyBlobs; + private final byte[] mEncryptedRecoveryKeyBlob; + + public KeyStoreRecoveryData(int snapshotVersion, @NonNull List<KeyStoreRecoveryMetadata> + recoveryMetadata, @NonNull List<KeyEntryRecoveryData> applicationKeyBlobs, + @NonNull byte[] encryptedRecoveryKeyBlob) { + mSnapshotVersion = snapshotVersion; + mRecoveryMetadata = Preconditions.checkNotNull(recoveryMetadata); + mApplicationKeyBlobs = Preconditions.checkNotNull(applicationKeyBlobs); + mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob); + } + + /** + * Snapshot version for given account. It is incremented when user secret or list of application + * keys changes. + */ + public int getSnapshotVersion() { + return mSnapshotVersion; + } + + /** + * UI and key derivation parameters. Note that combination of secrets may be used. + */ + public @NonNull List<KeyStoreRecoveryMetadata> getRecoveryMetadata() { + return mRecoveryMetadata; + } + + /** + * List of application keys, with key material encrypted by + * the recovery key ({@link #getEncryptedRecoveryKeyBlob}). + */ + public @NonNull List<KeyEntryRecoveryData> getApplicationKeyBlobs() { + return mApplicationKeyBlobs; + } + + /** + * Recovery key blob, encrypted by user secret and recovery service public key. + */ + public @NonNull byte[] getEncryptedRecoveryKeyBlob() { + return mEncryptedRecoveryKeyBlob; + } + + public static final Parcelable.Creator<KeyStoreRecoveryData> CREATOR = + new Parcelable.Creator<KeyStoreRecoveryData>() { + public KeyStoreRecoveryData createFromParcel(Parcel in) { + return new KeyStoreRecoveryData(in); + } + + public KeyStoreRecoveryData[] newArray(int length) { + return new KeyStoreRecoveryData[length]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mSnapshotVersion); + out.writeTypedList(mRecoveryMetadata); + out.writeByteArray(mEncryptedRecoveryKeyBlob); + out.writeTypedList(mApplicationKeyBlobs); + } + + protected KeyStoreRecoveryData(Parcel in) { + mSnapshotVersion = in.readInt(); + mRecoveryMetadata = in.createTypedArrayList(KeyStoreRecoveryMetadata.CREATOR); + mEncryptedRecoveryKeyBlob = in.createByteArray(); + mApplicationKeyBlobs = in.createTypedArrayList(KeyEntryRecoveryData.CREATOR); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl new file mode 100644 index 000000000000..e1d49defe157 --- /dev/null +++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 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.security.recoverablekeystore; + +/* @hide */ +parcelable KeyStoreRecoveryMetadata; diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java new file mode 100644 index 000000000000..43f9c80571b3 --- /dev/null +++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2017 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.security.recoverablekeystore; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/** + * Helper class with data necessary to recover Keystore on a new device. + * It defines UI shown to the user and a way to derive a cryptographic key from user output. + * + * @hide + */ +public final class KeyStoreRecoveryMetadata implements Parcelable { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD}) + public @interface UserSecretType { + } + + /** + * Lockscreen secret is required to recover KeyStore. + */ + public static final int TYPE_LOCKSCREEN = 1; + + /** + * Custom passphrase, unrelated to lock screen, is required to recover KeyStore. + */ + public static final int TYPE_CUSTOM_PASSWORD = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_PIN, TYPE_PASSWORD, TYPE_PATTERN}) + public @interface LockScreenUiFormat { + } + + /** + * Pin with digits only. + */ + public static final int TYPE_PIN = 1; + + /** + * Password. String with latin-1 characters only. + */ + public static final int TYPE_PASSWORD = 2; + + /** + * Pattern with 3 by 3 grid. + */ + public static final int TYPE_PATTERN = 3; + + @UserSecretType + private final int mUserSecretType; + + @LockScreenUiFormat + private final int mLockScreenUiFormat; + + /** + * Parameters of key derivation function, including algorithm, difficulty, salt. + */ + private KeyDerivationParameters mKeyDerivationParameters; + private byte[] mSecret; // Derived from user secret. The field must have limited visibility. + + /** + * @param secret Constructor creates a reference to the secret. Caller must use + * @link {#clearSecret} to overwrite its value in memory. + */ + public KeyStoreRecoveryMetadata(@UserSecretType int userSecretType, + @LockScreenUiFormat int lockScreenUiFormat, + @NonNull KeyDerivationParameters keyDerivationParameters, @NonNull byte[] secret) { + mUserSecretType = userSecretType; + mLockScreenUiFormat = lockScreenUiFormat; + mKeyDerivationParameters = Preconditions.checkNotNull(keyDerivationParameters); + mSecret = Preconditions.checkNotNull(secret); + } + + /** + * Specifies UX shown to user during recovery. + * + * @see KeyStore.TYPE_PIN + * @see KeyStore.TYPE_PASSWORD + * @see KeyStore.TYPE_PATTERN + */ + public @LockScreenUiFormat int getLockScreenUiFormat() { + return mLockScreenUiFormat; + } + + /** + * Specifies function used to derive symmetric key from user input + * Format is defined in separate util class. + */ + public @NonNull KeyDerivationParameters getKeyDerivationParameters() { + return mKeyDerivationParameters; + } + + /** + * Secret string derived from user input. + */ + public @NonNull byte[] getSecret() { + return mSecret; + } + + /** + * @see KeyStore.TYPE_LOCKSCREEN + * @see KeyStore.TYPE_CUSTOM_PASSWORD + */ + public @UserSecretType int getUserSecretType() { + return mUserSecretType; + } + + /** + * Removes secret from memory than object is no longer used. + * Since finalizer call is not reliable, please use @link {#clearSecret} directly. + */ + @Override + protected void finalize() throws Throwable { + clearSecret(); + super.finalize(); + } + + /** + * Fills mSecret with zeroes. + */ + public void clearSecret() { + Arrays.fill(mSecret, (byte) 0); + } + + public static final Parcelable.Creator<KeyStoreRecoveryMetadata> CREATOR = + new Parcelable.Creator<KeyStoreRecoveryMetadata>() { + public KeyStoreRecoveryMetadata createFromParcel(Parcel in) { + return new KeyStoreRecoveryMetadata(in); + } + + public KeyStoreRecoveryMetadata[] newArray(int length) { + return new KeyStoreRecoveryMetadata[length]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mUserSecretType); + out.writeInt(mLockScreenUiFormat); + out.writeTypedObject(mKeyDerivationParameters, flags); + out.writeByteArray(mSecret); + } + + protected KeyStoreRecoveryMetadata(Parcel in) { + mUserSecretType = in.readInt(); + mLockScreenUiFormat = in.readInt(); + mKeyDerivationParameters = in.readTypedObject(KeyDerivationParameters.CREATOR); + mSecret = in.createByteArray(); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java new file mode 100644 index 000000000000..0510320d3e11 --- /dev/null +++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2017 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.security.recoverablekeystore; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.ServiceManager; + +import com.android.internal.widget.ILockSettings; + +import java.util.List; + +/** + * A wrapper around KeyStore which lets key be exported to + * trusted hardware on server side and recovered later. + * + * @hide + */ +public class RecoverableKeyStoreLoader { + + private final ILockSettings mBinder; + + // Exception codes, should be in sync with {@code KeyStoreException}. + public static final int SYSTEM_ERROR = 4; + + public static final int UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20; + + // Too many updates to recovery public key or server parameters. + public static final int RATE_LIMIT_EXCEEDED = 21; + + private RecoverableKeyStoreLoader(ILockSettings binder) { + mBinder = binder; + } + + /** + * @hide + */ + public static RecoverableKeyStoreLoader getInstance() { + ILockSettings lockSettings = + ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings")); + return new RecoverableKeyStoreLoader(lockSettings); + } + + /** + * @hide + */ + public static class RecoverableKeyStoreLoaderException extends Exception { + private final int mErrorCode; + + public RecoverableKeyStoreLoaderException(int errorCode, String message) { + super(message); + mErrorCode = errorCode; + } + + public int getErrorCode() { + return mErrorCode; + } + } + + /** + * Initializes key recovery service for the calling application. RecoverableKeyStoreLoader + * randomly chooses one of the keys from the list + * and keeps it to use for future key export operations. Collection of all keys + * in the list must be signed by the provided {@code rootCertificateAlias}, which must also be + * present in the list of root certificates preinstalled on the device. The random selection + * allows RecoverableKeyStoreLoader to select which of a set of remote recovery service + * devices will be used. + * + * <p>In addition, RecoverableKeyStoreLoader enforces a delay of three months between + * consecutive initialization attempts, to limit the ability of an attacker to often switch + * remote recovery devices and significantly increase number of recovery attempts. + * + * @param rootCertificateAlias alias of a root certificate preinstalled on the device + * @param signedPublicKeyList binary blob a list of X509 certificates and signature + * @throws RecoverableKeyStoreLoaderException if signature is invalid, or key rotation was rate + * limited. + * @hide + */ + public void initRecoveryService(@NonNull String rootCertificateAlias, + @NonNull byte[] signedPublicKeyList) + throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + // TODO: extend widget/ILockSettings.aidl + /* try { + mBinder.initRecoveryService(rootCertificate, publicKeyList); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } */ + } + + /** + * Returns data necessary to store all recoverable keys for given account. + * Key material is encrypted with user secret and recovery public key. + */ + public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account) + throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } + + /** + * Server parameters used to generate new recovery key blobs. This value will be included in + * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}. + * The same value must be included in vaultParams {@link startRecoverySession} + * + * @see #getRecoveryData + * @throws RecoverableKeyStoreLoaderException If parameters rotation is rate limited. + */ + public void updateServerParameters(long serverParameters) + throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } + + /** + * Updates recovery status for given keys. + * It is used to notify keystore that key was successfully stored on the server or + * there were an error. Returned as a part of KeyInfo data structure. + * + * @param packageName Application whose recoverable keys' statuses are to be updated. + * @param aliases List of application-specific key aliases. If the array is empty, updates the + * status for all existing recoverable keys. + * @param status Status specific to recovery agent. + */ + public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases, + int status) throws NameNotFoundException, RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } + + /** + * Specifies a set of secret types used for end-to-end keystore encryption. + * Knowing all of them is necessary to recover data. + * + * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or + * {@link KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD} + */ + public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType + int[] secretTypes) throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } + + /** + * Defines a set of secret types used for end-to-end keystore encryption. + * Knowing all of them is necessary to generate KeyStoreRecoveryData. + * @see KeyStoreRecoveryData + */ + public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes() + throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } + + /** + * Returns a list of recovery secret types, necessary to create a pending recovery snapshot. + * When user enters a secret of a pending type + * {@link #recoverySecretAvailable} should be called. + */ + public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes() + throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } + + /** + * Method notifies KeyStore that a user-generated secret is available. + * This method generates a symmetric session key which a trusted remote device can use + * to return a recovery key. + * Caller should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value + * in memory. + * + * @param recoverySecret user generated secret together with parameters necessary to + * regenerate it on a new device. + */ + public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret) + throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } + + /** + * Initializes recovery session and returns a blob with proof of recovery secrets possession. + * The method generates symmetric key for a session, which trusted remote device can use + * to return recovery key. + * + * @param sessionId ID for recovery session. + * @param verifierPublicKey Certificate with Public key used to create the recovery blob on + * the source device. Keystore will verify the certificate using root of trust. + * @param vaultParams Must match the parameters in the corresponding field in the recovery blob. + * Used to limit number of guesses. + * @param vaultChallenge Data passed from server for this recovery session and used to prevent + * replay attacks + * @param secrets Secrets provided by user, the method only uses type and secret fields. + * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and + * contains a proof of user secrets, session symmetric key and parameters necessary to identify + * the counter with the number of failed recovery attempts. + */ + public @NonNull byte[] startRecoverySession(@NonNull String sessionId, + @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams, + @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets) + throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } + + /** + * Imports keys. + * + * @param sessionId Id for recovery session, same as in = {@link startRecoverySession}. + * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. + * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob + * and session. KeyStore only uses package names from the application info in + * {@link KeyEntryRecoveryData}. Caller is responsibility to perform certificates check. + */ + public void recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, + @NonNull List<KeyEntryRecoveryData> applicationKeys) + throws RecoverableKeyStoreLoaderException { + throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented"); + } +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ab5372a6b0ac..02beee05811b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4299,6 +4299,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ Runnable mShowTooltipRunnable; Runnable mHideTooltipRunnable; + + /** + * Hover move is ignored if it is within this distance in pixels from the previous one. + */ + int mHoverSlop; + + /** + * Update the anchor position if it significantly (that is by at least mHoverSlop) + * different from the previously stored position. Ignoring insignificant changes + * filters out the jitter which is typical for such input sources as stylus. + * + * @return True if the position has been updated. + */ + private boolean updateAnchorPos(MotionEvent event) { + final int newAnchorX = (int) event.getX(); + final int newAnchorY = (int) event.getY(); + if (Math.abs(newAnchorX - mAnchorX) <= mHoverSlop + && Math.abs(newAnchorY - mAnchorY) <= mHoverSlop) { + return false; + } + mAnchorX = newAnchorX; + mAnchorY = newAnchorY; + return true; + } + + /** + * Clear the anchor position to ensure that the next change is considered significant. + */ + private void clearAnchorPos() { + mAnchorX = Integer.MAX_VALUE; + mAnchorY = Integer.MAX_VALUE; + } } TooltipInfo mTooltipInfo; @@ -26816,6 +26848,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTooltipInfo = new TooltipInfo(); mTooltipInfo.mShowTooltipRunnable = this::showHoverTooltip; mTooltipInfo.mHideTooltipRunnable = this::hideTooltip; + mTooltipInfo.mHoverSlop = ViewConfiguration.get(mContext).getScaledHoverSlop(); + mTooltipInfo.clearAnchorPos(); } mTooltipInfo.mTooltipText = tooltipText; } @@ -26882,6 +26916,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTooltipInfo.mTooltipPopup.hide(); mTooltipInfo.mTooltipPopup = null; mTooltipInfo.mTooltipFromLongClick = false; + mTooltipInfo.clearAnchorPos(); if (mAttachInfo != null) { mAttachInfo.mTooltipHost = null; } @@ -26906,11 +26941,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mViewFlags & TOOLTIP) != TOOLTIP || (mViewFlags & ENABLED_MASK) != ENABLED) { break; } - if (!mTooltipInfo.mTooltipFromLongClick) { + if (!mTooltipInfo.mTooltipFromLongClick && mTooltipInfo.updateAnchorPos(event)) { if (mTooltipInfo.mTooltipPopup == null) { // Schedule showing the tooltip after a timeout. - mTooltipInfo.mAnchorX = (int) event.getX(); - mTooltipInfo.mAnchorY = (int) event.getY(); removeCallbacks(mTooltipInfo.mShowTooltipRunnable); postDelayed(mTooltipInfo.mShowTooltipRunnable, ViewConfiguration.getHoverTooltipShowTimeout()); @@ -26932,6 +26965,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return true; case MotionEvent.ACTION_HOVER_EXIT: + mTooltipInfo.clearAnchorPos(); if (!mTooltipInfo.mTooltipFromLongClick) { hideTooltip(); } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index c44c8dda83a9..c5a94daaba53 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -290,6 +290,7 @@ public class ViewConfiguration { private final int mMaximumFlingVelocity; private final int mScrollbarSize; private final int mTouchSlop; + private final int mHoverSlop; private final int mMinScrollbarTouchTarget; private final int mDoubleTapTouchSlop; private final int mPagingTouchSlop; @@ -320,6 +321,7 @@ public class ViewConfiguration { mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY; mScrollbarSize = SCROLL_BAR_SIZE; mTouchSlop = TOUCH_SLOP; + mHoverSlop = TOUCH_SLOP / 2; mMinScrollbarTouchTarget = MIN_SCROLLBAR_TOUCH_TARGET; mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP; mPagingTouchSlop = PAGING_TOUCH_SLOP; @@ -407,6 +409,8 @@ public class ViewConfiguration { com.android.internal.R.bool.config_ui_enableFadingMarquee); mTouchSlop = res.getDimensionPixelSize( com.android.internal.R.dimen.config_viewConfigurationTouchSlop); + mHoverSlop = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_viewConfigurationHoverSlop); mMinScrollbarTouchTarget = res.getDimensionPixelSize( com.android.internal.R.dimen.config_minScrollbarTouchTarget); mPagingTouchSlop = mTouchSlop * 2; @@ -640,6 +644,14 @@ public class ViewConfiguration { } /** + * @return Distance in pixels a hover can wander while it is still considered "stationary". + * + */ + public int getScaledHoverSlop() { + return mHoverSlop; + } + + /** * @return Distance in pixels the first touch can wander before we do not consider this a * potential double tap event * @hide diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c6c42faad6b0..3f8da093487b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -324,6 +324,13 @@ public final class ViewRootImpl implements ViewParent, final Rect mTempRect; // used in the transaction to not thrash the heap. final Rect mVisRect; // used to retrieve visible rect of focused view. + // This is used to reduce the race between window focus changes being dispatched from + // the window manager and input events coming through the input system. + @GuardedBy("this") + boolean mUpcomingWindowFocus; + @GuardedBy("this") + boolean mUpcomingInTouchMode; + public boolean mTraversalScheduled; int mTraversalBarrier; boolean mWillDrawSoon; @@ -2461,6 +2468,93 @@ public final class ViewRootImpl implements ViewParent, } } + private void handleWindowFocusChanged() { + final boolean hasWindowFocus; + final boolean inTouchMode; + synchronized (this) { + hasWindowFocus = mUpcomingWindowFocus; + inTouchMode = mUpcomingInTouchMode; + } + + if (mAttachInfo.mHasWindowFocus == hasWindowFocus) { + return; + } + + if (mAdded) { + profileRendering(hasWindowFocus); + + if (hasWindowFocus) { + ensureTouchModeLocally(inTouchMode); + if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) { + mFullRedrawNeeded = true; + try { + final WindowManager.LayoutParams lp = mWindowAttributes; + final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null; + mAttachInfo.mThreadedRenderer.initializeIfNeeded( + mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets); + } catch (OutOfResourcesException e) { + Log.e(mTag, "OutOfResourcesException locking surface", e); + try { + if (!mWindowSession.outOfMemory(mWindow)) { + Slog.w(mTag, "No processes killed for memory;" + + " killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { + } + // Retry in a bit. + mHandler.sendMessageDelayed(mHandler.obtainMessage( + MSG_WINDOW_FOCUS_CHANGED), 500); + return; + } + } + } + + mAttachInfo.mHasWindowFocus = hasWindowFocus; + + mLastWasImTarget = WindowManager.LayoutParams + .mayUseInputMethod(mWindowAttributes.flags); + + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { + imm.onPreWindowFocus(mView, hasWindowFocus); + } + if (mView != null) { + mAttachInfo.mKeyDispatchState.reset(); + mView.dispatchWindowFocusChanged(hasWindowFocus); + mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus); + + if (mAttachInfo.mTooltipHost != null) { + mAttachInfo.mTooltipHost.hideTooltip(); + } + } + + // Note: must be done after the focus change callbacks, + // so all of the view state is set up correctly. + if (hasWindowFocus) { + if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { + imm.onPostWindowFocus(mView, mView.findFocus(), + mWindowAttributes.softInputMode, + !mHasHadWindowFocus, mWindowAttributes.flags); + } + // Clear the forward bit. We can just do this directly, since + // the window manager doesn't care about it. + mWindowAttributes.softInputMode &= + ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; + ((WindowManager.LayoutParams) mView.getLayoutParams()) + .softInputMode &= + ~WindowManager.LayoutParams + .SOFT_INPUT_IS_FORWARD_NAVIGATION; + mHasHadWindowFocus = true; + } else { + if (mPointerCapture) { + handlePointerCaptureChanged(false); + } + } + } + mFirstInputStage.onWindowFocusChanged(hasWindowFocus); + } + private void handleOutOfResourcesException(Surface.OutOfResourcesException e) { Log.e(mTag, "OutOfResourcesException initializing HW surface", e); try { @@ -3909,81 +4003,7 @@ public final class ViewRootImpl implements ViewParent, } break; case MSG_WINDOW_FOCUS_CHANGED: { - final boolean hasWindowFocus = msg.arg1 != 0; - if (mAdded) { - mAttachInfo.mHasWindowFocus = hasWindowFocus; - - profileRendering(hasWindowFocus); - - if (hasWindowFocus) { - boolean inTouchMode = msg.arg2 != 0; - ensureTouchModeLocally(inTouchMode); - if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) { - mFullRedrawNeeded = true; - try { - final WindowManager.LayoutParams lp = mWindowAttributes; - final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null; - mAttachInfo.mThreadedRenderer.initializeIfNeeded( - mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets); - } catch (OutOfResourcesException e) { - Log.e(mTag, "OutOfResourcesException locking surface", e); - try { - if (!mWindowSession.outOfMemory(mWindow)) { - Slog.w(mTag, "No processes killed for memory;" - + " killing self"); - Process.killProcess(Process.myPid()); - } - } catch (RemoteException ex) { - } - // Retry in a bit. - sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), - 500); - return; - } - } - } - - mLastWasImTarget = WindowManager.LayoutParams - .mayUseInputMethod(mWindowAttributes.flags); - - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { - imm.onPreWindowFocus(mView, hasWindowFocus); - } - if (mView != null) { - mAttachInfo.mKeyDispatchState.reset(); - mView.dispatchWindowFocusChanged(hasWindowFocus); - mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus); - - if (mAttachInfo.mTooltipHost != null) { - mAttachInfo.mTooltipHost.hideTooltip(); - } - } - - // Note: must be done after the focus change callbacks, - // so all of the view state is set up correctly. - if (hasWindowFocus) { - if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { - imm.onPostWindowFocus(mView, mView.findFocus(), - mWindowAttributes.softInputMode, - !mHasHadWindowFocus, mWindowAttributes.flags); - } - // Clear the forward bit. We can just do this directly, since - // the window manager doesn't care about it. - mWindowAttributes.softInputMode &= - ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; - ((WindowManager.LayoutParams) mView.getLayoutParams()) - .softInputMode &= - ~WindowManager.LayoutParams - .SOFT_INPUT_IS_FORWARD_NAVIGATION; - mHasHadWindowFocus = true; - } else { - if (mPointerCapture) { - handlePointerCaptureChanged(false); - } - } - } - mFirstInputStage.onWindowFocusChanged(hasWindowFocus); + handleWindowFocusChanged(); } break; case MSG_DIE: doDie(); @@ -6854,6 +6874,7 @@ public final class ViewRootImpl implements ViewParent, } if (stage != null) { + handleWindowFocusChanged(); stage.deliver(q); } else { finishInputEvent(q); @@ -7159,10 +7180,12 @@ public final class ViewRootImpl implements ViewParent, } public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { + synchronized (this) { + mUpcomingWindowFocus = hasFocus; + mUpcomingInTouchMode = inTouchMode; + } Message msg = Message.obtain(); msg.what = MSG_WINDOW_FOCUS_CHANGED; - msg.arg1 = hasFocus ? 1 : 0; - msg.arg2 = inTouchMode ? 1 : 0; mHandler.sendMessage(msg); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index a050a3ce0cf2..5d6d5bcaa6bc 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -11228,7 +11228,9 @@ public class BatteryStatsImpl extends BatteryStats { reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null, status, plugType, level, temp); - final boolean onBattery = plugType == BATTERY_PLUGGED_NONE; + final boolean onBattery = + plugType == BATTERY_PLUGGED_NONE && + status != BatteryManager.BATTERY_STATUS_UNKNOWN; final long uptime = mClocks.uptimeMillis(); final long elapsedRealtime = mClocks.elapsedRealtime(); if (!mHaveBatteryLevel) { @@ -11262,7 +11264,8 @@ public class BatteryStatsImpl extends BatteryStats { mRecordingHistory = true; startRecordingHistory(elapsedRealtime, uptime, true); } - } else if (level < 96) { + } else if (level < 96 && + status != BatteryManager.BATTERY_STATUS_UNKNOWN) { if (!mRecordingHistory) { mRecordingHistory = true; startRecordingHistory(elapsedRealtime, uptime, true); @@ -11400,9 +11403,12 @@ public class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtime, uptime); } } - if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) { - // We don't record history while we are plugged in and fully charged. - // The next time we are unplugged, history will be cleared. + if (!onBattery && + (status == BatteryManager.BATTERY_STATUS_FULL || + status == BatteryManager.BATTERY_STATUS_UNKNOWN)) { + // We don't record history while we are plugged in and fully charged + // (or when battery is not present). The next time we are + // unplugged, history will be cleared. mRecordingHistory = DEBUG; } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 4be6b28ad95c..e871003a6489 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -27,7 +27,6 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.UserInfo; import android.os.AsyncTask; -import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -35,7 +34,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IStorageManager; @@ -964,9 +962,12 @@ public class LockPatternUtils { /** * Retrieves whether the current profile and device locks can be unified. + * @param userHandle profile user handle. */ public boolean isSeparateProfileChallengeAllowedToUnify(int userHandle) { - return getDevicePolicyManager().isProfileActivePasswordSufficientForParent(userHandle); + return getDevicePolicyManager().isProfileActivePasswordSufficientForParent(userHandle) + && !getUserManager().hasUserRestriction( + UserManager.DISALLOW_UNIFIED_PASSWORD, UserHandle.of(userHandle)); } private boolean hasSeparateChallenge(int userHandle) { diff --git a/core/proto/android/graphics/point.proto b/core/proto/android/graphics/point.proto new file mode 100644 index 000000000000..5ae17cb5df5d --- /dev/null +++ b/core/proto/android/graphics/point.proto @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.graphics; + +option java_multiple_files = true; + +message PointProto { + optional int32 x = 1; + optional int32 y = 2; +} + diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 915fe9d5ce41..ef7b6b0019f3 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -170,6 +170,8 @@ message WindowStateProto { optional WindowStateAnimatorProto animator = 13; optional bool animating_exit = 14; repeated WindowStateProto child_windows = 15; + optional .android.graphics.RectProto surface_position = 16; + optional .android.graphics.RectProto shown_position = 17; } message IdentifierProto { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8377751f4236..dc791cf68b6b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2097,6 +2097,10 @@ movement threshold where scrolling should begin. --> <dimen name="config_viewConfigurationTouchSlop">8dp</dimen> + <!-- Base "hover slop" value used by ViewConfiguration as a + movement threshold under which hover is considered "stationary". --> + <dimen name="config_viewConfigurationHoverSlop">4dp</dimen> + <!-- Minimum velocity to initiate a fling, as measured in dips per second. --> <dimen name="config_viewMinFlingVelocity">50dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index be7ce5fc7e57..4b2424ffd213 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -470,6 +470,7 @@ <java-symbol type="dimen" name="config_minScrollbarTouchTarget" /> <java-symbol type="dimen" name="config_prefDialogWidth" /> <java-symbol type="dimen" name="config_viewConfigurationTouchSlop" /> + <java-symbol type="dimen" name="config_viewConfigurationHoverSlop" /> <java-symbol type="dimen" name="config_viewMinFlingVelocity" /> <java-symbol type="dimen" name="config_viewMaxFlingVelocity" /> <java-symbol type="dimen" name="config_scrollbarSize" /> diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java new file mode 100644 index 000000000000..230655de8a00 --- /dev/null +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 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.database.sqlite; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.database.DatabaseUtils; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +/** + * Tests for {@link SQLiteCompatibilityWalFlags} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class SQLiteCompatibilityWalFlagsTest { + + private SQLiteDatabase mDatabase; + + @After + public void tearDown() { + SQLiteCompatibilityWalFlags.reset(); + if (mDatabase != null) { + mDatabase.close(); + SQLiteDatabase.deleteDatabase(new File(mDatabase.getPath())); + } + } + + @Test + public void testParseConfig() { + SQLiteCompatibilityWalFlags.init(""); + assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); + + SQLiteCompatibilityWalFlags.init(null); + assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); + + SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=false,wal_syncmode=OFF"); + assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); + assertFalse(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); + assertEquals("OFF", SQLiteCompatibilityWalFlags.getWALSyncMode()); + + SQLiteCompatibilityWalFlags.init("wal_syncmode=VALUE"); + assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); + assertEquals(SQLiteGlobal.isCompatibilityWalSupported(), + SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); + assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode()); + + SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true"); + assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); + assertEquals(SQLiteGlobal.getWALSyncMode(), + SQLiteCompatibilityWalFlags.getWALSyncMode()); + assertTrue(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); + + SQLiteCompatibilityWalFlags.reset(); + SQLiteCompatibilityWalFlags.init("Invalid value"); + assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); + } + + @Test + public void testApplyFlags() { + Context ctx = InstrumentationRegistry.getContext(); + + SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=NORMAL"); + mDatabase = SQLiteDatabase + .openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null); + String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null); + assertEquals("WAL", journalMode.toUpperCase()); + String syncMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA synchronous", null); + assertEquals("Normal mode (1) is expected", "1", syncMode); + } + + +} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index b03f0540064f..907a1b843c0c 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -335,6 +335,7 @@ public class SettingsBackupTest { Settings.Global.SMS_SHORT_CODES_UPDATE_CONTENT_URL, Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL, Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS, + Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, Settings.Global.STORAGE_BENCHMARK_INTERVAL, Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD, Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS, diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java index abcccbdbc9fb..c6b6c668f03a 100644 --- a/graphics/java/android/graphics/Point.java +++ b/graphics/java/android/graphics/Point.java @@ -18,6 +18,7 @@ package android.graphics; import android.os.Parcel; import android.os.Parcelable; +import android.util.proto.ProtoOutputStream; import java.io.PrintWriter; @@ -121,6 +122,21 @@ public class Point implements Parcelable { out.writeInt(y); } + /** + * Write to a protocol buffer output stream. + * Protocol buffer message definition at {@link android.graphics.PointProto} + * + * @param protoOutputStream Stream to write the Rect object to. + * @param fieldId Field Id of the Rect as defined in the parent message + * @hide + */ + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + final long token = protoOutputStream.start(fieldId); + protoOutputStream.write(PointProto.X, x); + protoOutputStream.write(PointProto.Y, y); + protoOutputStream.end(token); + } + public static final Parcelable.Creator<Point> CREATOR = new Parcelable.Creator<Point>() { /** * Return a new point from the data in the specified parcel. diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index b4331b21cf13..eca52cc3e8b6 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -16,6 +16,7 @@ package android.security; import android.content.pm.StringParceledListSlice; +import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.ParcelableKeyGenParameterSpec; /** @@ -33,6 +34,7 @@ interface IKeyChainService { void setUserSelectable(String alias, boolean isUserSelectable); boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); + boolean attestKey(in String alias, in byte[] challenge, out KeymasterCertificateChain chain); // APIs used by CertInstaller and DevicePolicyManager String installCaCertificate(in byte[] caCertificate); diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java index cf4347d14d6d..0811100f74c7 100644 --- a/keystore/java/android/security/keystore/AttestationUtils.java +++ b/keystore/java/android/security/keystore/AttestationUtils.java @@ -73,6 +73,33 @@ public abstract class AttestationUtils { public static final int ID_TYPE_MEID = 3; /** + * Creates an array of X509Certificates from the provided KeymasterCertificateChain. + * + * @hide Only called by the DevicePolicyManager. + */ + @NonNull public static X509Certificate[] parseCertificateChain( + final KeymasterCertificateChain kmChain) throws + KeyAttestationException { + // Extract certificate chain. + final Collection<byte[]> rawChain = kmChain.getCertificates(); + if (rawChain.size() < 2) { + throw new KeyAttestationException("Attestation certificate chain contained " + + rawChain.size() + " entries. At least two are required."); + } + final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream(); + try { + for (final byte[] cert : rawChain) { + concatenatedRawChain.write(cert); + } + return CertificateFactory.getInstance("X.509").generateCertificates( + new ByteArrayInputStream(concatenatedRawChain.toByteArray())) + .toArray(new X509Certificate[0]); + } catch (Exception e) { + throw new KeyAttestationException("Unable to construct certificate chain", e); + } + } + + /** * Performs attestation of the device's identifiers. This method returns a certificate chain * whose first element contains the requested device identifiers in an extension. The device's * manufacturer, model, brand, device and product are always also included in the attestation. @@ -173,22 +200,18 @@ public abstract class AttestationUtils { KeyStore.getKeyStoreException(errorCode)); } - // Extract certificate chain. - final Collection<byte[]> rawChain = outChain.getCertificates(); - if (rawChain.size() < 2) { - throw new DeviceIdAttestationException("Attestation certificate chain contained " - + rawChain.size() + " entries. At least two are required."); - } - final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream(); try { - for (final byte[] cert : rawChain) { - concatenatedRawChain.write(cert); - } - return CertificateFactory.getInstance("X.509").generateCertificates( - new ByteArrayInputStream(concatenatedRawChain.toByteArray())) - .toArray(new X509Certificate[0]); - } catch (Exception e) { - throw new DeviceIdAttestationException("Unable to construct certificate chain", e); + return parseCertificateChain(outChain); + } catch (KeyAttestationException e) { + throw new DeviceIdAttestationException(e.getMessage(), e); } } + + /** + * Returns true if the attestation chain provided is a valid key attestation chain. + * @hide + */ + public static boolean isChainValid(KeymasterCertificateChain chain) { + return chain != null && chain.getCertificates().size() >= 2; + } } diff --git a/keystore/java/android/security/keystore/KeyAttestationException.java b/keystore/java/android/security/keystore/KeyAttestationException.java new file mode 100644 index 000000000000..6cf5fb2ff8ef --- /dev/null +++ b/keystore/java/android/security/keystore/KeyAttestationException.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 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.security.keystore; + +/** + * Thrown when {@link AttestationUtils} is unable to attest the given key or handle + * the resulting attestation record. + * + * @hide + */ +public class KeyAttestationException extends Exception { + /** + * Constructs a new {@code KeyAttestationException} with the current stack trace and the + * specified detail message. + * + * @param detailMessage the detail message for this exception. + */ + public KeyAttestationException(String detailMessage) { + super(detailMessage); + } + + /** + * Constructs a new {@code KeyAttestationException} with the current stack trace, the + * specified detail message and the specified cause. + * + * @param message the detail message for this exception. + * @param cause the cause of this exception, may be {@code null}. + */ + public KeyAttestationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 87677d479471..1238d8774e58 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -680,6 +680,40 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * A Builder constructor taking in an already-built KeyGenParameterSpec, useful for + * changing values of the KeyGenParameterSpec quickly. + * @hide Should be used internally only. + */ + public Builder(@NonNull KeyGenParameterSpec sourceSpec) { + this(sourceSpec.getKeystoreAlias(), sourceSpec.getPurposes()); + mUid = sourceSpec.getUid(); + mKeySize = sourceSpec.getKeySize(); + mSpec = sourceSpec.getAlgorithmParameterSpec(); + mCertificateSubject = sourceSpec.getCertificateSubject(); + mCertificateSerialNumber = sourceSpec.getCertificateSerialNumber(); + mCertificateNotBefore = sourceSpec.getCertificateNotBefore(); + mCertificateNotAfter = sourceSpec.getCertificateNotAfter(); + mKeyValidityStart = sourceSpec.getKeyValidityStart(); + mKeyValidityForOriginationEnd = sourceSpec.getKeyValidityForOriginationEnd(); + mKeyValidityForConsumptionEnd = sourceSpec.getKeyValidityForConsumptionEnd(); + mPurposes = sourceSpec.getPurposes(); + if (sourceSpec.isDigestsSpecified()) { + mDigests = sourceSpec.getDigests(); + } + mEncryptionPaddings = sourceSpec.getEncryptionPaddings(); + mSignaturePaddings = sourceSpec.getSignaturePaddings(); + mBlockModes = sourceSpec.getBlockModes(); + mRandomizedEncryptionRequired = sourceSpec.isRandomizedEncryptionRequired(); + mUserAuthenticationRequired = sourceSpec.isUserAuthenticationRequired(); + mUserAuthenticationValidityDurationSeconds = + sourceSpec.getUserAuthenticationValidityDurationSeconds(); + mAttestationChallenge = sourceSpec.getAttestationChallenge(); + mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded(); + mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody(); + mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment(); + } + + /** * Sets the UID which will own the key. * * @param uid UID or {@code -1} for the UID of the current process. diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java index 73b489f98e1d..254b6be77ea8 100644 --- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java @@ -55,7 +55,7 @@ public final class ParcelableKeyGenParameterSpecTest { static final int USER_AUTHENTICATION_DURATION = 300; static final byte[] ATTESTATION_CHALLENGE = new byte[] {'c', 'h'}; - KeyGenParameterSpec configureDefaultSpec() { + public static KeyGenParameterSpec configureDefaultSpec() { return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) .setUid(UID) .setKeySize(KEYSIZE) @@ -80,7 +80,7 @@ public final class ParcelableKeyGenParameterSpecTest { .build(); } - void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) { + public static void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) { assertThat(spec.getKeystoreAlias(), is(alias)); assertThat(spec.getPurposes(), is(KEY_PURPOSES)); assertThat(spec.getUid(), is(uid)); diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java new file mode 100644 index 000000000000..865cad472eb5 --- /dev/null +++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 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.security.keystore; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.security.ParcelableKeyGenParameterSpecTest; +import android.support.test.runner.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link KeyGenParameterSpec}. */ +@RunWith(AndroidJUnit4.class) +public final class KeyGenParameterSpecTest { + static final String ALIAS = "keystore-alias"; + static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY; + + @Test + public void testBuilderCopyingValues() { + KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec(); + KeyGenParameterSpec copiedSpec = + new KeyGenParameterSpec.Builder(spec).build(); + ParcelableKeyGenParameterSpecTest.validateSpecValues( + copiedSpec, spec.getUid(), spec.getKeystoreAlias()); + } + + @Test + public void testBuilderCopyingEmptyValues() { + KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES).build(); + KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build(); + + assertThat(copiedSpec.getKeystoreAlias(), is(ALIAS)); + assertThat(copiedSpec.getPurposes(), is(KEY_PURPOSES)); + } + + @Test + public void testCanModifyValuesInCopiedBuilder() { + KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec(); + KeyGenParameterSpec copiedSpec = + new KeyGenParameterSpec.Builder(spec) + .setAttestationChallenge(null) + .build(); + + assertEquals(copiedSpec.getAttestationChallenge(), null); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 27bc599f7f52..9f393215c9bd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -49,6 +49,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe private boolean mIsVerifyUnlockOnly; private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid; private SecurityCallback mSecurityCallback; + private AlertDialog mAlertDialog; private final KeyguardUpdateMonitor mUpdateMonitor; @@ -95,6 +96,10 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe @Override public void onPause() { + if (mAlertDialog != null) { + mAlertDialog.dismiss(); + mAlertDialog = null; + } if (mCurrentSecuritySelection != SecurityMode.None) { getSecurityView(mCurrentSecuritySelection).onPause(); } @@ -174,16 +179,20 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } private void showDialog(String title, String message) { - final AlertDialog dialog = new AlertDialog.Builder(mContext) + if (mAlertDialog != null) { + mAlertDialog.dismiss(); + } + + mAlertDialog = new AlertDialog.Builder(mContext) .setTitle(title) .setMessage(message) .setCancelable(false) .setNeutralButton(R.string.ok, null) .create(); if (!(mContext instanceof Activity)) { - dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); } - dialog.show(); + mAlertDialog.show(); } private void showTimeoutDialog(int userId, int timeoutMs) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java index 01679ddad04b..ad31cc5e0c33 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java @@ -25,6 +25,7 @@ import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -39,7 +40,9 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Slog; import android.util.SparseArray; import android.view.KeyEvent; @@ -93,6 +96,7 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec protected final Object mLock; protected final SecurityPolicy mSecurityPolicy; + private final AppOpsManager mAppOpsManager; // The service that's bound to this instance. Whenever this value is non-null, this // object is registered as a death recipient @@ -250,6 +254,7 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec mAccessibilityServiceInfo = accessibilityServiceInfo; mLock = lock; mSecurityPolicy = securityPolicy; + mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mGlobalActionPerformer = globalActionPerfomer; mSystemSupport = systemSupport; mInvocationHandler = new InvocationHandler(mainHandler.getLooper()); @@ -706,6 +711,16 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { + + // Skip this if the caller is the Accessibility InteractionBridge. + if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { + if (mAppOpsManager.noteOp(AppOpsManager.OP_PERFORM_ACCESSIBILITY_ACTION, + Binder.getCallingUid(), mComponentName.getPackageName()) + != AppOpsManager.MODE_ALLOWED) { + return false; + } + } + final int resolvedWindowId; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { @@ -725,6 +740,15 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec @Override public boolean performGlobalAction(int action) { + // Skip this if the caller is the Accessibility InteractionBridge. + if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { + if (mAppOpsManager.noteOp(AppOpsManager.OP_PERFORM_ACCESSIBILITY_ACTION, + Binder.getCallingUid(), mComponentName.getPackageName()) + != AppOpsManager.MODE_ALLOWED) { + return false; + } + } + synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { return false; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 5ec3850cc5e7..972a426dd8c5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2985,11 +2985,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private boolean isValidPackageForUid(String packageName, int uid) { + final long token = Binder.clearCallingIdentity(); try { return uid == mPackageManager.getPackageUidAsUser( packageName, UserHandle.getUserId(uid)); } catch (PackageManager.NameNotFoundException e) { return false; + } finally { + Binder.restoreCallingIdentity(token); } } diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 4adcb99f5628..94b06b67ff87 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -25,7 +25,6 @@ import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKU import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR; -import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_INIT; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR; @@ -120,6 +119,7 @@ import com.android.server.backup.params.RestoreParams; import com.android.server.backup.restore.ActiveRestoreSession; import com.android.server.backup.restore.PerformUnifiedRestoreTask; import com.android.server.backup.transport.TransportClient; +import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.BackupManagerMonitorUtils; import com.android.server.backup.utils.BackupObserverUtils; @@ -1083,56 +1083,35 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter return mBackupPasswordManager.backupPasswordMatches(currentPw); } - // Maintain persistent state around whether need to do an initialize operation. - // Must be called with the queue lock held. - public void recordInitPendingLocked(boolean isPending, String transportName) { + /** + * Maintain persistent state around whether need to do an initialize operation. + * Must be called with the queue lock held. + */ + @GuardedBy("mQueueLock") + public void recordInitPendingLocked( + boolean isPending, String transportName, String transportDirName) { if (MORE_DEBUG) { Slog.i(TAG, "recordInitPendingLocked: " + isPending + " on transport " + transportName); } - mBackupHandler.removeMessages(MSG_RETRY_INIT); - try { - IBackupTransport transport = mTransportManager.getTransportBinder(transportName); - if (transport != null) { - String transportDirName = transport.transportDirName(); - File stateDir = new File(mBaseStateDir, transportDirName); - File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); - - if (isPending) { - // We need an init before we can proceed with sending backup data. - // Record that with an entry in our set of pending inits, as well as - // journaling it via creation of a sentinel file. - mPendingInits.add(transportName); - try { - (new FileOutputStream(initPendingFile)).close(); - } catch (IOException ioe) { - // Something is badly wrong with our permissions; just try to move on - } - } else { - // No more initialization needed; wipe the journal and reset our state. - initPendingFile.delete(); - mPendingInits.remove(transportName); - } - return; // done; don't fall through to the error case - } - } catch (Exception e) { - // transport threw when asked its name; fall through to the lookup-failed case - Slog.e(TAG, "Transport " + transportName + " failed to report name: " - + e.getMessage()); - } + File stateDir = new File(mBaseStateDir, transportDirName); + File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); - // The named transport doesn't exist or threw. This operation is - // important, so we record the need for a an init and post a message - // to retry the init later. if (isPending) { + // We need an init before we can proceed with sending backup data. + // Record that with an entry in our set of pending inits, as well as + // journaling it via creation of a sentinel file. mPendingInits.add(transportName); - mBackupHandler.sendMessageDelayed( - mBackupHandler.obtainMessage(MSG_RETRY_INIT, - (isPending ? 1 : 0), - 0, - transportName), - TRANSPORT_RETRY_INTERVAL); + try { + (new FileOutputStream(initPendingFile)).close(); + } catch (IOException ioe) { + // Something is badly wrong with our permissions; just try to move on + } + } else { + // No more initialization needed; wipe the journal and reset our state. + initPendingFile.delete(); + mPendingInits.remove(transportName); } } @@ -1614,27 +1593,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter return BackupManager.ERROR_BACKUP_NOT_ALLOWED; } - // We're using pieces of the new binding on-demand infra-structure and the old always-bound - // infra-structure below this comment. The TransportManager.getCurrentTransportClient() line - // is using the new one and TransportManager.getCurrentTransportBinder() is using the old. - // This is weird but there is a reason. - // This is the natural place to put TransportManager.getCurrentTransportClient() because of - // the null handling below that should be the same for TransportClient. - // TransportClient.connect() would return a IBackupTransport for us (instead of using the - // old infra), but it may block and we don't want this in this thread. - // The only usage of transport in this method is for transport.transportDirName(). When the - // push-from-transport part of binding on-demand is in place we will replace the calls for - // IBackupTransport.transportDirName() with calls for - // TransportManager.transportDirName(transportName) or similar. So we'll leave the old piece - // here until we implement that. - // TODO(brufino): Remove always-bound code mTransportManager.getCurrentTransportBinder() TransportClient transportClient = mTransportManager.getCurrentTransportClient("BMS.requestBackup()"); - IBackupTransport transport = mTransportManager.getCurrentTransportBinder(); - if (transportClient == null || transport == null) { - if (transportClient != null) { - mTransportManager.disposeOfTransportClient(transportClient, "BMS.requestBackup()"); - } + if (transportClient == null) { BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED); monitor = BackupManagerMonitorUtils.monitorEvent(monitor, BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL, @@ -1679,15 +1640,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter + " k/v backups"); } - String dirName; - try { - dirName = transport.transportDirName(); - } catch (Exception e) { - Slog.e(TAG, "Transport unavailable while attempting backup: " + e.getMessage()); - BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED); - return BackupManager.ERROR_TRANSPORT_ABORTED; - } - + String dirName = transportClient.getTransportDirName(); boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0; Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP); @@ -1998,16 +1951,17 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter writeFullBackupScheduleAsync(); } - private boolean fullBackupAllowable(IBackupTransport transport) { - if (transport == null) { - Slog.w(TAG, "Transport not present; full data backup not performed"); + private boolean fullBackupAllowable(String transportName) { + if (!mTransportManager.isTransportRegistered(transportName)) { + Slog.w(TAG, "Transport not registered; full data backup not performed"); return false; } // Don't proceed unless we have already established package metadata // for the current dataset via a key/value backup pass. try { - File stateDir = new File(mBaseStateDir, transport.transportDirName()); + String transportDirName = mTransportManager.getTransportDirName(transportName); + File stateDir = new File(mBaseStateDir, transportDirName); File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL); if (pmState.length() <= 0) { if (DEBUG) { @@ -2097,7 +2051,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter headBusy = false; - if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) { + String transportName = mTransportManager.getCurrentTransportName(); + if (!fullBackupAllowable(transportName)) { if (MORE_DEBUG) { Slog.i(TAG, "Preconditions not met; not running full backup"); } @@ -2545,7 +2500,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter throw new IllegalStateException("Restore supported only for the device owner"); } - if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) { + String transportName = mTransportManager.getCurrentTransportName(); + if (!fullBackupAllowable(transportName)) { Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?"); } else { if (DEBUG) { @@ -2826,10 +2782,30 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter if (wasEnabled && mProvisioned) { // NOTE: we currently flush every registered transport, not just // the currently-active one. - String[] allTransports = mTransportManager.getBoundTransportNames(); + List<String> transportNames = new ArrayList<>(); + List<String> transportDirNames = new ArrayList<>(); + mTransportManager.forEachRegisteredTransport( + name -> { + final String dirName; + try { + dirName = + mTransportManager + .getTransportDirName(name); + } catch (TransportNotRegisteredException e) { + // Should never happen + Slog.e(TAG, "Unexpected unregistered transport", e); + return; + } + transportNames.add(name); + transportDirNames.add(dirName); + }); + // build the set of transports for which we are posting an init - for (String transport : allTransports) { - recordInitPendingLocked(true, transport); + for (int i = 0; i < transportNames.size(); i++) { + recordInitPendingLocked( + true, + transportNames.get(i), + transportDirNames.get(i)); } mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), mRunInitIntent); @@ -2993,7 +2969,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter final long oldId = Binder.clearCallingIdentity(); try { - mTransportManager.describeTransport( + mTransportManager.updateTransportAttributes( transportComponent, name, configurationIntent, @@ -3093,23 +3069,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter public Intent getConfigurationIntent(String transportName) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getConfigurationIntent"); - - final IBackupTransport transport = mTransportManager.getTransportBinder(transportName); - if (transport != null) { - try { - final Intent intent = transport.configurationIntent(); - if (MORE_DEBUG) { - Slog.d(TAG, "getConfigurationIntent() returning config intent " - + intent); - } - return intent; - } catch (Exception e) { - /* fall through to return null */ - Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage()); + try { + Intent intent = mTransportManager.getTransportConfigurationIntent(transportName); + if (MORE_DEBUG) { + Slog.d(TAG, "getConfigurationIntent() returning intent " + intent); } + return intent; + } catch (TransportNotRegisteredException e) { + Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage()); + return null; } - - return null; } // Supply the configuration summary string for the given transport. If the name is @@ -3143,22 +3112,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getDataManagementIntent"); - final IBackupTransport transport = mTransportManager.getTransportBinder(transportName); - if (transport != null) { - try { - final Intent intent = transport.dataManagementIntent(); - if (MORE_DEBUG) { - Slog.d(TAG, "getDataManagementIntent() returning intent " - + intent); - } - return intent; - } catch (Exception e) { - /* fall through to return null */ - Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage()); + try { + Intent intent = mTransportManager.getTransportDataManagementIntent(transportName); + if (MORE_DEBUG) { + Slog.d(TAG, "getDataManagementIntent() returning intent " + intent); } + return intent; + } catch (TransportNotRegisteredException e) { + Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage()); + return null; } - - return null; } // Supply the menu label for affordances that fire the manage-data intent @@ -3168,19 +3131,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getDataManagementLabel"); - final IBackupTransport transport = mTransportManager.getTransportBinder(transportName); - if (transport != null) { - try { - final String text = transport.dataManagementLabel(); - if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text); - return text; - } catch (Exception e) { - /* fall through to return null */ - Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage()); + try { + String label = mTransportManager.getTransportDataManagementLabel(transportName); + if (MORE_DEBUG) { + Slog.d(TAG, "getDataManagementLabel() returning " + label); } + return label; + } catch (TransportNotRegisteredException e) { + Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage()); + return null; } - - return null; } // Callback: a requested backup agent has been instantiated. This should only @@ -3497,14 +3457,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter pw.println("Available transports:"); final String[] transports = listAllTransports(); if (transports != null) { - for (String t : listAllTransports()) { + for (String t : transports) { pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * " : " ") + t); try { IBackupTransport transport = mTransportManager.getTransportBinder(t); - File dir = new File(mBaseStateDir, transport.transportDirName()); + File dir = new File(mBaseStateDir, + mTransportManager.getTransportDirName(t)); pw.println(" destination: " + transport.currentDestinationString()); - pw.println(" intent: " + transport.configurationIntent()); + pw.println(" intent: " + + mTransportManager.getTransportConfigurationIntent(t)); for (File f : dir.listFiles()) { pw.println( " " + f.getName() + " - " + f.length() + " state bytes"); diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java index 1f3ebf936ceb..f1854433dcd0 100644 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ b/services/backup/java/com/android/server/backup/TransportManager.java @@ -49,12 +49,14 @@ import com.android.server.EventLogTags; import com.android.server.backup.transport.TransportClient; import com.android.server.backup.transport.TransportClientManager; import com.android.server.backup.transport.TransportConnectionListener; +import com.android.server.backup.transport.TransportNotRegisteredException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -236,6 +238,72 @@ public class TransportManager { return getTransportBinder(mCurrentTransportName); } + /** + * Retrieve the configuration intent of {@code transportName}. + * @throws TransportNotRegisteredException if the transport is not registered. + */ + @Nullable + public Intent getTransportConfigurationIntent(String transportName) + throws TransportNotRegisteredException { + synchronized (mTransportLock) { + return getRegisteredTransportDescriptionOrThrowLocked(transportName) + .configurationIntent; + } + } + + /** + * Retrieve the data management intent of {@code transportName}. + * @throws TransportNotRegisteredException if the transport is not registered. + */ + @Nullable + public Intent getTransportDataManagementIntent(String transportName) + throws TransportNotRegisteredException { + synchronized (mTransportLock) { + return getRegisteredTransportDescriptionOrThrowLocked(transportName) + .dataManagementIntent; + } + } + + /** + * Retrieve the data management label of {@code transportName}. + * @throws TransportNotRegisteredException if the transport is not registered. + */ + @Nullable + public String getTransportDataManagementLabel(String transportName) + throws TransportNotRegisteredException { + synchronized (mTransportLock) { + return getRegisteredTransportDescriptionOrThrowLocked(transportName) + .dataManagementLabel; + } + } + + /** + * Retrieve the transport dir name of {@code transportName}. + * @throws TransportNotRegisteredException if the transport is not registered. + */ + public String getTransportDirName(String transportName) + throws TransportNotRegisteredException { + synchronized (mTransportLock) { + return getRegisteredTransportDescriptionOrThrowLocked(transportName) + .transportDirName; + } + } + + /** + * Execute {@code transportConsumer} for each registered transport passing the transport name. + * This is called with an internal lock held, ensuring that the transport will remain registered + * while {@code transportConsumer} is being executed. Don't do heavy operations in + * {@code transportConsumer}. + */ + public void forEachRegisteredTransport(Consumer<String> transportConsumer) { + synchronized (mTransportLock) { + for (TransportDescription transportDescription + : mRegisteredTransportsDescriptionMap.values()) { + transportConsumer.accept(transportDescription.name); + } + } + } + public String getTransportName(IBackupTransport binder) { synchronized (mTransportLock) { for (TransportConnection conn : mValidTransports.values()) { @@ -281,6 +349,17 @@ public class TransportManager { } @GuardedBy("mTransportLock") + private TransportDescription getRegisteredTransportDescriptionOrThrowLocked( + String transportName) throws TransportNotRegisteredException { + TransportDescription description = getRegisteredTransportDescriptionLocked(transportName); + if (description == null) { + throw new TransportNotRegisteredException(transportName); + } + return description; + } + + + @GuardedBy("mTransportLock") @Nullable private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked( String transportName) { @@ -385,13 +464,13 @@ public class TransportManager { * Updates given values for the transport already registered and identified with * {@param transportComponent}. If the transport is not registered it will log and return. */ - public void describeTransport( + public void updateTransportAttributes( ComponentName transportComponent, String name, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, - String dataManagementLabel) { + @Nullable String dataManagementLabel) { synchronized (mTransportLock) { TransportDescription description = mRegisteredTransportsDescriptionMap.get(transportComponent); @@ -766,7 +845,7 @@ public class TransportManager { @Nullable private Intent configurationIntent; private String currentDestinationString; @Nullable private Intent dataManagementIntent; - private String dataManagementLabel; + @Nullable private String dataManagementLabel; private TransportDescription( String name, @@ -774,7 +853,7 @@ public class TransportManager { @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, - String dataManagementLabel) { + @Nullable String dataManagementLabel) { this.name = name; this.transportDirName = transportDirName; this.configurationIntent = configurationIntent; diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java index 4c7834825054..f29a9c2ecb6d 100644 --- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java +++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java @@ -293,16 +293,6 @@ public class BackupHandler extends Handler { break; } - case MSG_RETRY_INIT: { - synchronized (backupManagerService.getQueueLock()) { - backupManagerService.recordInitPendingLocked(msg.arg1 != 0, (String) msg.obj); - backupManagerService.getAlarmManager().set(AlarmManager.RTC_WAKEUP, - System.currentTimeMillis(), - backupManagerService.getRunInitIntent()); - } - break; - } - case MSG_RUN_GET_RESTORE_SETS: { // Like other async operations, this is entered with the wakelock held RestoreSet[] sets = null; diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java index 690922fd9aa9..b21b0724acc2 100644 --- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java +++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java @@ -98,7 +98,8 @@ public class PerformInitializeTask implements Runnable { transportDirName)); EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); synchronized (backupManagerService.getQueueLock()) { - backupManagerService.recordInitPendingLocked(false, transportName); + backupManagerService.recordInitPendingLocked( + false, transportName, transportDirName); } notifyResult(transportName, BackupTransport.TRANSPORT_OK); } else { @@ -107,7 +108,8 @@ public class PerformInitializeTask implements Runnable { Slog.e(TAG, "Transport error in initializeDevice()"); EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); synchronized (backupManagerService.getQueueLock()) { - backupManagerService.recordInitPendingLocked(true, transportName); + backupManagerService.recordInitPendingLocked( + true, transportName, transportDirName); } notifyResult(transportName, status); result = status; diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java new file mode 100644 index 000000000000..26bf92cb10eb --- /dev/null +++ b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 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.backup.transport; + +import android.util.AndroidException; + +import com.android.server.backup.TransportManager; + +/** + * Exception thrown when the transport is not registered. + * + * @see TransportManager#getTransportDirName(String) + * @see TransportManager#getTransportConfigurationIntent(String) + * @see TransportManager#getTransportDataManagementIntent(String) + * @see TransportManager#getTransportDataManagementLabel(String) + */ +public class TransportNotRegisteredException extends AndroidException { + public TransportNotRegisteredException(String transportName) { + super("Transport " + transportName + " not registered"); + } +} diff --git a/services/core/Android.bp b/services/core/Android.bp index a2505a8805e3..9fb26819dae0 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -31,6 +31,7 @@ java_library_static { static_libs: [ "time_zone_distro", "time_zone_distro_installer", + "android.hardware.broadcastradio-V2.0-java", "android.hardware.health-V1.0-java", "android.hardware.health-V2.0-java", "android.hardware.weaver-V1.0-java", diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 924e736bab07..04d292fa1ae4 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -421,7 +421,9 @@ public final class BatteryService extends SystemService { boolean logOutlier = false; long dischargeDuration = 0; - mBatteryLevelCritical = (mHealthInfo.batteryLevel <= mCriticalBatteryLevel); + mBatteryLevelCritical = + mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN + && mHealthInfo.batteryLevel <= mCriticalBatteryLevel; if (mHealthInfo.chargerAcOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_AC; } else if (mHealthInfo.chargerUsbOnline) { @@ -509,6 +511,8 @@ public final class BatteryService extends SystemService { if (!mBatteryLevelLow) { // Should we now switch in to low battery mode? if (mPlugType == BATTERY_PLUGGED_NONE + && mHealthInfo.batteryStatus != + BatteryManager.BATTERY_STATUS_UNKNOWN && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) { mBatteryLevelLow = true; } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 2116d9a818cf..e9eb3b3c469d 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -827,15 +827,15 @@ public class IpSecService extends IIpSecService.Stub { throw new IllegalArgumentException("Invalid Direction: " + direction); } - @Override /** Get a new SPI and maintain the reservation in the system server */ - public synchronized IpSecSpiResponse reserveSecurityParameterIndex( + @Override + public synchronized IpSecSpiResponse allocateSecurityParameterIndex( int direction, String remoteAddress, int requestedSpi, IBinder binder) throws RemoteException { checkDirection(direction); checkInetAddress(remoteAddress); /* requestedSpi can be anything in the int range, so no check is needed. */ - checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex"); + checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex"); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); int resourceId = mNextResourceId.getAndIncrement(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9366f6e348a3..85303130de64 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2947,9 +2947,11 @@ public class ActivityManagerService extends IActivityManager.Stub try { return super.onTransact(code, data, reply, flags); } catch (RuntimeException e) { - // The activity manager only throws security exceptions, so let's + // The activity manager only throws certain exceptions intentionally, so let's // log all others. - if (!(e instanceof SecurityException)) { + if (!(e instanceof SecurityException + || e instanceof IllegalArgumentException + || e instanceof IllegalStateException)) { Slog.wtf(TAG, "Activity Manager Crash." + " UID:" + Binder.getCallingUid() + " PID:" + Binder.getCallingPid() diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 54938eb05cf5..4f60e1731a50 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -48,6 +48,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ShellCommand; +import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; @@ -128,7 +129,7 @@ final class ActivityManagerShellCommand extends ShellCommand { if (cmd == null) { return handleDefaultCommands(cmd); } - PrintWriter pw = getOutPrintWriter(); + final PrintWriter pw = getOutPrintWriter(); try { switch (cmd) { case "start": @@ -1328,65 +1329,95 @@ final class ActivityManagerShellCommand extends ShellCommand { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) throws RemoteException { synchronized (this) { - mPw.print(uid); - mPw.print(" procstate "); - mPw.print(ProcessList.makeProcStateString(procState)); - mPw.print(" seq "); - mPw.println(procStateSeq); - mPw.flush(); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + mPw.print(uid); + mPw.print(" procstate "); + mPw.print(ProcessList.makeProcStateString(procState)); + mPw.print(" seq "); + mPw.println(procStateSeq); + mPw.flush(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } } } @Override public void onUidGone(int uid, boolean disabled) throws RemoteException { synchronized (this) { - mPw.print(uid); - mPw.print(" gone"); - if (disabled) { - mPw.print(" disabled"); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + mPw.print(uid); + mPw.print(" gone"); + if (disabled) { + mPw.print(" disabled"); + } + mPw.println(); + mPw.flush(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); } - mPw.println(); - mPw.flush(); } } @Override public void onUidActive(int uid) throws RemoteException { synchronized (this) { - mPw.print(uid); - mPw.println(" active"); - mPw.flush(); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + mPw.print(uid); + mPw.println(" active"); + mPw.flush(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } } } @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException { synchronized (this) { - mPw.print(uid); - mPw.print(" idle"); - if (disabled) { - mPw.print(" disabled"); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + mPw.print(uid); + mPw.print(" idle"); + if (disabled) { + mPw.print(" disabled"); + } + mPw.println(); + mPw.flush(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); } - mPw.println(); - mPw.flush(); } } @Override public void onUidCachedChanged(int uid, boolean cached) throws RemoteException { synchronized (this) { - mPw.print(uid); - mPw.println(cached ? " cached" : " uncached"); - mPw.flush(); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + mPw.print(uid); + mPw.println(cached ? " cached" : " uncached"); + mPw.flush(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } } } @Override public void onOomAdjMessage(String msg) { synchronized (this) { - mPw.print("# "); - mPw.println(msg); - mPw.flush(); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + mPw.print("# "); + mPw.println(msg); + mPw.flush(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index edaa51112867..0a42aa9cce63 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2312,7 +2312,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (displayId == INVALID_DISPLAY) { displayId = candidateDisplayId; } - if (displayId != INVALID_DISPLAY) { + if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) { if (r != null) { // TODO: This should also take in the windowing mode and activity type into account. stack = (T) getValidLaunchStackOnDisplay(displayId, r); @@ -2341,7 +2341,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } if (stack != null) { display = stack.getDisplay(); - if (display != null) { + if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) { final int windowingMode = display.resolveWindowingMode(r, options, candidateTask, activityType); if (stack.isCompatible(windowingMode, activityType)) { @@ -2351,6 +2351,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } if (display == null + || !canLaunchOnDisplay(r, display.mDisplayId) // TODO: Can be removed once we figure-out how non-standard types should launch // outside the default display. || (activityType != ACTIVITY_TYPE_STANDARD @@ -2361,6 +2362,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); } + /** @return true if activity record is null or can be launched on provided display. */ + private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) { + if (r == null) { + return true; + } + return r.canBeLaunchedOnDisplay(displayId); + } + /** * Get a topmost stack on the display, that is a valid launch stack for specified activity. * If there is no such stack, new dynamic stack can be created. @@ -4248,22 +4257,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Handle incorrect launch/move to secondary display if needed. if (isSecondaryDisplayPreferred) { - final boolean launchOnSecondaryDisplayFailed; final int actualDisplayId = task.getStack().mDisplayId; if (!task.canBeLaunchedOnDisplay(actualDisplayId)) { - // The task landed on an inappropriate display somehow, move it to the default - // display. - // TODO(multi-display): Find proper stack for the task on the default display. - mService.setTaskWindowingMode(task.taskId, - WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */); - launchOnSecondaryDisplayFailed = true; - } else { - // The task might have landed on a display different from requested. - launchOnSecondaryDisplayFailed = actualDisplayId == DEFAULT_DISPLAY - || (preferredDisplayId != INVALID_DISPLAY - && preferredDisplayId != actualDisplayId); + throw new IllegalStateException("Task resolved to incompatible display"); } - if (launchOnSecondaryDisplayFailed) { + // The task might have landed on a display different from requested. + // TODO(multi-display): Find proper stack for the task on the default display. + mService.setTaskWindowingMode(task.taskId, + WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */); + if (preferredDisplayId != actualDisplayId) { // Display a warning toast that we tried to put a non-resizeable task on a secondary // display with config different from global config. mService.mTaskChangeNotificationController diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 69cc3c7aacbc..abdbfadf8d99 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1812,6 +1812,9 @@ class ActivityStarter { } } } + // Need to update mTargetStack because if task was moved out of it, the original stack may + // be destroyed. + mTargetStack = intentActivity.getStack(); if (!mMovedToFront && mDoResume) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack + " from " + intentActivity); diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index abb296e9c8c3..2de84ab265ff 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -662,7 +662,7 @@ class RecentTasks { * task to be trimmed as a result of that add. */ private boolean canAddTaskWithoutTrim(TaskRecord task) { - return findTrimIndexForAddTask(task) == -1; + return findRemoveIndexForAddTask(task) == -1; } /** @@ -896,7 +896,7 @@ class RecentTasks { } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task); - trimForAddTask(task); + removeForAddTask(task); task.inRecents = true; if (!isAffiliated || needAffiliationFix) { @@ -1175,8 +1175,8 @@ class RecentTasks { * If needed, remove oldest existing entries in recents that are for the same kind * of task as the given one. */ - private void trimForAddTask(TaskRecord task) { - final int removeIndex = findTrimIndexForAddTask(task); + private void removeForAddTask(TaskRecord task) { + final int removeIndex = findRemoveIndexForAddTask(task); if (removeIndex == -1) { // Nothing to trim return; @@ -1187,7 +1187,7 @@ class RecentTasks { // callbacks here. final TaskRecord removedTask = mTasks.remove(removeIndex); if (removedTask != task) { - notifyTaskRemoved(removedTask, TRIMMED); + notifyTaskRemoved(removedTask, !TRIMMED); if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask + " for addition of task=" + task); } @@ -1198,7 +1198,7 @@ class RecentTasks { * Find the task that would be removed if the given {@param task} is added to the recent tasks * list (if any). */ - private int findTrimIndexForAddTask(TaskRecord task) { + private int findRemoveIndexForAddTask(TaskRecord task) { int recentsCount = mTasks.size(); final Intent intent = task.intent; final boolean document = intent != null && intent.isDocument(); @@ -1241,7 +1241,6 @@ class RecentTasks { // don't need to trim it. continue; } else if (maxRecents > 0) { - // Otherwise only trim if we are over our max recents for this task --maxRecents; if (!sameIntent || multiTasksAllowed) { // We don't want to trim if we are not over the max allowed entries and diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index dabcbcdcbb7c..91b3315e4c0d 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -623,6 +623,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi if (toStack == sourceStack) { return false; } + if (!canBeLaunchedOnDisplay(toStack.mDisplayId)) { + return false; + } final int toStackWindowingMode = toStack.getWindowingMode(); final ActivityRecord topActivity = getTopActivity(); @@ -2251,7 +2254,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi String callingPackage = ""; int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE; boolean supportsPictureInPicture = false; - Rect bounds = null; + Rect lastNonFullscreenBounds = null; int minWidth = INVALID_MIN_SIZE; int minHeight = INVALID_MIN_SIZE; int persistTaskVersion = 0; @@ -2336,7 +2339,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi supportsPictureInPicture = Boolean.parseBoolean(attrValue); break; case ATTR_NON_FULLSCREEN_BOUNDS: - bounds = Rect.unflattenFromString(attrValue); + lastNonFullscreenBounds = Rect.unflattenFromString(attrValue); break; case ATTR_MIN_WIDTH: minWidth = Integer.parseInt(attrValue); @@ -2431,7 +2434,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete, minWidth, minHeight); - task.updateOverrideConfiguration(bounds); + task.mLastNonFullscreenBounds = lastNonFullscreenBounds; + task.setBounds(lastNonFullscreenBounds); for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { activities.get(activityNdx).setTask(task); diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java index 8fdbcf6c657b..3064144072ae 100644 --- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java @@ -16,6 +16,7 @@ package com.android.server.broadcastradio; +import android.annotation.NonNull; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; @@ -28,14 +29,16 @@ import android.os.ParcelableException; import com.android.server.SystemService; import java.util.List; +import java.util.Objects; +import java.util.OptionalInt; public class BroadcastRadioService extends SystemService { private final ServiceImpl mServiceImpl = new ServiceImpl(); - /** - * This field is used by native code, do not access or modify. - */ - private final long mNativeContext = nativeInit(); + private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1 = + new com.android.server.broadcastradio.hal1.BroadcastRadioService(); + private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2 = + new com.android.server.broadcastradio.hal2.BroadcastRadioService(); private final Object mLock = new Object(); private List<RadioManager.ModuleProperties> mModules = null; @@ -45,22 +48,18 @@ public class BroadcastRadioService extends SystemService { } @Override - protected void finalize() throws Throwable { - nativeFinalize(mNativeContext); - super.finalize(); - } - - private native long nativeInit(); - private native void nativeFinalize(long nativeContext); - private native List<RadioManager.ModuleProperties> nativeLoadModules(long nativeContext); - private native Tuner nativeOpenTuner(long nativeContext, int moduleId, - RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback); - - @Override public void onStart() { publishBinderService(Context.RADIO_SERVICE, mServiceImpl); } + /** + * Finds next available index for newly loaded modules. + */ + private static int getNextId(@NonNull List<RadioManager.ModuleProperties> modules) { + OptionalInt max = modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max(); + return max.isPresent() ? max.getAsInt() + 1 : 0; + } + private class ServiceImpl extends IRadioService.Stub { private void enforcePolicyAccess() { if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission( @@ -75,11 +74,8 @@ public class BroadcastRadioService extends SystemService { synchronized (mLock) { if (mModules != null) return mModules; - mModules = nativeLoadModules(mNativeContext); - if (mModules == null) { - throw new ParcelableException(new NullPointerException( - "couldn't load radio modules")); - } + mModules = mHal1.loadModules(); + mModules.addAll(mHal2.loadModules(getNextId(mModules))); return mModules; } @@ -93,7 +89,11 @@ public class BroadcastRadioService extends SystemService { throw new IllegalArgumentException("Callback must not be empty"); } synchronized (mLock) { - return nativeOpenTuner(mNativeContext, moduleId, bandConfig, withAudio, callback); + if (mHal2.hasModule(moduleId)) { + throw new RuntimeException("Not implemented"); + } else { + return mHal1.openTuner(moduleId, bandConfig, withAudio, callback); + } } } } diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java new file mode 100644 index 000000000000..e8ac5477469b --- /dev/null +++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2017 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.broadcastradio.hal1; + +import android.annotation.NonNull; +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.radio.IRadioService; +import android.hardware.radio.ITuner; +import android.hardware.radio.ITunerCallback; +import android.hardware.radio.RadioManager; +import android.os.ParcelableException; + +import com.android.server.SystemService; + +import java.util.List; +import java.util.Objects; + +public class BroadcastRadioService { + /** + * This field is used by native code, do not access or modify. + */ + private final long mNativeContext = nativeInit(); + + private final Object mLock = new Object(); + + @Override + protected void finalize() throws Throwable { + nativeFinalize(mNativeContext); + super.finalize(); + } + + private native long nativeInit(); + private native void nativeFinalize(long nativeContext); + private native List<RadioManager.ModuleProperties> nativeLoadModules(long nativeContext); + private native Tuner nativeOpenTuner(long nativeContext, int moduleId, + RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback); + + public @NonNull List<RadioManager.ModuleProperties> loadModules() { + synchronized (mLock) { + return Objects.requireNonNull(nativeLoadModules(mNativeContext)); + } + } + + public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig, + boolean withAudio, @NonNull ITunerCallback callback) { + synchronized (mLock) { + return nativeOpenTuner(mNativeContext, moduleId, bandConfig, withAudio, callback); + } + } +} diff --git a/services/core/java/com/android/server/broadcastradio/Convert.java b/services/core/java/com/android/server/broadcastradio/hal1/Convert.java index 125554fc3025..80c776254d98 100644 --- a/services/core/java/com/android/server/broadcastradio/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal1/Convert.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.broadcastradio; +package com.android.server.broadcastradio.hal1; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/services/core/java/com/android/server/broadcastradio/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java index 2ea4271864f2..cce534d3bdb1 100644 --- a/services/core/java/com/android/server/broadcastradio/Tuner.java +++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.broadcastradio; +package com.android.server.broadcastradio.hal1; import android.annotation.NonNull; import android.graphics.Bitmap; diff --git a/services/core/java/com/android/server/broadcastradio/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java index 2460c67a64a2..673ff88d5c98 100644 --- a/services/core/java/com/android/server/broadcastradio/TunerCallback.java +++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.broadcastradio; +package com.android.server.broadcastradio.hal1; import android.annotation.NonNull; import android.hardware.radio.ITuner; diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java new file mode 100644 index 000000000000..7629477438e4 --- /dev/null +++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2017 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.broadcastradio.hal2; + +import android.annotation.NonNull; +import android.hardware.radio.RadioManager; +import android.hardware.broadcastradio.V2_0.IBroadcastRadio; +import android.hidl.manager.V1_0.IServiceManager; +import android.os.RemoteException; +import android.util.Slog; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class BroadcastRadioService { + private static final String TAG = "BcRadio2Srv"; + + private final Map<Integer, RadioModule> mModules = new HashMap<>(); + + private static @NonNull List<String> listByInterface(@NonNull String fqName) { + try { + IServiceManager manager = IServiceManager.getService(); + if (manager == null) { + Slog.e(TAG, "Failed to get HIDL Service Manager"); + return Collections.emptyList(); + } + + List<String> list = manager.listByInterface(fqName); + if (list == null) { + Slog.e(TAG, "Didn't get interface list from HIDL Service Manager"); + return Collections.emptyList(); + } + return list; + } catch (RemoteException ex) { + Slog.e(TAG, "Failed fetching interface list", ex); + return Collections.emptyList(); + } + } + + public @NonNull Collection<RadioManager.ModuleProperties> loadModules(int idx) { + Slog.v(TAG, "loadModules(" + idx + ")"); + + for (String serviceName : listByInterface(IBroadcastRadio.kInterfaceName)) { + Slog.v(TAG, "checking service: " + serviceName); + + RadioModule module = RadioModule.tryLoadingModule(idx, serviceName); + if (module != null) { + Slog.i(TAG, "loaded broadcast radio module " + idx + ": " + + serviceName + " (HAL 2.0)"); + mModules.put(idx++, module); + } + } + + return mModules.values().stream().map(module -> module.mProperties). + collect(Collectors.toList()); + } + + public boolean hasModule(int id) { + return mModules.containsKey(id); + } +} diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java new file mode 100644 index 000000000000..c3394e9e0df5 --- /dev/null +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2017 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.broadcastradio.hal2; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.broadcastradio.V2_0.Properties; +import android.hardware.broadcastradio.V2_0.VendorKeyValue; +import android.hardware.radio.ProgramSelector; +import android.hardware.radio.RadioManager; +import android.util.Slog; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +class Convert { + private static final String TAG = "BcRadio2Srv.convert"; + + private static @NonNull Map<String, String> + vendorInfoFromHal(@Nullable List<VendorKeyValue> info) { + if (info == null) return Collections.emptyMap(); + + Map<String, String> map = new HashMap<>(); + for (VendorKeyValue kvp : info) { + if (kvp.key == null || kvp.value == null) { + Slog.w(TAG, "VendorKeyValue contains null pointers"); + continue; + } + map.put(kvp.key, kvp.value); + } + + return map; + } + + private static @NonNull int[] + identifierTypesToProgramTypes(@NonNull int[] idTypes) { + Set<Integer> pTypes = new HashSet<>(); + + for (int idType : idTypes) { + switch (idType) { + case ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY: + case ProgramSelector.IDENTIFIER_TYPE_RDS_PI: + // TODO(b/69958423): verify AM/FM with region info + pTypes.add(ProgramSelector.PROGRAM_TYPE_AM); + pTypes.add(ProgramSelector.PROGRAM_TYPE_FM); + break; + case ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT: + // TODO(b/69958423): verify AM/FM with region info + pTypes.add(ProgramSelector.PROGRAM_TYPE_AM_HD); + pTypes.add(ProgramSelector.PROGRAM_TYPE_FM_HD); + break; + case ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC: + case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE: + case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID: + case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY: + pTypes.add(ProgramSelector.PROGRAM_TYPE_DAB); + break; + case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID: + case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY: + pTypes.add(ProgramSelector.PROGRAM_TYPE_DRMO); + break; + case ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID: + case ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL: + pTypes.add(ProgramSelector.PROGRAM_TYPE_SXM); + break; + default: + break; + } + if (idType >= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_START + && idType <= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_END) { + pTypes.add(idType); + } + } + + return pTypes.stream().mapToInt(Integer::intValue).toArray(); + } + + static @NonNull RadioManager.ModuleProperties + propertiesFromHal(int id, @NonNull String serviceName, Properties prop) { + Objects.requireNonNull(prop); + + // TODO(b/69958423): implement region info + RadioManager.BandDescriptor[] bands = new RadioManager.BandDescriptor[0]; + + int[] supportedIdentifierTypes = prop.supportedIdentifierTypes.stream(). + mapToInt(Integer::intValue).toArray(); + int[] supportedProgramTypes = identifierTypesToProgramTypes(supportedIdentifierTypes); + + return new RadioManager.ModuleProperties( + id, + serviceName, + + // There is no Class concept in HAL 2.0. + RadioManager.CLASS_AM_FM, + + prop.maker, + prop.product, + prop.version, + prop.serial, + + /* HAL 2.0 only supports single tuner and audio source per + * HAL implementation instance. */ + 1, // numTuners + 1, // numAudioSources + false, // isCaptureSupported + + bands, + true, // isBgScanSupported is deprecated + supportedProgramTypes, + supportedIdentifierTypes, + vendorInfoFromHal(prop.vendorInfo)); + } +} diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java new file mode 100644 index 000000000000..34c1b0ce7d93 --- /dev/null +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2017 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.broadcastradio.hal2; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.radio.RadioManager; +import android.hardware.broadcastradio.V2_0.IBroadcastRadio; +import android.os.RemoteException; +import android.util.Slog; + +import java.util.Objects; + +class RadioModule { + private static final String TAG = "BcRadio2Srv.module"; + + @NonNull private final IBroadcastRadio mService; + @NonNull public final RadioManager.ModuleProperties mProperties; + + private RadioModule(@NonNull IBroadcastRadio service, + @NonNull RadioManager.ModuleProperties properties) { + mProperties = Objects.requireNonNull(properties); + mService = Objects.requireNonNull(service); + } + + public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) { + try { + IBroadcastRadio service = IBroadcastRadio.getService(); + if (service == null) return null; + + RadioManager.ModuleProperties prop = + Convert.propertiesFromHal(idx, fqName, service.getProperties()); + + return new RadioModule(service, prop); + } catch (RemoteException ex) { + Slog.e(TAG, "failed to load module " + fqName, ex); + return null; + } + } +} diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 009f10e74336..bcb57efffc19 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -279,8 +279,8 @@ public final class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L; - private static final int DEFAULT_STANDBY_WORKING_BEATS = 5; // ~ 1 hour, with 11-min beats - private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 31; // ~ 6 hours + private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats + private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours /** diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java index 90c912a5aee4..1ad0cf931165 100644 --- a/services/core/java/com/android/server/location/ContextHubService.java +++ b/services/core/java/com/android/server/location/ContextHubService.java @@ -642,11 +642,10 @@ public class ContextHubService extends IContextHubService.Stub { /** * Loads a nanoapp binary at the specified Context hub. * - * @param contextHubId the ID of the hub to load the binary + * @param contextHubId the ID of the hub to load the binary * @param transactionCallback the client-facing transaction callback interface - * @param nanoAppBinary the binary to load + * @param nanoAppBinary the binary to load * - * @throws RemoteException * @throws IllegalStateException if the transaction queue is full */ @Override @@ -673,11 +672,10 @@ public class ContextHubService extends IContextHubService.Stub { /** * Unloads a nanoapp from the specified Context Hub. * - * @param contextHubId the ID of the hub to unload the nanoapp + * @param contextHubId the ID of the hub to unload the nanoapp * @param transactionCallback the client-facing transaction callback interface - * @param nanoAppId the ID of the nanoapp to unload + * @param nanoAppId the ID of the nanoapp to unload * - * @throws RemoteException * @throws IllegalStateException if the transaction queue is full */ @Override @@ -696,12 +694,59 @@ public class ContextHubService extends IContextHubService.Stub { } /** + * Enables a nanoapp at the specified Context Hub. + * + * @param contextHubId the ID of the hub to enable the nanoapp + * @param transactionCallback the client-facing transaction callback interface + * @param nanoAppId the ID of the nanoapp to enable + * + * @throws IllegalStateException if the transaction queue is full + */ + @Override + public void enableNanoApp( + int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) + throws RemoteException { + checkPermissions(); + if (!checkHalProxyAndContextHubId( + contextHubId, transactionCallback, ContextHubTransaction.TYPE_ENABLE_NANOAPP)) { + return; + } + + ContextHubServiceTransaction transaction = mTransactionManager.createEnableTransaction( + contextHubId, nanoAppId, transactionCallback); + mTransactionManager.addTransaction(transaction); + } + + /** + * Disables a nanoapp at the specified Context Hub. + * + * @param contextHubId the ID of the hub to disable the nanoapp + * @param transactionCallback the client-facing transaction callback interface + * @param nanoAppId the ID of the nanoapp to disable + * + * @throws IllegalStateException if the transaction queue is full + */ + @Override + public void disableNanoApp( + int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) + throws RemoteException { + checkPermissions(); + if (!checkHalProxyAndContextHubId( + contextHubId, transactionCallback, ContextHubTransaction.TYPE_DISABLE_NANOAPP)) { + return; + } + + ContextHubServiceTransaction transaction = mTransactionManager.createDisableTransaction( + contextHubId, nanoAppId, transactionCallback); + mTransactionManager.addTransaction(transaction); + } + + /** * Queries for a list of nanoapps from the specified Context hub. * - * @param contextHubId the ID of the hub to query + * @param contextHubId the ID of the hub to query * @param transactionCallback the client-facing transaction callback interface * - * @throws RemoteException * @throws IllegalStateException if the transaction queue is full */ @Override @@ -713,8 +758,8 @@ public class ContextHubService extends IContextHubService.Stub { return; } - ContextHubServiceTransaction transaction = - mTransactionManager.createQueryTransaction(contextHubId, transactionCallback); + ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction( + contextHubId, transactionCallback); mTransactionManager.addTransaction(transaction); } diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java index 00252bc75bbf..412d43da29f7 100644 --- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java +++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java @@ -143,7 +143,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Creates a transaction for unloading a nanoapp. * - * @param contextHubId the ID of the hub to load the nanoapp to + * @param contextHubId the ID of the hub to unload the nanoapp from * @param nanoAppId the ID of the nanoapp to unload * @param onCompleteCallback the client on complete callback * @return the generated transaction @@ -182,6 +182,76 @@ import java.util.concurrent.atomic.AtomicInteger; } /** + * Creates a transaction for enabling a nanoapp. + * + * @param contextHubId the ID of the hub to enable the nanoapp on + * @param nanoAppId the ID of the nanoapp to enable + * @param onCompleteCallback the client on complete callback + * @return the generated transaction + */ + /* package */ ContextHubServiceTransaction createEnableTransaction( + int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) { + return new ContextHubServiceTransaction( + mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_ENABLE_NANOAPP) { + @Override + /* package */ int onTransact() { + try { + return mContextHubProxy.enableNanoApp( + contextHubId, nanoAppId, this.getTransactionId()); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while trying to enable nanoapp with ID 0x" + + Long.toHexString(nanoAppId), e); + return Result.UNKNOWN_FAILURE; + } + } + + @Override + /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) { + try { + onCompleteCallback.onTransactionComplete(result); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while calling client onTransactionComplete", e); + } + } + }; + } + + /** + * Creates a transaction for disabling a nanoapp. + * + * @param contextHubId the ID of the hub to disable the nanoapp on + * @param nanoAppId the ID of the nanoapp to disable + * @param onCompleteCallback the client on complete callback + * @return the generated transaction + */ + /* package */ ContextHubServiceTransaction createDisableTransaction( + int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) { + return new ContextHubServiceTransaction( + mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_DISABLE_NANOAPP) { + @Override + /* package */ int onTransact() { + try { + return mContextHubProxy.disableNanoApp( + contextHubId, nanoAppId, this.getTransactionId()); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while trying to disable nanoapp with ID 0x" + + Long.toHexString(nanoAppId), e); + return Result.UNKNOWN_FAILURE; + } + } + + @Override + /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) { + try { + onCompleteCallback.onTransactionComplete(result); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while calling client onTransactionComplete", e); + } + } + }; + } + + /** * Creates a transaction for querying for a list of nanoapps. * * @param contextHubId the ID of the hub to query diff --git a/services/core/java/com/android/server/location/OWNERS b/services/core/java/com/android/server/location/OWNERS index d3c0ea8caaf7..5f0369b5714d 100644 --- a/services/core/java/com/android/server/location/OWNERS +++ b/services/core/java/com/android/server/location/OWNERS @@ -1,4 +1,6 @@ ashutoshj@google.com bduddie@google.com +gomo@google.com +sooniln@google.com weiwa@google.com wyattriley@google.com diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java new file mode 100644 index 000000000000..e4d2b953b61a --- /dev/null +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locksettings.recoverablekeystore; + +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +/** + * Utility functions for the flow where the RecoverableKeyStoreLoader syncs keys with remote + * storage. + * + * @hide + */ +public class KeySyncUtils { + + private static final String RECOVERY_KEY_ALGORITHM = "AES"; + private static final int RECOVERY_KEY_SIZE_BITS = 256; + + private static final byte[] THM_ENCRYPTED_RECOVERY_KEY_HEADER = + "V1 THM_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8); + private static final byte[] LOCALLY_ENCRYPTED_RECOVERY_KEY_HEADER = + "V1 locally_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8); + private static final byte[] ENCRYPTED_APPLICATION_KEY_HEADER = + "V1 encrypted_application_key".getBytes(StandardCharsets.UTF_8); + + private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8); + + /** + * Encrypts the recovery key using both the lock screen hash and the remote storage's public + * key. + * + * @param publicKey The public key of the remote storage. + * @param lockScreenHash The user's lock screen hash. + * @param vaultParams Additional parameters to send to the remote storage. + * @param recoveryKey The recovery key. + * @return The encrypted bytes. + * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable. + * @throws InvalidKeyException if the public key or the lock screen could not be used to encrypt + * the data. + * + * @hide + */ + public byte[] thmEncryptRecoveryKey( + PublicKey publicKey, + byte[] lockScreenHash, + byte[] vaultParams, + SecretKey recoveryKey + ) throws NoSuchAlgorithmException, InvalidKeyException { + byte[] encryptedRecoveryKey = locallyEncryptRecoveryKey(lockScreenHash, recoveryKey); + byte[] thmKfHash = calculateThmKfHash(lockScreenHash); + byte[] header = concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams); + return SecureBox.encrypt( + /*theirPublicKey=*/ publicKey, + /*sharedSecret=*/ thmKfHash, + /*header=*/ header, + /*payload=*/ encryptedRecoveryKey); + } + + /** + * Calculates the THM_KF hash of the lock screen hash. + * + * @param lockScreenHash The lock screen hash. + * @return The hash. + * @throws NoSuchAlgorithmException if SHA-256 is unavailable (should never happen). + * + * @hide + */ + public static byte[] calculateThmKfHash(byte[] lockScreenHash) + throws NoSuchAlgorithmException { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(THM_KF_HASH_PREFIX); + messageDigest.update(lockScreenHash); + return messageDigest.digest(); + } + + /** + * Encrypts the recovery key using the lock screen hash. + * + * @param lockScreenHash The raw lock screen hash. + * @param recoveryKey The recovery key. + * @return The encrypted bytes. + * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable. + * @throws InvalidKeyException if the hash cannot be used to encrypt for some reason. + */ + private static byte[] locallyEncryptRecoveryKey(byte[] lockScreenHash, SecretKey recoveryKey) + throws NoSuchAlgorithmException, InvalidKeyException { + return SecureBox.encrypt( + /*theirPublicKey=*/ null, + /*sharedSecret=*/ lockScreenHash, + /*header=*/ LOCALLY_ENCRYPTED_RECOVERY_KEY_HEADER, + /*payload=*/ recoveryKey.getEncoded()); + } + + /** + * Returns a new random 256-bit AES recovery key. + * + * @hide + */ + public static SecretKey generateRecoveryKey() throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance(RECOVERY_KEY_ALGORITHM); + keyGenerator.init(RECOVERY_KEY_SIZE_BITS, SecureRandom.getInstanceStrong()); + return keyGenerator.generateKey(); + } + + /** + * Encrypts all of the given keys with the recovery key, using SecureBox. + * + * @param recoveryKey The recovery key. + * @param keys The keys, indexed by their aliases. + * @return The encrypted key material, indexed by aliases. + * @throws NoSuchAlgorithmException if any of the SecureBox algorithms are unavailable. + * @throws InvalidKeyException if the recovery key is not appropriate for encrypting the keys. + * + * @hide + */ + public static Map<String, byte[]> encryptKeysWithRecoveryKey( + SecretKey recoveryKey, Map<String, SecretKey> keys) + throws NoSuchAlgorithmException, InvalidKeyException { + HashMap<String, byte[]> encryptedKeys = new HashMap<>(); + for (String alias : keys.keySet()) { + SecretKey key = keys.get(alias); + byte[] encryptedKey = SecureBox.encrypt( + /*theirPublicKey=*/ null, + /*sharedSecret=*/ recoveryKey.getEncoded(), + /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER, + /*payload=*/ key.getEncoded()); + encryptedKeys.put(alias, encryptedKey); + } + return encryptedKeys; + } + + /** + * Returns a new array, the contents of which are the concatenation of {@code a} and {@code b}. + */ + private static byte[] concat(byte[] a, byte[] b) { + byte[] result = new byte[a.length + b.length]; + System.arraycopy(a, 0, result, 0, a.length); + System.arraycopy(b, 0, result, a.length, b.length); + return result; + } + + // Statics only + private KeySyncUtils() {} +} diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java index cd85a97c3522..6a189efcdb57 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java @@ -68,4 +68,13 @@ public interface RecoverableKeyStorage { SecretKey loadFromAndroidKeyStore(String alias) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException; + + /** + * Removes the entry with the given {@code alias} from AndroidKeyStore. + * + * @throws KeyStoreException if an error occurred deleting the key from AndroidKeyStore. + * + * @hide + */ + void removeFromAndroidKeyStore(String alias) throws KeyStoreException; } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java index 4e0751b2ad95..d4dede165177 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java @@ -16,44 +16,40 @@ package com.android.server.locksettings.recoverablekeystore; +import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyProtection; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.UnrecoverableEntryException; -import java.security.cert.CertificateException; import javax.crypto.SecretKey; /** - * Implementation of {@link RecoverableKeyStorage}. + * Implementation of {@link RecoverableKeyStorage} for a specific application. * * <p>Persists wrapped keys to disk, and loads raw keys into AndroidKeyStore. * * @hide */ public class RecoverableKeyStorageImpl implements RecoverableKeyStorage { - private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; - private final KeyStore mKeyStore; /** - * A new instance. + * A new instance, storing recoverable keys for the given {@code userId}. * * @throws KeyStoreException if unable to load AndroidKeyStore. + * @throws NoSuchProviderException if AndroidKeyStore is not in this version of Android. Should + * never occur. * * @hide */ - public static RecoverableKeyStorageImpl newInstance() throws KeyStoreException { - KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); - try { - keyStore.load(/*param=*/ null); - } catch (CertificateException | IOException | NoSuchAlgorithmException e) { - // Should never happen. - throw new KeyStoreException("Unable to load keystore.", e); - } + public static RecoverableKeyStorageImpl newInstance(int userId) throws KeyStoreException, + NoSuchProviderException { + KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(userId); return new RecoverableKeyStorageImpl(keyStore); } @@ -75,8 +71,7 @@ public class RecoverableKeyStorageImpl implements RecoverableKeyStorage { } /** - * Imports {@code key} into AndroidKeyStore, keyed by the application's uid and the - * {@code alias}. + * Imports {@code key} into the application's AndroidKeyStore, keyed by {@code alias}. * * @param alias The alias of the key. * @param key The key. @@ -93,7 +88,7 @@ public class RecoverableKeyStorageImpl implements RecoverableKeyStorage { } /** - * Loads a key handle from AndroidKeyStore. + * Loads a key handle from the application's AndroidKeyStore. * * @param alias Alias of the key to load. * @return The key handle. @@ -104,7 +99,18 @@ public class RecoverableKeyStorageImpl implements RecoverableKeyStorage { @Override public SecretKey loadFromAndroidKeyStore(String alias) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { - return ((KeyStore.SecretKeyEntry) mKeyStore.getEntry(alias, /*protParam=*/ null)) - .getSecretKey(); + return ((SecretKey) mKeyStore.getKey(alias, /*password=*/ null)); + } + + /** + * Removes the entry with the given {@code alias} from the application's AndroidKeyStore. + * + * @throws KeyStoreException if an error occurred deleting the key from AndroidKeyStore. + * + * @hide + */ + @Override + public void removeFromAndroidKeyStore(String alias) throws KeyStoreException { + mKeyStore.deleteEntry(alias); } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java new file mode 100644 index 000000000000..457fdc14c7f6 --- /dev/null +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locksettings.recoverablekeystore; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; + +/** + * TODO(b/69056040) Add implementation of SecureBox. This is a placeholder so KeySyncUtils compiles. + * + * @hide + */ +public class SecureBox { + /** + * TODO(b/69056040) Add implementation of encrypt. + * + * @hide + */ + public static byte[] encrypt( + PublicKey theirPublicKey, byte[] sharedSecret, byte[] header, byte[] payload) + throws NoSuchAlgorithmException, InvalidKeyException { + throw new UnsupportedOperationException("Needs to be implemented."); + } +} diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java index c97c66ef9016..9002292174ba 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java @@ -16,14 +16,21 @@ package com.android.server.locksettings.recoverablekeystore; +import android.util.Log; + +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; /** * A {@link javax.crypto.SecretKey} wrapped with AES/GCM/NoPadding. @@ -31,7 +38,11 @@ import javax.crypto.SecretKey; * @hide */ public class WrappedKey { + private static final String TAG = "WrappedKey"; + private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding"; + private static final String APPLICATION_KEY_ALGORITHM = "AES"; + private static final int GCM_TAG_LENGTH_BITS = 128; private final byte[] mNonce; private final byte[] mKeyMaterial; @@ -112,4 +123,54 @@ public class WrappedKey { public byte[] getKeyMaterial() { return mKeyMaterial; } + + /** + * Unwraps the {@code wrappedKeys} with the {@code platformKey}. + * + * @return The unwrapped keys, indexed by alias. + * @throws NoSuchAlgorithmException if AES/GCM/NoPadding Cipher or AES key type is unavailable. + * + * @hide + */ + public static Map<String, SecretKey> unwrapKeys( + SecretKey platformKey, + Map<String, WrappedKey> wrappedKeys) + throws NoSuchAlgorithmException, NoSuchPaddingException { + HashMap<String, SecretKey> unwrappedKeys = new HashMap<>(); + Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM); + + for (String alias : wrappedKeys.keySet()) { + WrappedKey wrappedKey = wrappedKeys.get(alias); + try { + cipher.init( + Cipher.UNWRAP_MODE, + platformKey, + new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce())); + } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { + Log.e(TAG, + String.format( + Locale.US, + "Could not init Cipher to unwrap recoverable key with alias '%s'", + alias), + e); + continue; + } + SecretKey key; + try { + key = (SecretKey) cipher.unwrap( + wrappedKey.getKeyMaterial(), APPLICATION_KEY_ALGORITHM, Cipher.SECRET_KEY); + } catch (InvalidKeyException | NoSuchAlgorithmException e) { + Log.e(TAG, + String.format( + Locale.US, + "Error unwrapping recoverable key with alias '%s'", + alias), + e); + continue; + } + unwrappedKeys.put(alias, key); + } + + return unwrappedKeys; + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f106391f5916..8f6d88b9f870 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -650,18 +650,15 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mInstallLock") final Installer mInstaller; - /** Directory where installed third-party apps stored */ - final File mAppInstallDir; - - /** - * Directory to which applications installed internally have their - * 32 bit native libraries copied. - */ - private File mAppLib32InstallDir; - - // Directory containing the private parts (e.g. code and non-resource assets) of forward-locked - // apps. - final File mDrmAppPrivateInstallDir; + /** Directory where installed applications are stored */ + private static final File sAppInstallDir = + new File(Environment.getDataDirectory(), "app"); + /** Directory where installed application's 32-bit native libraries are copied. */ + private static final File sAppLib32InstallDir = + new File(Environment.getDataDirectory(), "app-lib"); + /** Directory where code and non-resource assets of forward-locked applications are stored */ + private static final File sDrmAppPrivateInstallDir = + new File(Environment.getDataDirectory(), "app-private"); // ---------------------------------------------------------------- @@ -742,9 +739,6 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mAvailableFeatures") final ArrayMap<String, FeatureInfo> mAvailableFeatures; - // If mac_permissions.xml was found for seinfo labeling. - boolean mFoundPolicyFile; - private final InstantAppRegistry mInstantAppRegistry; @GuardedBy("mPackages") @@ -2445,11 +2439,6 @@ public class PackageManagerService extends IPackageManager.Stub Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); mInstantAppRegistry = new InstantAppRegistry(this); - File dataDir = Environment.getDataDirectory(); - mAppInstallDir = new File(dataDir, "app"); - mAppLib32InstallDir = new File(dataDir, "app-lib"); - mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); - ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries(); final int builtInLibCount = libConfig.size(); for (int i = 0; i < builtInLibCount; i++) { @@ -2459,7 +2448,7 @@ public class PackageManagerService extends IPackageManager.Stub SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0); } - mFoundPolicyFile = SELinuxMMAC.readInstallPolicy(); + SELinuxMMAC.readInstallPolicy(); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings"); mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false)); @@ -2737,9 +2726,9 @@ public class PackageManagerService extends IPackageManager.Stub if (!mOnlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); - scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0); + scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0); - scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags + scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags | PackageParser.PARSE_FORWARD_LOCK, scanFlags | SCAN_REQUIRE_KNOWN, 0); @@ -2908,7 +2897,18 @@ public class PackageManagerService extends IPackageManager.Stub // NOTE: We ignore potential failures here during a system scan (like // the rest of the commands above) because there's precious little we // can do about it. A settings error is reported, though. - adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/); + final List<String> changedAbiCodePath = + adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/); + if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { + for (int i = changedAbiCodePath.size() - 1; i <= 0; --i) { + final String codePathString = changedAbiCodePath.get(i); + try { + mInstaller.rmdex(codePathString, + getDexCodeInstructionSet(getPreferredInstructionSet())); + } catch (InstallerException ignored) { + } + } + } } // Now that we know all the packages we are keeping, @@ -8134,6 +8134,7 @@ public class PackageManagerService extends IPackageManager.Stub int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { + // TODO(toddke): move lower in the scan chain // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); @@ -8486,7 +8487,7 @@ public class PackageManagerService extends IPackageManager.Stub // Set CPU Abis to application info. if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) { final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPs); - derivePackageAbi(pkg, cpuAbiOverride, false, mAppLib32InstallDir); + derivePackageAbi(pkg, cpuAbiOverride, false); } else { pkg.applicationInfo.primaryCpuAbi = updatedPs.primaryCpuAbiString; pkg.applicationInfo.secondaryCpuAbi = updatedPs.secondaryCpuAbiString; @@ -8594,7 +8595,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Note that we invoke the following method only if we are about to unpack an application - PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags + PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); /* @@ -9615,12 +9616,12 @@ public class PackageManagerService extends IPackageManager.Stub final PackageParser.Package scannedPkg; try { // Scan the parent - scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user); + scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); - scanPackageLI(childPkg, parseFlags, + scanPackageNewLI(childPkg, parseFlags, scanFlags, currentTime, user); } } finally { @@ -9634,89 +9635,423 @@ public class PackageManagerService extends IPackageManager.Stub return scannedPkg; } - private PackageParser.Package scanPackageLI(PackageParser.Package pkg, + /** The result of a package scan. */ + private static class ScanResult { + /** Whether or not the package scan was successful */ + public final boolean success; + /** + * The final package settings. This may be the same object passed in + * the {@link ScanRequest}, but, with modified values. + */ + @Nullable public final PackageSetting pkgSetting; + /** ABI code paths that have changed in the package scan */ + @Nullable public final List<String> changedAbiCodePath; + public ScanResult( + boolean success, + @Nullable PackageSetting pkgSetting, + @Nullable List<String> changedAbiCodePath) { + this.success = success; + this.pkgSetting = pkgSetting; + this.changedAbiCodePath = changedAbiCodePath; + } + } + + /** A package to be scanned */ + private static class ScanRequest { + /** The parsed package */ + @NonNull public final PackageParser.Package pkg; + /** Shared user settings, if the package has a shared user */ + @Nullable public final SharedUserSetting sharedUserSetting; + /** + * Package settings of the currently installed version. + * <p><em>IMPORTANT:</em> The contents of this object may be modified + * during scan. + */ + @Nullable public final PackageSetting pkgSetting; + /** A copy of the settings for the currently installed version */ + @Nullable public final PackageSetting oldPkgSetting; + /** Package settings for the disabled version on the /system partition */ + @Nullable public final PackageSetting disabledPkgSetting; + /** Package settings for the installed version under its original package name */ + @Nullable public final PackageSetting originalPkgSetting; + /** The real package name of a renamed application */ + @Nullable public final String realPkgName; + public final @ParseFlags int parseFlags; + public final @ScanFlags int scanFlags; + /** The user for which the package is being scanned */ + @Nullable public final UserHandle user; + /** Whether or not the platform package is being scanned */ + public final boolean isPlatformPackage; + public ScanRequest( + @NonNull PackageParser.Package pkg, + @Nullable SharedUserSetting sharedUserSetting, + @Nullable PackageSetting pkgSetting, + @Nullable PackageSetting disabledPkgSetting, + @Nullable PackageSetting originalPkgSetting, + @Nullable String realPkgName, + @ParseFlags int parseFlags, + @ScanFlags int scanFlags, + boolean isPlatformPackage, + @Nullable UserHandle user) { + this.pkg = pkg; + this.pkgSetting = pkgSetting; + this.sharedUserSetting = sharedUserSetting; + this.oldPkgSetting = pkgSetting == null ? null : new PackageSetting(pkgSetting); + this.disabledPkgSetting = disabledPkgSetting; + this.originalPkgSetting = originalPkgSetting; + this.realPkgName = realPkgName; + this.parseFlags = parseFlags; + this.scanFlags = scanFlags; + this.isPlatformPackage = isPlatformPackage; + this.user = user; + } + } + + @GuardedBy("mInstallLock") + private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg, final @ParseFlags int parseFlags, final @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { - boolean success = false; - try { - final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags, - currentTime, user); - success = true; - return res; - } finally { - if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) { - // DELETE_DATA_ON_FAILURES is only used by frozen paths - destroyAppDataLIF(pkg, UserHandle.USER_ALL, - StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); - destroyAppProfilesLIF(pkg, UserHandle.USER_ALL); + + final String renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); + final String realPkgName = getRealPackageName(pkg, renamedPkgName); + if (realPkgName != null) { + ensurePackageRenamed(pkg, renamedPkgName); + } + final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); + final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.packageName); + final PackageSetting disabledPkgSetting = + mSettings.getDisabledSystemPkgLPr(pkg.packageName); + + if (mTransferedPackages.contains(pkg.packageName)) { + Slog.w(TAG, "Package " + pkg.packageName + + " was transferred to another, but its .apk remains"); + } + + synchronized (mPackages) { + applyPolicy(pkg, parseFlags, scanFlags); + assertPackageIsValid(pkg, parseFlags, scanFlags); + + SharedUserSetting sharedUserSetting = null; + if (pkg.mSharedUserId != null) { + // SIDE EFFECTS; may potentially allocate a new shared user + sharedUserSetting = mSettings.getSharedUserLPw( + pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/); + if (DEBUG_PACKAGE_SCANNING) { + if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) + Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + + " (uid=" + sharedUserSetting.userId + "):" + + " packages=" + sharedUserSetting.packages); + } + } + + boolean scanSucceeded = false; + try { + final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, pkgSetting, + disabledPkgSetting, originalPkgSetting, realPkgName, parseFlags, scanFlags, + (pkg == mPlatformPackage), user); + final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime); + if (result.success) { + commitScanResultsLocked(request, result); + } + scanSucceeded = true; + } finally { + if (!scanSucceeded && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) { + // DELETE_DATA_ON_FAILURES is only used by frozen paths + destroyAppDataLIF(pkg, UserHandle.USER_ALL, + StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); + destroyAppProfilesLIF(pkg, UserHandle.USER_ALL); + } } } + return pkg; } /** - * Returns {@code true} if the given file contains code. Otherwise {@code false}. + * Commits the package scan and modifies system state. + * <p><em>WARNING:</em> The method may throw an excpetion in the middle + * of committing the package, leaving the system in an inconsistent state. + * This needs to be fixed so, once we get to this point, no errors are + * possible and the system is not left in an inconsistent state. */ - private static boolean apkHasCode(String fileName) { - StrictJarFile jarFile = null; - try { - jarFile = new StrictJarFile(fileName, - false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); - return jarFile.findEntry("classes.dex") != null; - } catch (IOException ignore) { - } finally { + @GuardedBy("mPackages") + private void commitScanResultsLocked(@NonNull ScanRequest request, @NonNull ScanResult result) + throws PackageManagerException { + final PackageParser.Package pkg = request.pkg; + final @ParseFlags int parseFlags = request.parseFlags; + final @ScanFlags int scanFlags = request.scanFlags; + final PackageSetting oldPkgSetting = request.oldPkgSetting; + final PackageSetting originalPkgSetting = request.originalPkgSetting; + final UserHandle user = request.user; + final String realPkgName = request.realPkgName; + final PackageSetting pkgSetting = result.pkgSetting; + final List<String> changedAbiCodePath = result.changedAbiCodePath; + final boolean newPkgSettingCreated = (result.pkgSetting != request.pkgSetting); + + if (newPkgSettingCreated) { + if (originalPkgSetting != null) { + mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name); + } + // THROWS: when we can't allocate a user id. add call to check if there's + // enough space to ensure we won't throw; otherwise, don't modify state + mSettings.addUserToSettingLPw(pkgSetting); + + if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) { + mTransferedPackages.add(originalPkgSetting.name); + } + } + // TODO(toddke): Consider a method specifically for modifying the Package object + // post scan; or, moving this stuff out of the Package object since it has nothing + // to do with the package on disk. + // We need to have this here because addUserToSettingLPw() is sometimes responsible + // for creating the application ID. If we did this earlier, we would be saving the + // correct ID. + pkg.applicationInfo.uid = pkgSetting.appId; + + mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting); + + if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realPkgName != null) { + mTransferedPackages.add(pkg.packageName); + } + + // THROWS: when requested libraries that can't be found. it only changes + // the state of the passed in pkg object, so, move to the top of the method + // and allow it to abort + if ((scanFlags & SCAN_BOOTING) == 0 + && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { + // Check all shared libraries and map to their actual file path. + // We only do this here for apps not on a system dir, because those + // are the only ones that can fail an install due to this. We + // will take care of the system apps by updating all of their + // library paths after the scan is done. Also during the initial + // scan don't update any libs as we do this wholesale after all + // apps are scanned to avoid dependency based scanning. + updateSharedLibrariesLPr(pkg, null); + } + + // All versions of a static shared library are referenced with the same + // package name. Internally, we use a synthetic package name to allow + // multiple versions of the same shared library to be installed. So, + // we need to generate the synthetic package name of the latest shared + // library in order to compare signatures. + PackageSetting signatureCheckPs = pkgSetting; + if (pkg.applicationInfo.isStaticSharedLibrary()) { + SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg); + if (libraryEntry != null) { + signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk); + } + } + + final KeySetManagerService ksms = mSettings.mKeySetManagerService; + if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { + if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) { + // We just determined the app is signed correctly, so bring + // over the latest parsed certs. + pkgSetting.signatures.mSignatures = pkg.mSignatures; + } else { + if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { + throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "Package " + pkg.packageName + " upgrade keys do not match the " + + "previously installed version"); + } else { + pkgSetting.signatures.mSignatures = pkg.mSignatures; + String msg = "System package " + pkg.packageName + + " signature changed; retaining data."; + reportSettingsProblem(Log.WARN, msg); + } + } + } else { try { - if (jarFile != null) { - jarFile.close(); + final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg); + final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg); + final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures, + compareCompat, compareRecover); + // The new KeySets will be re-added later in the scanning process. + if (compatMatch) { + synchronized (mPackages) { + ksms.removeAppKeySetDataLPw(pkg.packageName); + } } - } catch (IOException ignore) {} + // We just determined the app is signed correctly, so bring + // over the latest parsed certs. + pkgSetting.signatures.mSignatures = pkg.mSignatures; + } catch (PackageManagerException e) { + if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { + throw e; + } + // The signature has changed, but this package is in the system + // image... let's recover! + pkgSetting.signatures.mSignatures = pkg.mSignatures; + // However... if this package is part of a shared user, but it + // doesn't match the signature of the shared user, let's fail. + // What this means is that you can't change the signatures + // associated with an overall shared user, which doesn't seem all + // that unreasonable. + if (signatureCheckPs.sharedUser != null) { + if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures, + pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { + throw new PackageManagerException( + INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, + "Signature mismatch for shared user: " + + pkgSetting.sharedUser); + } + } + // File a report about this. + String msg = "System package " + pkg.packageName + + " signature changed; retaining data."; + reportSettingsProblem(Log.WARN, msg); + } + } + + if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) { + // This package wants to adopt ownership of permissions from + // another package. + for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) { + final String origName = pkg.mAdoptPermissions.get(i); + final PackageSetting orig = mSettings.getPackageLPr(origName); + if (orig != null) { + if (verifyPackageUpdateLPr(orig, pkg)) { + Slog.i(TAG, "Adopting permissions from " + origName + " to " + + pkg.packageName); + mSettings.mPermissions.transferPermissions(origName, pkg.packageName); + } + } + } + } + + if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { + for (int i = changedAbiCodePath.size() - 1; i <= 0; --i) { + final String codePathString = changedAbiCodePath.get(i); + try { + mInstaller.rmdex(codePathString, + getDexCodeInstructionSet(getPreferredInstructionSet())); + } catch (InstallerException ignored) { + } + } + } + + if ((scanFlags & SCAN_CHECK_ONLY) != 0) { + if (oldPkgSetting != null) { + synchronized (mPackages) { + mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting); + } + } + } else { + final int userId = user == null ? 0 : user.getIdentifier(); + // Modify state for the given package setting + commitPackageSettings(pkg, pkgSetting, user, scanFlags, + (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/); + if (pkgSetting.getInstantApp(userId)) { + mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId); + } } - return false; } /** - * Enforces code policy for the package. This ensures that if an APK has - * declared hasCode="true" in its manifest that the APK actually contains - * code. - * - * @throws PackageManagerException If bytecode could not be found when it should exist + * Returns the "real" name of the package. + * <p>This may differ from the package's actual name if the application has already + * been installed under one of this package's original names. */ - private static void assertCodePolicy(PackageParser.Package pkg) - throws PackageManagerException { - final boolean shouldHaveCode = - (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0; - if (shouldHaveCode && !apkHasCode(pkg.baseCodePath)) { - throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, - "Package " + pkg.baseCodePath + " code is missing"); + private static @Nullable String getRealPackageName(@NonNull PackageParser.Package pkg, + @Nullable String renamedPkgName) { + if (pkg.mOriginalPackages == null || !pkg.mOriginalPackages.contains(renamedPkgName)) { + return null; } + return pkg.mRealPackage; + } - if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { - for (int i = 0; i < pkg.splitCodePaths.length; i++) { - final boolean splitShouldHaveCode = - (pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0; - if (splitShouldHaveCode && !apkHasCode(pkg.splitCodePaths[i])) { - throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, - "Package " + pkg.splitCodePaths[i] + " code is missing"); + /** + * Returns the original package setting. + * <p>A package can migrate its name during an update. In this scenario, a package + * designates a set of names that it considers as one of its original names. + * <p>An original package must be signed identically and it must have the same + * shared user [if any]. + */ + @GuardedBy("mPackages") + private @Nullable PackageSetting getOriginalPackageLocked(@NonNull PackageParser.Package pkg, + @Nullable String renamedPkgName) { + if (pkg.mOriginalPackages == null || pkg.mOriginalPackages.contains(renamedPkgName)) { + return null; + } + for (int i = pkg.mOriginalPackages.size() - 1; i >= 0; --i) { + final PackageSetting originalPs = + mSettings.getPackageLPr(pkg.mOriginalPackages.get(i)); + if (originalPs != null) { + // the package is already installed under its original name... + // but, should we use it? + if (!verifyPackageUpdateLPr(originalPs, pkg)) { + // the new package is incompatible with the original + continue; + } else if (originalPs.sharedUser != null) { + if (!originalPs.sharedUser.name.equals(pkg.mSharedUserId)) { + // the shared user id is incompatible with the original + Slog.w(TAG, "Unable to migrate data from " + originalPs.name + + " to " + pkg.packageName + ": old uid " + + originalPs.sharedUser.name + + " differs from " + pkg.mSharedUserId); + continue; + } + // TODO: Add case when shared user id is added [b/28144775] + } else { + if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package " + + pkg.packageName + " to old name " + originalPs.name); } + return originalPs; } } + return null; } - private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, - final @ParseFlags int parseFlags, final @ScanFlags int scanFlags, long currentTime, - @Nullable UserHandle user) + /** + * Renames the package if it was installed under a different name. + * <p>When we've already installed the package under an original name, update + * the new package so we can continue to have the old name. + */ + private static void ensurePackageRenamed(@NonNull PackageParser.Package pkg, + @NonNull String renamedPackageName) { + if (pkg.mOriginalPackages == null + || !pkg.mOriginalPackages.contains(renamedPackageName) + || pkg.packageName.equals(renamedPackageName)) { + return; + } + pkg.setPackageName(renamedPackageName); + } + + /** + * Just scans the package without any side effects. + * <p>Not entirely true at the moment. There is still one side effect -- this + * method potentially modifies a live {@link PackageSetting} object representing + * the package being scanned. This will be resolved in the future. + * + * @param request Information about the package to be scanned + * @param isUnderFactoryTest Whether or not the device is under factory test + * @param currentTime The current time, in millis + * @return The results of the scan + */ + @GuardedBy("mInstallLock") + private static @NonNull ScanResult scanPackageOnlyLI(@NonNull ScanRequest request, + boolean isUnderFactoryTest, long currentTime) throws PackageManagerException { + final PackageParser.Package pkg = request.pkg; + PackageSetting pkgSetting = request.pkgSetting; + final PackageSetting disabledPkgSetting = request.disabledPkgSetting; + final PackageSetting originalPkgSetting = request.originalPkgSetting; + final @ParseFlags int parseFlags = request.parseFlags; + final @ScanFlags int scanFlags = request.scanFlags; + final String realPkgName = request.realPkgName; + final SharedUserSetting sharedUserSetting = request.sharedUserSetting; + final UserHandle user = request.user; + final boolean isPlatformPackage = request.isPlatformPackage; + + List<String> changedAbiCodePath = null; + if (DEBUG_PACKAGE_SCANNING) { if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) Log.d(TAG, "Scanning package " + pkg.packageName); } - applyPolicy(pkg, parseFlags, scanFlags); - - assertPackageIsValid(pkg, parseFlags, scanFlags); - if (Build.IS_DEBUGGABLE && pkg.isPrivileged() && - SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) { + !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) { PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg); } @@ -9725,312 +10060,93 @@ public class PackageManagerService extends IPackageManager.Stub final File destCodeFile = new File(pkg.applicationInfo.getCodePath()); final File destResourceFile = new File(pkg.applicationInfo.getResourcePath()); - SharedUserSetting suid = null; - PackageSetting pkgSetting = null; - - // Getting the package setting may have a side-effect, so if we - // are only checking if scan would succeed, stash a copy of the - // old setting to restore at the end. - PackageSetting nonMutatedPs = null; - // We keep references to the derived CPU Abis from settings in oder to reuse // them in the case where we're not upgrading or booting for the first time. String primaryCpuAbiFromSettings = null; String secondaryCpuAbiFromSettings = null; boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0; - // writer - synchronized (mPackages) { - if (pkg.mSharedUserId != null) { - // SIDE EFFECTS; may potentially allocate a new shared user - suid = mSettings.getSharedUserLPw( - pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/); - if (DEBUG_PACKAGE_SCANNING) { - if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) - Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId - + "): packages=" + suid.packages); - } - } - - // Check if we are renaming from an original package name. - PackageSetting origPackage = null; - String realName = null; - if (pkg.mOriginalPackages != null) { - // This package may need to be renamed to a previously - // installed name. Let's check on that... - final String renamed = mSettings.getRenamedPackageLPr(pkg.mRealPackage); - if (pkg.mOriginalPackages.contains(renamed)) { - // This package had originally been installed as the - // original name, and we have already taken care of - // transitioning to the new one. Just update the new - // one to continue using the old name. - realName = pkg.mRealPackage; - if (!pkg.packageName.equals(renamed)) { - // Callers into this function may have already taken - // care of renaming the package; only do it here if - // it is not already done. - pkg.setPackageName(renamed); - } - } else { - for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) { - if ((origPackage = mSettings.getPackageLPr( - pkg.mOriginalPackages.get(i))) != null) { - // We do have the package already installed under its - // original name... should we use it? - if (!verifyPackageUpdateLPr(origPackage, pkg)) { - // New package is not compatible with original. - origPackage = null; - continue; - } else if (origPackage.sharedUser != null) { - // Make sure uid is compatible between packages. - if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) { - Slog.w(TAG, "Unable to migrate data from " + origPackage.name - + " to " + pkg.packageName + ": old uid " - + origPackage.sharedUser.name - + " differs from " + pkg.mSharedUserId); - origPackage = null; - continue; - } - // TODO: Add case when shared user id is added [b/28144775] - } else { - if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package " - + pkg.packageName + " to old name " + origPackage.name); - } - break; - } - } - } - } - - if (mTransferedPackages.contains(pkg.packageName)) { - Slog.w(TAG, "Package " + pkg.packageName - + " was transferred to another, but its .apk remains"); - } - - // See comments in nonMutatedPs declaration - if ((scanFlags & SCAN_CHECK_ONLY) != 0) { - PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName); - if (foundPs != null) { - nonMutatedPs = new PackageSetting(foundPs); - } - } - - if (!needToDeriveAbi) { - PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName); - if (foundPs != null) { - primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString; - secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString; - } else { - // when re-adding a system package failed after uninstalling updates. - needToDeriveAbi = true; - } - } - - pkgSetting = mSettings.getPackageLPr(pkg.packageName); - if (pkgSetting != null && pkgSetting.sharedUser != suid) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Package " + pkg.packageName + " shared user changed from " - + (pkgSetting.sharedUser != null - ? pkgSetting.sharedUser.name : "<nothing>") - + " to " - + (suid != null ? suid.name : "<nothing>") - + "; replacing with new"); - pkgSetting = null; - } - final PackageSetting oldPkgSetting = - pkgSetting == null ? null : new PackageSetting(pkgSetting); - final PackageSetting disabledPkgSetting = - mSettings.getDisabledSystemPkgLPr(pkg.packageName); - - String[] usesStaticLibraries = null; - if (pkg.usesStaticLibraries != null) { - usesStaticLibraries = new String[pkg.usesStaticLibraries.size()]; - pkg.usesStaticLibraries.toArray(usesStaticLibraries); - } - - if (pkgSetting == null) { - final String parentPackageName = (pkg.parentPackage != null) - ? pkg.parentPackage.packageName : null; - final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; - final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0; - // REMOVE SharedUserSetting from method; update in a separate call - pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage, - disabledPkgSetting, realName, suid, destCodeFile, destResourceFile, - pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi, - pkg.applicationInfo.secondaryCpuAbi, pkg.getLongVersionCode(), - pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user, - true /*allowInstall*/, instantApp, virtualPreload, - parentPackageName, pkg.getChildPackageNames(), - UserManagerService.getInstance(), usesStaticLibraries, - pkg.usesStaticLibrariesVersions); - // SIDE EFFECTS; updates system state; move elsewhere - if (origPackage != null) { - mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name); - } - mSettings.addUserToSettingLPw(pkgSetting); + if (!needToDeriveAbi) { + if (pkgSetting != null) { + primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString; + secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString; } else { - // REMOVE SharedUserSetting from method; update in a separate call. - // - // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi, - // secondaryCpuAbi are not known at this point so we always update them - // to null here, only to reset them at a later point. - Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, suid, destCodeFile, - pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.primaryCpuAbi, - pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags, - pkg.applicationInfo.privateFlags, pkg.getChildPackageNames(), - UserManagerService.getInstance(), usesStaticLibraries, - pkg.usesStaticLibrariesVersions); - } - // SIDE EFFECTS; persists system state to files on disk; move elsewhere - mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting); - - // SIDE EFFECTS; modifies system state; move elsewhere - if (pkgSetting.origPackage != null) { - // If we are first transitioning from an original package, - // fix up the new package's name now. We need to do this after - // looking up the package under its new name, so getPackageLP - // can take care of fiddling things correctly. - pkg.setPackageName(origPackage.name); - - // File a report about this. - String msg = "New package " + pkgSetting.realName - + " renamed to replace old package " + pkgSetting.name; - reportSettingsProblem(Log.WARN, msg); - - // Make a note of it. - if ((scanFlags & SCAN_CHECK_ONLY) == 0) { - mTransferedPackages.add(origPackage.name); - } - - // No longer need to retain this. - pkgSetting.origPackage = null; - } - - // SIDE EFFECTS; modifies system state; move elsewhere - if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realName != null) { - // Make a note of it. - mTransferedPackages.add(pkg.packageName); + // Re-scanning a system package after uninstalling updates; need to derive ABI + needToDeriveAbi = true; } + } - if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) { - pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - } - - if ((scanFlags & SCAN_BOOTING) == 0 - && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { - // Check all shared libraries and map to their actual file path. - // We only do this here for apps not on a system dir, because those - // are the only ones that can fail an install due to this. We - // will take care of the system apps by updating all of their - // library paths after the scan is done. Also during the initial - // scan don't update any libs as we do this wholesale after all - // apps are scanned to avoid dependency based scanning. - updateSharedLibrariesLPr(pkg, null); - } - - if (mFoundPolicyFile) { - SELinuxMMAC.assignSeInfoValue(pkg); - } - pkg.applicationInfo.uid = pkgSetting.appId; - pkg.mExtras = pkgSetting; - + if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Package " + pkg.packageName + " shared user changed from " + + (pkgSetting.sharedUser != null + ? pkgSetting.sharedUser.name : "<nothing>") + + " to " + + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>") + + "; replacing with new"); + pkgSetting = null; + } - // Static shared libs have same package with different versions where - // we internally use a synthetic package name to allow multiple versions - // of the same package, therefore we need to compare signatures against - // the package setting for the latest library version. - PackageSetting signatureCheckPs = pkgSetting; - if (pkg.applicationInfo.isStaticSharedLibrary()) { - SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg); - if (libraryEntry != null) { - signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk); - } - } + String[] usesStaticLibraries = null; + if (pkg.usesStaticLibraries != null) { + usesStaticLibraries = new String[pkg.usesStaticLibraries.size()]; + pkg.usesStaticLibraries.toArray(usesStaticLibraries); + } + + final boolean createNewPackage = (pkgSetting == null); + if (createNewPackage) { + final String parentPackageName = (pkg.parentPackage != null) + ? pkg.parentPackage.packageName : null; + final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; + final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0; + // REMOVE SharedUserSetting from method; update in a separate call + pkgSetting = Settings.createNewSetting(pkg.packageName, originalPkgSetting, + disabledPkgSetting, realPkgName, sharedUserSetting, destCodeFile, + destResourceFile, pkg.applicationInfo.nativeLibraryRootDir, + pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi, + pkg.mVersionCode, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, + user, true /*allowInstall*/, instantApp, virtualPreload, + parentPackageName, pkg.getChildPackageNames(), + UserManagerService.getInstance(), usesStaticLibraries, + pkg.usesStaticLibrariesVersions); + } else { + // REMOVE SharedUserSetting from method; update in a separate call. + // + // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi, + // secondaryCpuAbi are not known at this point so we always update them + // to null here, only to reset them at a later point. + Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting, + destCodeFile, pkg.applicationInfo.nativeLibraryDir, + pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi, + pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, + pkg.getChildPackageNames(), UserManagerService.getInstance(), + usesStaticLibraries, pkg.usesStaticLibrariesVersions); + } + if (createNewPackage && originalPkgSetting != null) { + // If we are first transitioning from an original package, + // fix up the new package's name now. We need to do this after + // looking up the package under its new name, so getPackageLP + // can take care of fiddling things correctly. + pkg.setPackageName(originalPkgSetting.name); - final KeySetManagerService ksms = mSettings.mKeySetManagerService; - if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { - if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) { - // We just determined the app is signed correctly, so bring - // over the latest parsed certs. - pkgSetting.signatures.mSignatures = pkg.mSignatures; - } else { - if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { - throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "Package " + pkg.packageName + " upgrade keys do not match the " - + "previously installed version"); - } else { - pkgSetting.signatures.mSignatures = pkg.mSignatures; - String msg = "System package " + pkg.packageName - + " signature changed; retaining data."; - reportSettingsProblem(Log.WARN, msg); - } - } - } else { - try { - final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg); - final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg); - final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures, - compareCompat, compareRecover); - // The new KeySets will be re-added later in the scanning process. - if (compatMatch) { - synchronized (mPackages) { - ksms.removeAppKeySetDataLPw(pkg.packageName); - } - } - // We just determined the app is signed correctly, so bring - // over the latest parsed certs. - pkgSetting.signatures.mSignatures = pkg.mSignatures; - } catch (PackageManagerException e) { - if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { - throw e; - } - // The signature has changed, but this package is in the system - // image... let's recover! - pkgSetting.signatures.mSignatures = pkg.mSignatures; - // However... if this package is part of a shared user, but it - // doesn't match the signature of the shared user, let's fail. - // What this means is that you can't change the signatures - // associated with an overall shared user, which doesn't seem all - // that unreasonable. - if (signatureCheckPs.sharedUser != null) { - if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures, - pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { - throw new PackageManagerException( - INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, - "Signature mismatch for shared user: " - + pkgSetting.sharedUser); - } - } - // File a report about this. - String msg = "System package " + pkg.packageName - + " signature changed; retaining data."; - reportSettingsProblem(Log.WARN, msg); - } - } + // File a report about this. + String msg = "New package " + pkgSetting.realName + + " renamed to replace old package " + pkgSetting.name; + reportSettingsProblem(Log.WARN, msg); + } - if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) { - // This package wants to adopt ownership of permissions from - // another package. - for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) { - final String origName = pkg.mAdoptPermissions.get(i); - final PackageSetting orig = mSettings.getPackageLPr(origName); - if (orig != null) { - if (verifyPackageUpdateLPr(orig, pkg)) { - Slog.i(TAG, "Adopting permissions from " + origName + " to " - + pkg.packageName); - // SIDE EFFECTS; updates permissions system state; move elsewhere - mSettings.mPermissions.transferPermissions(origName, pkg.packageName); - } - } - } - } + if (disabledPkgSetting != null) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } + SELinuxMMAC.assignSeInfoValue(pkg); + + pkg.mExtras = pkgSetting; pkg.applicationInfo.processName = fixProcessName( pkg.applicationInfo.packageName, pkg.applicationInfo.processName); - if (pkg != mPlatformPackage) { + if (!isPlatformPackage) { // Get all of our default paths setup pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM); } @@ -10041,7 +10157,7 @@ public class PackageManagerService extends IPackageManager.Stub if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final boolean extractNativeLibs = !pkg.isLibrary(); - derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs, mAppLib32InstallDir); + derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Some system apps still use directory structure for native libraries @@ -10050,7 +10166,7 @@ public class PackageManagerService extends IPackageManager.Stub if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() && pkg.applicationInfo.primaryCpuAbi == null) { setBundledAppAbisAndRoots(pkg, pkgSetting); - setNativeLibraryPaths(pkg, mAppLib32InstallDir); + setNativeLibraryPaths(pkg, sAppLib32InstallDir); } } else { // This is not a first boot or an upgrade, don't bother deriving the @@ -10059,12 +10175,12 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings; pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings; - setNativeLibraryPaths(pkg, mAppLib32InstallDir); + setNativeLibraryPaths(pkg, sAppLib32InstallDir); if (DEBUG_ABI_SELECTION) { Slog.i(TAG, "Using ABIS and native lib paths from settings : " + - pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " + - pkg.applicationInfo.secondaryCpuAbi); + pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " + + pkg.applicationInfo.secondaryCpuAbi); } } } else { @@ -10080,14 +10196,14 @@ public class PackageManagerService extends IPackageManager.Stub // ABIs we've determined above. For non-moves, the path will be updated based on the // ABIs we determined during compilation, but the path will depend on the final // package path (after the rename away from the stage path). - setNativeLibraryPaths(pkg, mAppLib32InstallDir); + setNativeLibraryPaths(pkg, sAppLib32InstallDir); } // This is a special case for the "system" package, where the ABI is // dictated by the zygote configuration (and init.rc). We should keep track // of this ABI so that we can deal with "normal" applications that run under // the same UID correctly. - if (mPlatformPackage == pkg) { + if (isPlatformPackage) { pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]; } @@ -10127,7 +10243,6 @@ public class PackageManagerService extends IPackageManager.Stub " secondary=" + pkg.applicationInfo.secondaryCpuAbi); } - // SIDE EFFECTS; removes DEX files from disk; move elsewhere if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) { // We don't do this here during boot because we can do it all // at once after scanning all existing packages. @@ -10135,10 +10250,11 @@ public class PackageManagerService extends IPackageManager.Stub // We also do this *before* we perform dexopt on this package, so that // we can avoid redundant dexopts, and also to make sure we've got the // code and package path correct. - adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg); + changedAbiCodePath = + adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg); } - if (mFactoryTest && pkg.requestedPermissions.contains( + if (isUnderFactoryTest && pkg.requestedPermissions.contains( android.Manifest.permission.FACTORY_TEST)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST; } @@ -10167,22 +10283,55 @@ public class PackageManagerService extends IPackageManager.Stub } pkgSetting.setTimeStamp(scanFileTime); - if ((scanFlags & SCAN_CHECK_ONLY) != 0) { - if (nonMutatedPs != null) { - synchronized (mPackages) { - mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs); + return new ScanResult(true, pkgSetting, changedAbiCodePath); + } + + /** + * Returns {@code true} if the given file contains code. Otherwise {@code false}. + */ + private static boolean apkHasCode(String fileName) { + StrictJarFile jarFile = null; + try { + jarFile = new StrictJarFile(fileName, + false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); + return jarFile.findEntry("classes.dex") != null; + } catch (IOException ignore) { + } finally { + try { + if (jarFile != null) { + jarFile.close(); + } + } catch (IOException ignore) {} + } + return false; + } + + /** + * Enforces code policy for the package. This ensures that if an APK has + * declared hasCode="true" in its manifest that the APK actually contains + * code. + * + * @throws PackageManagerException If bytecode could not be found when it should exist + */ + private static void assertCodePolicy(PackageParser.Package pkg) + throws PackageManagerException { + final boolean shouldHaveCode = + (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0; + if (shouldHaveCode && !apkHasCode(pkg.baseCodePath)) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Package " + pkg.baseCodePath + " code is missing"); + } + + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + for (int i = 0; i < pkg.splitCodePaths.length; i++) { + final boolean splitShouldHaveCode = + (pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0; + if (splitShouldHaveCode && !apkHasCode(pkg.splitCodePaths[i])) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Package " + pkg.splitCodePaths[i] + " code is missing"); } - } - } else { - final int userId = user == null ? 0 : user.getIdentifier(); - // Modify state for the given package setting - commitPackageSettings(pkg, pkgSetting, user, scanFlags, - (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/); - if (pkgSetting.getInstantApp(userId)) { - mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId); } } - return pkg; } /** @@ -10192,7 +10341,7 @@ public class PackageManagerService extends IPackageManager.Stub * Implementation detail: This method must NOT have any side effect. It would * ideally be static, but, it requires locks to read system state. */ - private void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags, + private static void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags, final @ScanFlags int scanFlags) { if ((scanFlags & SCAN_AS_SYSTEM) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; @@ -10581,8 +10730,7 @@ public class PackageManagerService extends IPackageManager.Stub * be available for query, resolution, etc... */ private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting, - UserHandle user, final @ScanFlags int scanFlags, boolean chatty) - throws PackageManagerException { + UserHandle user, final @ScanFlags int scanFlags, boolean chatty) { final String pkgName = pkg.packageName; if (mCustomResolverComponentName != null && mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) { @@ -10935,11 +11083,11 @@ public class PackageManagerService extends IPackageManager.Stub * If {@code extractLibs} is true, native libraries are extracted from the app if required. */ private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride, - boolean extractLibs, File appLib32InstallDir) + boolean extractLibs) throws PackageManagerException { // Give ourselves some initial paths; we'll come back for another // pass once we've determined ABI below. - setNativeLibraryPaths(pkg, appLib32InstallDir); + setNativeLibraryPaths(pkg, sAppLib32InstallDir); // We would never need to extract libs for forward-locked and external packages, // since the container service will do it for us. We shouldn't attempt to @@ -11089,7 +11237,7 @@ public class PackageManagerService extends IPackageManager.Stub // Now that we've calculated the ABIs and determined if it's an internal app, // we will go ahead and populate the nativeLibraryPath. - setNativeLibraryPaths(pkg, appLib32InstallDir); + setNativeLibraryPaths(pkg, sAppLib32InstallDir); } /** @@ -11105,8 +11253,9 @@ public class PackageManagerService extends IPackageManager.Stub * NOTE: We currently only match for the primary CPU abi string. Matching the secondary * adds unnecessary complexity. */ - private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser, - PackageParser.Package scannedPackage) { + private static @Nullable List<String> adjustCpuAbisForSharedUserLPw( + Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) { + List<String> changedAbiCodePath = null; String requiredInstructionSet = null; if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { requiredInstructionSet = VMRuntime.getInstructionSet( @@ -11176,15 +11325,15 @@ public class PackageManagerService extends IPackageManager.Stub + (scannedPackage != null ? scannedPackage : "null") + ")"); } - try { - mInstaller.rmdex(ps.codePathString, - getDexCodeInstructionSet(getPreferredInstructionSet())); - } catch (InstallerException ignored) { + if (changedAbiCodePath == null) { + changedAbiCodePath = new ArrayList<>(); } + changedAbiCodePath.add(ps.codePathString); } } } } + return changedAbiCodePath; } private void setUpCustomResolverActivity(PackageParser.Package pkg) { @@ -16621,7 +16770,7 @@ public class PackageManagerService extends IPackageManager.Stub String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ? args.abiOverride : pkg.cpuAbiOverride); final boolean extractNativeLibs = !pkg.isLibrary(); - derivePackageAbi(pkg, abiOverride, extractNativeLibs, mAppLib32InstallDir); + derivePackageAbi(pkg, abiOverride, extractNativeLibs); } catch (PackageManagerException pme) { Slog.e(TAG, "Error deriving application ABI", pme); res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI"); @@ -16947,7 +17096,7 @@ public class PackageManagerService extends IPackageManager.Stub return name.startsWith("vmdl") && name.endsWith(".tmp"); } }; - for (File file : mDrmAppPrivateInstallDir.listFiles(filter)) { + for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) { file.delete(); } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 9733624cddc6..809e16cbee6c 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -131,7 +131,6 @@ public abstract class PackageSettingBase extends SettingBase { * using the full set of code paths when the package's process is started. */ Set<String> oldCodePaths; - PackageSettingBase origPackage; /** Package name of the app that installed this package */ String installerPackageName; @@ -263,7 +262,6 @@ public abstract class PackageSettingBase extends SettingBase { lastUpdateTime = orig.lastUpdateTime; legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString; // Intentionally skip oldCodePaths; it's not relevant for copies - origPackage = orig.origPackage; parentPackageName = orig.parentPackageName; primaryCpuAbiString = orig.primaryCpuAbiString; resourcePath = orig.resourcePath; diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index f0ce3c9d230e..fbf3d82455c8 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -59,6 +59,8 @@ public final class SELinuxMMAC { // All policy stanzas read from mac_permissions.xml. This is also the lock // to synchronize access during policy load and access attempts. private static List<Policy> sPolicies = new ArrayList<>(); + /** Whether or not the policy files have been read */ + private static boolean sPolicyRead; /** Path to MAC permissions on system image */ private static final File[] MAC_PERMISSIONS = @@ -88,6 +90,12 @@ public final class SELinuxMMAC { * were loaded successfully; no partial loading is possible. */ public static boolean readInstallPolicy() { + synchronized (sPolicies) { + if (sPolicyRead) { + return true; + } + } + // Temp structure to hold the rules while we parse the xml file List<Policy> policies = new ArrayList<>(); @@ -142,7 +150,9 @@ public final class SELinuxMMAC { } synchronized (sPolicies) { - sPolicies = policies; + sPolicies.clear(); + sPolicies.addAll(policies); + sPolicyRead = true; if (DEBUG_POLICY_ORDER) { for (Policy policy : sPolicies) { @@ -280,6 +290,12 @@ public final class SELinuxMMAC { */ public static void assignSeInfoValue(PackageParser.Package pkg) { synchronized (sPolicies) { + if (!sPolicyRead) { + if (DEBUG_POLICY) { + Slog.d(TAG, "Policy not read"); + } + return; + } for (Policy policy : sPolicies) { String seInfo = policy.getMatchedSeInfo(pkg); if (seInfo != null) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 4a5772fba96f..648f847ac3c5 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -687,7 +687,6 @@ public final class Settings { (childPkgNames != null) ? new ArrayList<>(childPkgNames) : null; pkgSetting.codePath = codePath; pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; - pkgSetting.origPackage = originalPkg; pkgSetting.parentPackageName = parentPkgName; pkgSetting.pkgFlags = pkgFlags; pkgSetting.pkgPrivateFlags = pkgPrivateFlags; diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 4b1340437795..45649880854a 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -113,7 +113,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_OEM_UNLOCK, UserManager.DISALLOW_UNMUTE_DEVICE, UserManager.DISALLOW_AUTOFILL, - UserManager.DISALLOW_USER_SWITCH + UserManager.DISALLOW_USER_SWITCH, + UserManager.DISALLOW_UNIFIED_PASSWORD, }); /** @@ -192,7 +193,7 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_BLUETOOTH_SHARING ); - /* + /** * Special user restrictions that are always applied to all users no matter who sets them. */ private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet( diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 7c75f777ff1d..9752a57e2423 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2499,7 +2499,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // check if user has enabled this operation. SecurityException will be thrown if this app // has not been allowed by the user - final int mode = mAppOpsManager.checkOpNoThrow(outAppOp[0], callingUid, attrs.packageName); + final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, attrs.packageName); switch (mode) { case AppOpsManager.MODE_ALLOWED: case AppOpsManager.MODE_IGNORED: diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 95b139ad4da2..2bda80d99328 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -234,16 +234,7 @@ final class AccessibilityController { private static void populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix) { - sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx; - sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx; - sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy; - sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy; - sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x; - sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y; - sTempFloats[Matrix.MPERSP_0] = 0; - sTempFloats[Matrix.MPERSP_1] = 0; - sTempFloats[Matrix.MPERSP_2] = 1; - outMatrix.setValues(sTempFloats); + windowState.getTransformationMatrix(sTempFloats, outMatrix); } /** diff --git a/services/core/java/com/android/server/wm/RemoteEventTrace.java b/services/core/java/com/android/server/wm/RemoteEventTrace.java index 9f65ba36f311..b214d35f208c 100644 --- a/services/core/java/com/android/server/wm/RemoteEventTrace.java +++ b/services/core/java/com/android/server/wm/RemoteEventTrace.java @@ -20,6 +20,7 @@ import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.DataOutputStream; +import android.os.StrictMode; import android.util.Slog; import android.os.Debug; @@ -40,22 +41,28 @@ class RemoteEventTrace { } void openSurfaceTransaction() { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); try { mOut.writeUTF("OpenTransaction"); writeSigil(); } catch (Exception e) { logException(e); mService.disableSurfaceTrace(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); } } void closeSurfaceTransaction() { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); try { mOut.writeUTF("CloseTransaction"); writeSigil(); } catch (Exception e) { logException(e); mService.disableSurfaceTrace(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); } } diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java index a12c2c40152d..d2cbf88aac58 100644 --- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java +++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java @@ -20,6 +20,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; import android.os.Parcel; +import android.os.StrictMode; import android.util.Slog; import android.view.SurfaceControl; @@ -54,67 +55,122 @@ class RemoteSurfaceTrace extends SurfaceControlWithBackground { @Override public void setAlpha(float alpha) { - writeFloatEvent("Alpha", alpha); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeFloatEvent("Alpha", alpha); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setAlpha(alpha); } @Override public void setLayer(int zorder) { - writeIntEvent("Layer", zorder); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeIntEvent("Layer", zorder); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setLayer(zorder); } @Override public void setPosition(float x, float y) { - writeFloatEvent("Position", x, y); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeFloatEvent("Position", x, y); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setPosition(x, y); } @Override public void setGeometryAppliesWithResize() { - writeEvent("GeometryAppliesWithResize"); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeEvent("GeometryAppliesWithResize"); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setGeometryAppliesWithResize(); } @Override public void setSize(int w, int h) { - writeIntEvent("Size", w, h); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeIntEvent("Size", w, h); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setSize(w, h); } @Override public void setWindowCrop(Rect crop) { - writeRectEvent("Crop", crop); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeRectEvent("Crop", crop); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setWindowCrop(crop); } @Override public void setFinalCrop(Rect crop) { - writeRectEvent("FinalCrop", crop); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeRectEvent("FinalCrop", crop); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setFinalCrop(crop); } @Override public void setLayerStack(int layerStack) { - writeIntEvent("LayerStack", layerStack); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeIntEvent("LayerStack", layerStack); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setLayerStack(layerStack); } @Override public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.setMatrix(dsdx, dtdx, dsdy, dtdy); } @Override public void hide() { - writeEvent("Hide"); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeEvent("Hide"); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.hide(); } @Override public void show() { - writeEvent("Show"); + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + writeEvent("Show"); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } super.show(); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b08eb18a6f13..e653e7d4afa2 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -431,7 +431,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (w.mAppOp == OP_NONE) { return; } - final int mode = mService.mAppOps.checkOpNoThrow(w.mAppOp, w.getOwningUid(), + final int mode = mService.mAppOps.noteOpNoThrow(w.mAppOp, w.getOwningUid(), w.getOwningPackage()); w.setAppOpVisibilityLw(mode == MODE_ALLOWED || mode == MODE_DEFAULT); }, false /* traverseTopToBottom */); diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index ca9f3a933f70..fa7ea2ffdd17 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -203,7 +203,7 @@ class TaskPositioner { // Post back to WM to handle clean-ups. We still need the input // event handler for the last finishInputEvent()! - mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING); + mService.mTaskPositioningController.finishTaskPositioning(); } handled = true; } catch (Exception e) { diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index bb5706ceb0ec..4dfe290c4f7d 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -22,6 +22,8 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.Nullable; import android.app.IActivityManager; import android.os.RemoteException; +import android.os.Handler; +import android.os.Looper; import android.util.Slog; import android.view.Display; import android.view.IWindow; @@ -37,6 +39,7 @@ class TaskPositioningController { private final InputManagerService mInputManager; private final InputMonitor mInputMonitor; private final IActivityManager mActivityManager; + private final Handler mHandler; @GuardedBy("WindowManagerSerivce.mWindowMap") private @Nullable TaskPositioner mTaskPositioner; @@ -50,11 +53,12 @@ class TaskPositioningController { } TaskPositioningController(WindowManagerService service, InputManagerService inputManager, - InputMonitor inputMonitor, IActivityManager activityManager) { + InputMonitor inputMonitor, IActivityManager activityManager, Looper looper) { mService = service; mInputMonitor = inputMonitor; mInputManager = inputManager; mActivityManager = activityManager; + mHandler = new Handler(looper); } boolean startMovingTask(IWindow window, float startX, float startY) { @@ -75,24 +79,27 @@ class TaskPositioningController { } void handleTapOutsideTask(DisplayContent displayContent, int x, int y) { - int taskId = -1; - synchronized (mService.mWindowMap) { - final Task task = displayContent.findTaskForResizePoint(x, y); - if (task != null) { - if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/, - task.preserveOrientationOnResize(), x, y)) { - return; + mHandler.post(() -> { + int taskId = -1; + synchronized (mService.mWindowMap) { + final Task task = displayContent.findTaskForResizePoint(x, y); + if (task != null) { + if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/, + task.preserveOrientationOnResize(), x, y)) { + return; + } + taskId = task.mTaskId; + } else { + taskId = displayContent.taskIdFromPoint(x, y); } - taskId = task.mTaskId; - } else { - taskId = displayContent.taskIdFromPoint(x, y); } - } - if (taskId >= 0) { - try { - mActivityManager.setFocusedTask(taskId); - } catch(RemoteException e) {} - } + if (taskId >= 0) { + try { + mActivityManager.setFocusedTask(taskId); + } catch (RemoteException e) { + } + } + }); } private boolean startPositioningLocked(WindowState win, boolean resize, @@ -145,15 +152,17 @@ class TaskPositioningController { return true; } - void finishPositioning() { - if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning"); + void finishTaskPositioning() { + mHandler.post(() -> { + if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning"); - synchronized (mService.mWindowMap) { - if (mTaskPositioner != null) { - mTaskPositioner.unregister(); - mTaskPositioner = null; - mInputMonitor.updateInputWindowsLw(true /*force*/); + synchronized (mService.mWindowMap) { + if (mTaskPositioner != null) { + mTaskPositioner.unregister(); + mTaskPositioner = null; + mInputMonitor.updateInputWindowsLw(true /*force*/); + } } - } + }); } } diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index 84ad5764025c..5abda2775664 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -62,8 +62,8 @@ public class TaskTapPointerEventListener implements PointerEventListener { synchronized (this) { if (!mTouchExcludeRegion.contains(x, y)) { - mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK, - x, y, mDisplayContent).sendToTarget(); + mService.mTaskPositioningController.handleTapOutsideTask( + mDisplayContent, x, y); } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7a3ba74086a3..9fc9f3c15cfc 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1086,8 +1086,8 @@ public class WindowManagerService extends IWindowManager.Stub mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); - mTaskPositioningController = - new TaskPositioningController(this, mInputManager, mInputMonitor, mActivityManager); + mTaskPositioningController = new TaskPositioningController( + this, mInputManager, mInputMonitor, mActivityManager, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); LocalServices.addService(WindowManagerInternal.class, new LocalService()); @@ -4561,6 +4561,7 @@ public class WindowManagerService extends IWindowManager.Stub if (displayContent != null) { mAnimator.addDisplayLocked(displayId); displayContent.initializeDisplayBaseInfo(); + reconfigureDisplayLocked(displayContent); } } } @@ -4608,7 +4609,6 @@ public class WindowManagerService extends IWindowManager.Stub public static final int DO_ANIMATION_CALLBACK = 26; public static final int CLIENT_FREEZE_TIMEOUT = 30; - public static final int TAP_OUTSIDE_TASK = 31; public static final int NOTIFY_ACTIVITY_DRAWN = 32; public static final int ALL_WINDOWS_DRAWN = 33; @@ -4622,8 +4622,6 @@ public class WindowManagerService extends IWindowManager.Stub public static final int RESET_ANR_MESSAGE = 38; public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39; - public static final int FINISH_TASK_POSITIONING = 40; - public static final int UPDATE_DOCKED_STACK_DIVIDER = 41; public static final int WINDOW_REPLACEMENT_TIMEOUT = 46; @@ -4900,17 +4898,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case TAP_OUTSIDE_TASK: { - mTaskPositioningController.handleTapOutsideTask( - (DisplayContent)msg.obj, msg.arg1, msg.arg2); - } - break; - - case FINISH_TASK_POSITIONING: { - mTaskPositioningController.finishPositioning(); - } - break; - case NOTIFY_ACTIVITY_DRAWN: try { mActivityManager.notifyActivityDrawn((IBinder) msg.obj); @@ -6700,7 +6687,6 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { final Display display = mDisplayManager.getDisplay(displayId); if (display != null) { - createDisplayContentLocked(display); displayReady(displayId); } mWindowPlacerLocked.requestTraversal(); diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 235869551c73..b9dc9db7c5ee 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -18,9 +18,23 @@ package com.android.server.wm; import static android.os.Build.IS_USER; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.os.ShellCommand; +import android.os.UserHandle; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.IWindowManager; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.PrintWriter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * ShellCommands for WindowManagerService. @@ -29,33 +43,279 @@ import java.io.PrintWriter; */ public class WindowManagerShellCommand extends ShellCommand { - private final WindowManagerService mService; + // IPC interface to activity manager -- don't need to do additional security checks. + private final IWindowManager mInterface; + + // Internal service impl -- must perform security checks before touching. + private final WindowManagerService mInternal; public WindowManagerShellCommand(WindowManagerService service) { - mService = service; + mInterface = service; + mInternal = service; } @Override public int onCommand(String cmd) { - switch (cmd) { - case "tracing": - return mService.mWindowTracing.onShellCommand(this, getNextArgRequired()); - default: - return handleDefaultCommands(cmd); + if (cmd == null) { + return handleDefaultCommands(cmd); + } + final PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd) { + case "size": + return runDisplaySize(pw); + case "density": + return runDisplayDensity(pw); + case "overscan": + return runDisplayOverscan(pw); + case "scaling": + return runDisplayScaling(pw); + case "screen-capture": + return runSetScreenCapture(pw); + case "dismiss-keyguard": + return runDismissKeyguard(pw); + case "surface-trace": + return runSurfaceTrace(pw); + case "tracing": + // XXX this should probably be changed to use openFileForSystem() to create + // the output trace file, so the shell gets the correct semantics for where + // trace files can be written. + return mInternal.mWindowTracing.onShellCommand(this, + getNextArgRequired()); + default: + return handleDefaultCommands(cmd); + } + } catch (RemoteException e) { + pw.println("Remote exception: " + e); + } + return -1; + } + + private int runDisplaySize(PrintWriter pw) throws RemoteException { + String size = getNextArg(); + int w, h; + if (size == null) { + Point initialSize = new Point(); + Point baseSize = new Point(); + try { + mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize); + mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize); + pw.println("Physical size: " + initialSize.x + "x" + initialSize.y); + if (!initialSize.equals(baseSize)) { + pw.println("Override size: " + baseSize.x + "x" + baseSize.y); + } + } catch (RemoteException e) { + } + return 0; + } else if ("reset".equals(size)) { + w = h = -1; + } else { + int div = size.indexOf('x'); + if (div <= 0 || div >= (size.length()-1)) { + getErrPrintWriter().println("Error: bad size " + size); + return -1; + } + String wstr = size.substring(0, div); + String hstr = size.substring(div+1); + try { + w = parseDimension(wstr); + h = parseDimension(hstr); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad number " + e); + return -1; + } + } + + if (w >= 0 && h >= 0) { + // TODO(multidisplay): For now Configuration only applies to main screen. + mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h); + } else { + mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY); + } + return 0; + } + + private int runDisplayDensity(PrintWriter pw) throws RemoteException { + String densityStr = getNextArg(); + int density; + if (densityStr == null) { + try { + int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY); + int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY); + pw.println("Physical density: " + initialDensity); + if (initialDensity != baseDensity) { + pw.println("Override density: " + baseDensity); + } + } catch (RemoteException e) { + } + return 0; + } else if ("reset".equals(densityStr)) { + density = -1; + } else { + try { + density = Integer.parseInt(densityStr); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad number " + e); + return -1; + } + if (density < 72) { + getErrPrintWriter().println("Error: density must be >= 72"); + return -1; + } + } + + if (density > 0) { + // TODO(multidisplay): For now Configuration only applies to main screen. + mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density, + UserHandle.USER_CURRENT); + } else { + mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, + UserHandle.USER_CURRENT); + } + return 0; + } + + private int runDisplayOverscan(PrintWriter pw) throws RemoteException { + String overscanStr = getNextArgRequired(); + Rect rect = new Rect(); + if ("reset".equals(overscanStr)) { + rect.set(0, 0, 0, 0); + } else { + final Pattern FLATTENED_PATTERN = Pattern.compile( + "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)"); + Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr); + if (!matcher.matches()) { + getErrPrintWriter().println("Error: bad rectangle arg: " + overscanStr); + return -1; + } + rect.left = Integer.parseInt(matcher.group(1)); + rect.top = Integer.parseInt(matcher.group(2)); + rect.right = Integer.parseInt(matcher.group(3)); + rect.bottom = Integer.parseInt(matcher.group(4)); + } + + mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, + rect.bottom); + return 0; + } + + private int runDisplayScaling(PrintWriter pw) throws RemoteException { + String scalingStr = getNextArgRequired(); + if ("auto".equals(scalingStr)) { + mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0); + } else if ("off".equals(scalingStr)) { + mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1); + } else { + getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'"); + return -1; + } + return 0; + } + + private int runSetScreenCapture(PrintWriter pw) throws RemoteException { + String userIdStr = getNextArg(); + String enableStr = getNextArg(); + int userId; + boolean disable; + + try { + userId = Integer.parseInt(userIdStr); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad number " + e); + return -1; + } + + disable = !Boolean.parseBoolean(enableStr); + mInternal.setScreenCaptureDisabled(userId, disable); + return 0; + } + + private int runDismissKeyguard(PrintWriter pw) throws RemoteException { + mInterface.dismissKeyguard(null /* callback */); + return 0; + } + + private int runSurfaceTrace(PrintWriter pw) throws RemoteException { + final ParcelFileDescriptor pfd; + try { + pfd = ParcelFileDescriptor.dup(getOutFileDescriptor()); + } catch (IOException e) { + getErrPrintWriter().println("Unable to dup output stream: " + e.getMessage()); + return -1; + } + mInternal.enableSurfaceTrace(pfd); + + // Read input until an explicit quit command is sent or the stream is closed (meaning + // the user killed the command). + try { + InputStream input = getRawInputStream(); + InputStreamReader converter = new InputStreamReader(input); + BufferedReader in = new BufferedReader(converter); + String line; + + while ((line = in.readLine()) != null) { + if (line.length() <= 0) { + // no-op + } else if ("q".equals(line) || "quit".equals(line)) { + break; + } else { + pw.println("Invalid command: " + line); + } + } + } catch (IOException e) { + e.printStackTrace(pw); + } finally { + mInternal.disableSurfaceTrace(); + try { + pfd.close(); + } catch (IOException e) { + } + } + + return 0; + } + + private int parseDimension(String s) throws NumberFormatException { + if (s.endsWith("px")) { + return Integer.parseInt(s.substring(0, s.length() - 2)); + } + if (s.endsWith("dp")) { + int density; + try { + density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY); + } catch (RemoteException e) { + density = DisplayMetrics.DENSITY_DEFAULT; + } + return Integer.parseInt(s.substring(0, s.length() - 2)) * density / + DisplayMetrics.DENSITY_DEFAULT; } + return Integer.parseInt(s); } @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); - pw.println("Window Manager (window) commands:"); + pw.println("Window manager (window) commands:"); pw.println(" help"); - pw.println(" Print this help text."); - pw.println(); - if (!IS_USER){ + pw.println(" Print this help text."); + pw.println(" size [reset|WxH|WdpxHdp]"); + pw.println(" Return or override display size."); + pw.println(" width and height in pixels unless suffixed with 'dp'."); + pw.println(" density [reset|DENSITY]"); + pw.println(" Return or override display density."); + pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM]"); + pw.println(" Set overscan area for display."); + pw.println(" scaling [off|auto]"); + pw.println(" Set display scaling mode."); + pw.println(" screen-capture [userId] [true|false]"); + pw.println(" Enable or disable screen capture."); + pw.println(" dismiss-keyguard"); + pw.println(" Dismiss the keyguard, prompting user for auth if necessary."); + pw.println(" surface-trace"); + pw.println(" Log surface commands to stdout in a binary format."); + if (!IS_USER) { pw.println(" tracing (start | stop)"); - pw.println(" start or stop window tracing"); - pw.println(); + pw.println(" Start or stop window tracing."); } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6bebcf407c75..ddc1eace1ea2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -118,7 +118,9 @@ import static com.android.server.wm.proto.WindowStateProto.GIVEN_CONTENT_INSETS; import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER; import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME; import static com.android.server.wm.proto.WindowStateProto.STACK_ID; +import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION; import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS; +import static com.android.server.wm.proto.WindowStateProto.SURFACE_POSITION; import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER; import android.annotation.CallSuper; @@ -3112,6 +3114,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mContentFrame.writeToProto(proto, CONTENT_FRAME); mContentInsets.writeToProto(proto, CONTENT_INSETS); mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS); + mSurfacePosition.writeToProto(proto, SURFACE_POSITION); + mShownPosition.writeToProto(proto, SHOWN_POSITION); mWinAnimator.writeToProto(proto, ANIMATOR); proto.write(ANIMATING_EXIT, mAnimatingExit); for (int i = 0; i < mChildren.size(); i++) { @@ -4314,6 +4318,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWinAnimator.onAnimationFinished(); } + /** + * Retrieves the current transformation matrix of the window, relative to the display. + * + * @param float9 A temporary array of 9 floats. + * @param outMatrix Matrix to fill in the transformation. + */ + void getTransformationMatrix(float[] float9, Matrix outMatrix) { + float9[Matrix.MSCALE_X] = mWinAnimator.mDsDx; + float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx; + float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy; + float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy; + float9[Matrix.MTRANS_X] = mSurfacePosition.x + mShownPosition.x; + float9[Matrix.MTRANS_Y] = mSurfacePosition.y + mShownPosition.y; + float9[Matrix.MPERSP_0] = 0; + float9[Matrix.MPERSP_1] = 0; + float9[Matrix.MPERSP_2] = 1; + outMatrix.setValues(float9); + } + // TODO: Hack to work around the number of states AppWindowToken needs to access without having // access to its windows children. Need to investigate re-writing // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed. diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp index 222ac5b903fb..98921465c673 100644 --- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp +++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp @@ -257,7 +257,7 @@ static const JNINativeMethod gRadioServiceMethods[] = { { "nativeFinalize", "(J)V", (void*)nativeFinalize }, { "nativeLoadModules", "(J)Ljava/util/List;", (void*)nativeLoadModules }, { "nativeOpenTuner", "(JILandroid/hardware/radio/RadioManager$BandConfig;Z" - "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/Tuner;", + "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/hal1/Tuner;", (void*)nativeOpenTuner }, }; @@ -270,7 +270,7 @@ void register_android_server_broadcastradio_BroadcastRadioService(JNIEnv *env) { register_android_server_broadcastradio_convert(env); - auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner"); + auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner"); gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass); gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>", "(Landroid/hardware/radio/ITunerCallback;IIZI)V"); @@ -281,7 +281,7 @@ void register_android_server_broadcastradio_BroadcastRadioService(JNIEnv *env) { gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); auto res = jniRegisterNativeMethods(env, - "com/android/server/broadcastradio/BroadcastRadioService", + "com/android/server/broadcastradio/hal1/BroadcastRadioService", gRadioServiceMethods, NELEM(gRadioServiceMethods)); LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); } diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp index 63339e933cac..42eb8739c9e7 100644 --- a/services/core/jni/BroadcastRadio/Tuner.cpp +++ b/services/core/jni/BroadcastRadio/Tuner.cpp @@ -592,18 +592,18 @@ void register_android_server_broadcastradio_Tuner(JavaVM *vm, JNIEnv *env) { register_android_server_broadcastradio_TunerCallback(vm, env); - auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner"); + auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner"); gjni.Tuner.nativeContext = GetFieldIDOrDie(env, tunerClass, "mNativeContext", "J"); gjni.Tuner.region = GetFieldIDOrDie(env, tunerClass, "mRegion", "I"); gjni.Tuner.tunerCallback = GetFieldIDOrDie(env, tunerClass, "mTunerCallback", - "Lcom/android/server/broadcastradio/TunerCallback;"); + "Lcom/android/server/broadcastradio/hal1/TunerCallback;"); auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass); gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V"); gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); - auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/Tuner", + auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/Tuner", gTunerMethods, NELEM(gTunerMethods)); LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); } diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp index d0ba00529d0b..39f2c057fc86 100644 --- a/services/core/jni/BroadcastRadio/TunerCallback.cpp +++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp @@ -406,7 +406,7 @@ sp<ITunerCallback> getNativeCallback(JNIEnv *env, jobject jTunerCallback) { } static const JNINativeMethod gTunerCallbackMethods[] = { - { "nativeInit", "(Lcom/android/server/broadcastradio/Tuner;I)J", (void*)nativeInit }, + { "nativeInit", "(Lcom/android/server/broadcastradio/hal1/Tuner;I)J", (void*)nativeInit }, { "nativeFinalize", "(J)V", (void*)nativeFinalize }, { "nativeDetach", "(J)V", (void*)nativeDetach }, }; @@ -420,7 +420,7 @@ void register_android_server_broadcastradio_TunerCallback(JavaVM *vm, JNIEnv *en gvm = vm; - auto tunerCbClass = FindClassOrDie(env, "com/android/server/broadcastradio/TunerCallback"); + auto tunerCbClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/TunerCallback"); gjni.TunerCallback.clazz = MakeGlobalRefOrDie(env, tunerCbClass); gjni.TunerCallback.nativeContext = GetFieldIDOrDie(env, tunerCbClass, "mNativeContext", "J"); gjni.TunerCallback.handleHwFailure = GetMethodIDOrDie(env, tunerCbClass, "handleHwFailure", "()V"); @@ -444,7 +444,7 @@ void register_android_server_broadcastradio_TunerCallback(JavaVM *vm, JNIEnv *en gjni.TunerCallback.onParametersUpdated = GetMethodIDOrDie(env, tunerCbClass, "onParametersUpdated", "(Ljava/util/Map;)V"); - auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/TunerCallback", + auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/TunerCallback", gTunerCallbackMethods, NELEM(gTunerCallbackMethods)); LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); } diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp index f5381a8caac1..be1ad72b6006 100644 --- a/services/core/jni/BroadcastRadio/convert.cpp +++ b/services/core/jni/BroadcastRadio/convert.cpp @@ -678,7 +678,7 @@ void register_android_server_broadcastradio_convert(JNIEnv *env) { gjni.AmBandDescriptor.cstor = GetMethodIDOrDie(env, amBandDescriptorClass, "<init>", "(IIIIIZ)V"); - auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/Convert"); + auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Convert"); gjni.Convert.clazz = MakeGlobalRefOrDie(env, convertClass); gjni.Convert.stringMapToNative = GetStaticMethodIDOrDie(env, convertClass, "stringMapToNative", "(Ljava/util/Map;)[[Ljava/lang/String;"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 916303762db8..5b9e3a1e70eb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -18,6 +18,8 @@ package com.android.server.devicepolicy; import android.app.admin.IDevicePolicyManager; import android.content.ComponentName; import android.os.PersistableBundle; +import android.security.keymaster.KeymasterCertificateChain; +import android.security.keystore.ParcelableKeyGenParameterSpec; import com.android.internal.R; import com.android.server.SystemService; @@ -56,8 +58,17 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { * @see {@link SystemService#onStopUser} */ abstract void handleStopUser(int userId); - + public void setSystemSetting(ComponentName who, String setting, String value){} public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {} + + public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm, + ParcelableKeyGenParameterSpec keySpec, KeymasterCertificateChain attestationChain) { + return false; + } + + public boolean isUsingUnifiedPassword(ComponentName who) { + return true; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 2080b72b3370..bead31fc675e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -150,13 +150,16 @@ import android.provider.ContactsContract.QuickContact; import android.provider.ContactsInternal; import android.provider.Settings; import android.provider.Settings.Global; +import android.security.Credentials; import android.security.IKeyChainAliasCallback; import android.security.IKeyChainService; import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; +import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.ParcelableKeyGenParameterSpec; import android.security.KeyStore; +import android.security.keystore.AttestationUtils; import android.service.persistentdata.PersistentDataBlockManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -4079,6 +4082,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean isUsingUnifiedPassword(ComponentName admin) { + if (!mHasFeature) { + return true; + } + final int userId = mInjector.userHandleGetCallingUserId(); + enforceProfileOrDeviceOwner(admin); + enforceManagedProfile(userId, "query unified challenge status"); + return !isSeparateProfileChallengeEnabled(userId); + } + + @Override public boolean isProfileActivePasswordSufficientForParent(int userHandle) { if (!mHasFeature) { return true; @@ -4913,19 +4927,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm, - ParcelableKeyGenParameterSpec parcelableKeySpec) { + ParcelableKeyGenParameterSpec parcelableKeySpec, + KeymasterCertificateChain attestationChain) { enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, DELEGATION_CERT_INSTALL); final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); if (TextUtils.isEmpty(keySpec.getKeystoreAlias())) { throw new IllegalArgumentException("Empty alias provided."); } + final String alias = keySpec.getKeystoreAlias(); // As the caller will be granted access to the key, ensure no UID was specified, as // it will not have the desired effect. if (keySpec.getUid() != KeyStore.UID_SELF) { Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair."); return false; } + final int callingUid = mInjector.binderGetCallingUid(); final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); @@ -4934,7 +4951,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try (KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle)) { IKeyChainService keyChain = keyChainConnection.getService(); - final boolean generationResult = keyChain.generateKeyPair(algorithm, parcelableKeySpec); + + // Copy the provided keySpec, excluding the attestation challenge, which will be + // used later for requesting key attestation record. + final KeyGenParameterSpec noAttestationSpec = + new KeyGenParameterSpec.Builder(keySpec) + .setAttestationChallenge(null) + .build(); + + final boolean generationResult = keyChain.generateKeyPair(algorithm, + new ParcelableKeyGenParameterSpec(noAttestationSpec)); if (!generationResult) { Log.e(LOG_TAG, "KeyChain failed to generate a keypair."); return false; @@ -4945,7 +4971,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Note the use of the calling UID, since the request for the private // key will come from the client's process, so the grant has to be for // that UID. - keyChain.setGrant(callingUid, keySpec.getKeystoreAlias(), true); + keyChain.setGrant(callingUid, alias, true); + + final byte[] attestationChallenge = keySpec.getAttestationChallenge(); + if (attestationChallenge != null) { + final boolean attestationResult = keyChain.attestKey( + alias, attestationChallenge, attestationChain); + if (!attestationResult) { + Log.e(LOG_TAG, String.format( + "Attestation for %s failed, deleting key.", alias)); + keyChain.removeKeyPair(alias); + return false; + } + } return true; } } catch (RemoteException e) { diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java index 2cb4a69cdcc1..13623e50d9c7 100644 --- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java +++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java @@ -18,9 +18,13 @@ package com.android.server.backup; import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.fail; + import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.robolectric.shadow.api.Shadow.extract; +import android.annotation.Nullable; import android.app.backup.BackupManager; import android.content.ComponentName; import android.content.Intent; @@ -29,15 +33,18 @@ import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.IBinder; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; -import com.android.server.backup.testing.BackupTransportStub; +import com.android.internal.backup.IBackupTransport; +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.server.backup.testing.ShadowBackupTransportStub; import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.backup.testing.TransportBoundListenerStub; import com.android.server.backup.testing.TransportReadyCallbackStub; import com.android.server.backup.transport.TransportClient; +import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; @@ -95,15 +102,29 @@ public class TransportManagerTest { (ShadowPackageManagerForBackup) extract(RuntimeEnvironment.application.getPackageManager()); - mTransport1 = new TransportInfo(PACKAGE_NAME, "transport1.name"); - mTransport2 = new TransportInfo(PACKAGE_NAME, "transport2.name"); + mTransport1 = new TransportInfo( + PACKAGE_NAME, + "transport1.name", + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel"); + mTransport2 = new TransportInfo( + PACKAGE_NAME, + "transport2.name", + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel"); ShadowContextImplForBackup.sComponentBinderMap.put(mTransport1.componentName, mTransport1.binder); ShadowContextImplForBackup.sComponentBinderMap.put(mTransport2.componentName, mTransport2.binder); - ShadowBackupTransportStub.sBinderTransportMap.put(mTransport1.binder, mTransport1.stub); - ShadowBackupTransportStub.sBinderTransportMap.put(mTransport2.binder, mTransport2.stub); + ShadowBackupTransportStub.sBinderTransportMap.put( + mTransport1.binder, mTransport1.binderInterface); + ShadowBackupTransportStub.sBinderTransportMap.put( + mTransport2.binder, mTransport2.binderInterface); } @After @@ -129,8 +150,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isTrue(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -153,8 +176,10 @@ public class TransportManagerTest { Collections.singleton(mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Collections.singleton(mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -193,8 +218,10 @@ public class TransportManagerTest { Collections.singleton(mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Collections.singleton(mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -250,8 +277,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -265,8 +294,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isFalse(); } @Test @@ -280,8 +311,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isFalse(); } @Test @@ -295,8 +328,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -305,9 +340,9 @@ public class TransportManagerTest { Arrays.asList(mTransport1, mTransport2), mTransport1.name); assertThat(transportManager.getTransportBinder(mTransport1.name)).isEqualTo( - mTransport1.stub); + mTransport1.binderInterface); assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo( - mTransport2.stub); + mTransport2.binderInterface); } @Test @@ -326,7 +361,7 @@ public class TransportManagerTest { assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull(); assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo( - mTransport2.stub); + mTransport2.binderInterface); } @Test @@ -356,7 +391,8 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - assertThat(transportManager.getCurrentTransportBinder()).isEqualTo(mTransport1.stub); + assertThat(transportManager.getCurrentTransportBinder()) + .isEqualTo(mTransport1.binderInterface); } @Test @@ -375,8 +411,10 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - assertThat(transportManager.getTransportName(mTransport1.stub)).isEqualTo(mTransport1.name); - assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name); + assertThat(transportManager.getTransportName(mTransport1.binderInterface)) + .isEqualTo(mTransport1.name); + assertThat(transportManager.getTransportName(mTransport2.binderInterface)) + .isEqualTo(mTransport2.name); } @Test @@ -385,8 +423,9 @@ public class TransportManagerTest { createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), Collections.singletonList(mTransport1), mTransport1.name); - assertThat(transportManager.getTransportName(mTransport1.stub)).isNull(); - assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name); + assertThat(transportManager.getTransportName(mTransport1.binderInterface)).isNull(); + assertThat(transportManager.getTransportName(mTransport2.binderInterface)) + .isEqualTo(mTransport2.name); } @Test @@ -499,7 +538,7 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - transportManager.describeTransport( + transportManager.updateTransportAttributes( mTransport1.componentName, "newName", null, "destinationString", null, null); TransportClient transportClient = @@ -514,7 +553,7 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - transportManager.describeTransport( + transportManager.updateTransportAttributes( mTransport1.componentName, "newName", null, "destinationString", null, null); TransportClient transportClient = @@ -529,7 +568,7 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - transportManager.describeTransport( + transportManager.updateTransportAttributes( mTransport1.componentName, "newName", null, "destinationString", null, null); String transportName = transportManager.getTransportName(mTransport1.componentName); @@ -549,6 +588,48 @@ public class TransportManagerTest { assertThat(transportManager.isTransportRegistered(mTransport2.name)).isFalse(); } + @Test + public void getTransportAttributes_forRegisteredTransport_returnsCorrectValues() + throws Exception { + TransportManager transportManager = + createTransportManagerAndSetUpTransports( + Collections.singletonList(mTransport1), + mTransport1.name); + + assertThat(transportManager.getTransportConfigurationIntent(mTransport1.name)) + .isEqualTo(mTransport1.binderInterface.configurationIntent()); + assertThat(transportManager.getTransportDataManagementIntent(mTransport1.name)) + .isEqualTo(mTransport1.binderInterface.dataManagementIntent()); + assertThat(transportManager.getTransportDataManagementLabel(mTransport1.name)) + .isEqualTo(mTransport1.binderInterface.dataManagementLabel()); + assertThat(transportManager.getTransportDirName(mTransport1.name)) + .isEqualTo(mTransport1.binderInterface.transportDirName()); + } + + @Test + public void getTransportAttributes_forUnregisteredTransport_throws() + throws Exception { + TransportManager transportManager = + createTransportManagerAndSetUpTransports( + Collections.singletonList(mTransport1), + Collections.singletonList(mTransport2), + mTransport1.name); + + expectThrows( + TransportNotRegisteredException.class, + () -> transportManager.getTransportConfigurationIntent(mTransport2.name)); + expectThrows( + TransportNotRegisteredException.class, + () -> transportManager.getTransportDataManagementIntent( + mTransport2.name)); + expectThrows( + TransportNotRegisteredException.class, + () -> transportManager.getTransportDataManagementLabel(mTransport2.name)); + expectThrows( + TransportNotRegisteredException.class, + () -> transportManager.getTransportDirName(mTransport2.name)); + } + private void setUpPackageWithTransports(String packageName, List<TransportInfo> transports, int flags) throws Exception { PackageInfo packageInfo = new PackageInfo(); @@ -616,10 +697,12 @@ public class TransportManagerTest { assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( availableTransportsNames); for (TransportInfo transport : availableTransports) { - assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface)) + .isTrue(); } for (TransportInfo transport : unavailableTransports) { - assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface)) + .isFalse(); } mTransportBoundListenerStub.resetState(); @@ -627,19 +710,46 @@ public class TransportManagerTest { return transportManager; } + private static <T extends Throwable> void expectThrows( + Class<T> throwableClass, ThrowingRunnable runnable) { + try { + runnable.runOrThrow(); + fail("Expected to throw " + throwableClass.getSimpleName()); + } catch (Throwable t) { + assertThat(t).isInstanceOf(throwableClass); + } + } + private static class TransportInfo { public final String packageName; public final String name; public final ComponentName componentName; - public final BackupTransportStub stub; + public final IBackupTransport binderInterface; public final IBinder binder; - TransportInfo(String packageName, String name) { + TransportInfo( + String packageName, + String name, + @Nullable Intent configurationIntent, + String currentDestinationString, + @Nullable Intent dataManagementIntent, + String dataManagementLabel) { this.packageName = packageName; this.name = name; this.componentName = new ComponentName(packageName, name); - this.stub = new BackupTransportStub(name); this.binder = mock(IBinder.class); + IBackupTransport transport = mock(IBackupTransport.class); + try { + when(transport.name()).thenReturn(name); + when(transport.configurationIntent()).thenReturn(configurationIntent); + when(transport.currentDestinationString()).thenReturn(currentDestinationString); + when(transport.dataManagementIntent()).thenReturn(dataManagementIntent); + when(transport.dataManagementLabel()).thenReturn(dataManagementLabel); + } catch (RemoteException e) { + // Only here to mock methods that throw RemoteException + } + this.binderInterface = transport; } } + } diff --git a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java b/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java deleted file mode 100644 index ec09f908c90d..000000000000 --- a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2017 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.backup.testing; - -import android.app.backup.RestoreDescription; -import android.app.backup.RestoreSet; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; - -import com.android.internal.backup.IBackupTransport; - -/** - * Stub backup transport, doing nothing and returning default values. - */ -public class BackupTransportStub implements IBackupTransport { - - private final String mName; - - public BackupTransportStub(String name) { - mName = name; - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public String name() throws RemoteException { - return mName; - } - - @Override - public Intent configurationIntent() throws RemoteException { - return null; - } - - @Override - public String currentDestinationString() throws RemoteException { - return null; - } - - @Override - public Intent dataManagementIntent() throws RemoteException { - return null; - } - - @Override - public String dataManagementLabel() throws RemoteException { - return null; - } - - @Override - public String transportDirName() throws RemoteException { - return null; - } - - @Override - public long requestBackupTime() throws RemoteException { - return 0; - } - - @Override - public int initializeDevice() throws RemoteException { - return 0; - } - - @Override - public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags) - throws RemoteException { - return 0; - } - - @Override - public int clearBackupData(PackageInfo packageInfo) throws RemoteException { - return 0; - } - - @Override - public int finishBackup() throws RemoteException { - return 0; - } - - @Override - public RestoreSet[] getAvailableRestoreSets() throws RemoteException { - return new RestoreSet[0]; - } - - @Override - public long getCurrentRestoreSet() throws RemoteException { - return 0; - } - - @Override - public int startRestore(long token, PackageInfo[] packages) throws RemoteException { - return 0; - } - - @Override - public RestoreDescription nextRestorePackage() throws RemoteException { - return null; - } - - @Override - public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException { - return 0; - } - - @Override - public void finishRestore() throws RemoteException { - - } - - @Override - public long requestFullBackupTime() throws RemoteException { - return 0; - } - - @Override - public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket, - int flags) - throws RemoteException { - return 0; - } - - @Override - public int checkFullBackupSize(long size) throws RemoteException { - return 0; - } - - @Override - public int sendBackupData(int numBytes) throws RemoteException { - return 0; - } - - @Override - public void cancelFullBackup() throws RemoteException { - - } - - @Override - public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup) - throws RemoteException { - return false; - } - - @Override - public long getBackupQuota(String packageName, boolean isFullBackup) - throws RemoteException { - return 0; - } - - @Override - public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException { - return 0; - } - - @Override - public int abortFullRestore() throws RemoteException { - return 0; - } -} diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java index 0d03863a0804..f38404436e69 100644 --- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java +++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; +import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.view.Display.DEFAULT_DISPLAY; @@ -36,6 +37,7 @@ import static org.mockito.Mockito.spy; import static java.lang.Integer.MAX_VALUE; +import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityManager.RunningTaskInfo; import android.content.ComponentName; @@ -181,23 +183,70 @@ public class RecentTasksTest extends ActivityTestsBase { assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(1))); mCallbacksRecorder.clear(); - // Add a task which will trigger the trimming of another + // Remove the callback, ensure we don't get any calls + mRecentTasks.unregisterCallback(mCallbacksRecorder); + mRecentTasks.add(mTasks.get(0)); + mRecentTasks.remove(mTasks.get(0)); + assertTrue(mCallbacksRecorder.added.isEmpty()); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.isEmpty()); + } + + @Test + public void testAddTasksNoMultiple_expectNoTrim() throws Exception { + // Add same non-multiple-task document tasks will remove the task (to re-add it) but not + // trim it TaskRecord documentTask1 = createDocumentTask(".DocumentTask1"); - documentTask1.maxRecents = 1; TaskRecord documentTask2 = createDocumentTask(".DocumentTask1"); mRecentTasks.add(documentTask1); mRecentTasks.add(documentTask2); assertTrue(mCallbacksRecorder.added.contains(documentTask1)); assertTrue(mCallbacksRecorder.added.contains(documentTask2)); - assertTrue(mCallbacksRecorder.trimmed.contains(documentTask1)); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); assertTrue(mCallbacksRecorder.removed.contains(documentTask1)); - mCallbacksRecorder.clear(); + } - // Remove the callback, ensure we don't get any calls - mRecentTasks.unregisterCallback(mCallbacksRecorder); - mRecentTasks.add(mTasks.get(0)); - mRecentTasks.remove(mTasks.get(0)); - assertTrue(mCallbacksRecorder.added.isEmpty()); + @Test + public void testAddTasksMaxTaskRecents_expectNoTrim() throws Exception { + // Add a task hitting max-recents for that app will remove the task (to add the next one) + // but not trim it + TaskRecord documentTask1 = createDocumentTask(".DocumentTask1"); + TaskRecord documentTask2 = createDocumentTask(".DocumentTask1"); + documentTask1.maxRecents = 1; + documentTask2.maxRecents = 1; + mRecentTasks.add(documentTask1); + mRecentTasks.add(documentTask2); + assertTrue(mCallbacksRecorder.added.contains(documentTask1)); + assertTrue(mCallbacksRecorder.added.contains(documentTask2)); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.contains(documentTask1)); + } + + @Test + public void testAddTasksSameTask_expectNoTrim() throws Exception { + // Add a task that is already in the task list does not trigger any callbacks, it just + // moves in the list + TaskRecord documentTask1 = createDocumentTask(".DocumentTask1"); + mRecentTasks.add(documentTask1); + mRecentTasks.add(documentTask1); + assertTrue(mCallbacksRecorder.added.size() == 1); + assertTrue(mCallbacksRecorder.added.contains(documentTask1)); + assertTrue(mCallbacksRecorder.trimmed.isEmpty()); + assertTrue(mCallbacksRecorder.removed.isEmpty()); + } + + @Test + public void testAddTasksMultipleTasks_expectNoTrim() throws Exception { + // Add same multiple-task document tasks does not trim the first tasks + TaskRecord documentTask1 = createDocumentTask(".DocumentTask1", + FLAG_ACTIVITY_MULTIPLE_TASK); + TaskRecord documentTask2 = createDocumentTask(".DocumentTask1", + FLAG_ACTIVITY_MULTIPLE_TASK); + mRecentTasks.add(documentTask1); + mRecentTasks.add(documentTask2); + assertTrue(mCallbacksRecorder.added.size() == 2); + assertTrue(mCallbacksRecorder.added.contains(documentTask1)); + assertTrue(mCallbacksRecorder.added.contains(documentTask2)); assertTrue(mCallbacksRecorder.trimmed.isEmpty()); assertTrue(mCallbacksRecorder.removed.isEmpty()); } @@ -549,10 +598,15 @@ public class RecentTasksTest extends ActivityTestsBase { } private TaskRecord createDocumentTask(String className) { + return createDocumentTask(className, 0); + } + + private TaskRecord createDocumentTask(String className, int flags) { TaskRecord task = createTaskBuilder(className) - .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT) + .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT | flags) .build(); task.affinity = null; + task.maxRecents = ActivityManager.getMaxAppRecentsLimitStatic(); return task; } diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java index 5520bd71ad57..c91e22f84a07 100644 --- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java @@ -17,33 +17,46 @@ package com.android.server.am; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.res.XmlResourceParser; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; +import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; +import android.util.Xml; +import com.android.frameworks.servicestests.R; import com.android.internal.app.IVoiceInteractor; import com.android.server.am.TaskRecord.TaskRecordFactory; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; +import java.io.OutputStream; +import java.io.Reader; +import java.nio.file.Files; import java.util.ArrayList; +import java.util.Comparator; /** * Tests for exercising {@link TaskRecord}. @@ -54,11 +67,33 @@ import java.util.ArrayList; @MediumTest @Presubmit @RunWith(AndroidJUnit4.class) -public class TaskRecordTests { +public class TaskRecordTests extends ActivityTestsBase { + + private static final String TASK_TAG = "task"; + + private ActivityManagerService mService; @Before public void setUp() throws Exception { + super.setUp(); TaskRecord.setTaskRecordFactory(null); + mService = createActivityManagerService(); + } + + @Test + public void testRestoreWindowedTask() throws Exception { + final TaskRecord expected = createTaskRecord(64); + expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100); + + final File serializedFile = serializeToFile(expected); + + try { + final TaskRecord actual = restoreFromFile(serializedFile); + assertEquals(expected.taskId, actual.taskId); + assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds); + } finally { + serializedFile.delete(); + } } @Test @@ -78,6 +113,38 @@ public class TaskRecordTests { assertTrue(factory.mCreated); } + private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException { + final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml"); + + try (final OutputStream os = new FileOutputStream(tmpFile)) { + final XmlSerializer serializer = Xml.newSerializer(); + serializer.setOutput(os, "UTF-8"); + serializer.startDocument(null, true); + serializer.startTag(null, TASK_TAG); + r.saveToXml(serializer); + serializer.endTag(null, TASK_TAG); + serializer.endDocument(); + } + + return tmpFile; + } + + private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException { + try (final Reader reader = new BufferedReader(new FileReader(file))) { + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(reader); + assertEquals(XmlPullParser.START_TAG, parser.next()); + assertEquals(TASK_TAG, parser.getName()); + return TaskRecord.restoreFromXml(parser, mService.mStackSupervisor); + } + } + + private TaskRecord createTaskRecord(int taskId) { + return new TaskRecord(mService, taskId, new Intent(), null, null, null, null, null, false, + false, false, 0, 10050, null, new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, + null, 0, false, false, false, 0, 0); + } + private static class TestTaskRecordFactory extends TaskRecordFactory { private boolean mCreated = false; diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java index 362856c707c7..f4c54420853c 100644 --- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -117,7 +117,7 @@ public class BackupManagerServiceTest { "dataManagementLabel"); verify(mTransportManager) - .describeTransport( + .updateTransportAttributes( eq(TRANSPORT_COMPONENT), eq(TRANSPORT_NAME), eq(configurationIntent), @@ -247,7 +247,7 @@ public class BackupManagerServiceTest { null); verify(mTransportManager) - .describeTransport( + .updateTransportAttributes( eq(TRANSPORT_COMPONENT), eq(TRANSPORT_NAME), eq(configurationIntent), @@ -274,7 +274,7 @@ public class BackupManagerServiceTest { "dataManagementLabel"); verify(mTransportManager) - .describeTransport( + .updateTransportAttributes( eq(TRANSPORT_COMPONENT), eq(TRANSPORT_NAME), eq(configurationIntent), diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java new file mode 100644 index 000000000000..c918e8c7899d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locksettings.recoverablekeystore; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.Arrays; + +import javax.crypto.SecretKey; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class KeySyncUtilsTest { + private static final int RECOVERY_KEY_LENGTH_BITS = 256; + private static final int THM_KF_HASH_SIZE = 256; + private static final String SHA_256_ALGORITHM = "SHA-256"; + + @Test + public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception { + byte[] lockScreenHash = utf8Bytes("012345678910"); + + byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(lockScreenHash); + + assertArrayEquals(calculateSha256(utf8Bytes("THM_KF_hash012345678910")), thmKfHash); + } + + @Test + public void calculateThmKfHash_is256BitsLong() throws Exception { + byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(utf8Bytes("1234")); + + assertEquals(THM_KF_HASH_SIZE / Byte.SIZE, thmKfHash.length); + } + + @Test + public void generateRecoveryKey_returnsA256BitKey() throws Exception { + SecretKey key = KeySyncUtils.generateRecoveryKey(); + + assertEquals(RECOVERY_KEY_LENGTH_BITS / Byte.SIZE, key.getEncoded().length); + } + + @Test + public void generateRecoveryKey_generatesANewKeyEachTime() throws Exception { + SecretKey a = KeySyncUtils.generateRecoveryKey(); + SecretKey b = KeySyncUtils.generateRecoveryKey(); + + assertFalse(Arrays.equals(a.getEncoded(), b.getEncoded())); + } + + private static byte[] utf8Bytes(String s) { + return s.getBytes(StandardCharsets.UTF_8); + } + + private static byte[] calculateSha256(byte[] bytes) throws Exception { + MessageDigest messageDigest = MessageDigest.getInstance(SHA_256_ALGORITHM); + messageDigest.update(bytes); + return messageDigest.digest(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImplTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImplTest.java new file mode 100644 index 000000000000..fb4e75e59d8f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImplTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locksettings.recoverablekeystore; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import android.security.keystore.KeyProperties; +import android.security.keystore.KeyProtection; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.KeyStoreException; +import java.util.Random; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RecoverableKeyStorageImplTest { + private static final String KEY_ALGORITHM = "AES"; + private static final int GCM_TAG_LENGTH_BYTES = 16; + private static final int BITS_PER_BYTE = 8; + private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE; + private static final int GCM_NONCE_LENGTH_BYTES = 12; + private static final String TEST_KEY_ALIAS = "RecoverableKeyStorageImplTestKey"; + private static final int KEYSTORE_UID_SELF = -1; + + private RecoverableKeyStorageImpl mRecoverableKeyStorage; + + @Before + public void setUp() throws Exception { + mRecoverableKeyStorage = RecoverableKeyStorageImpl.newInstance( + /*userId=*/ KEYSTORE_UID_SELF); + } + + @After + public void tearDown() { + try { + mRecoverableKeyStorage.removeFromAndroidKeyStore(TEST_KEY_ALIAS); + } catch (KeyStoreException e) { + // Do nothing. + } + } + + @Test + public void loadFromAndroidKeyStore_loadsAKeyThatWasImported() throws Exception { + SecretKey key = generateKey(); + mRecoverableKeyStorage.importIntoAndroidKeyStore( + TEST_KEY_ALIAS, + key, + getKeyProperties()); + + assertKeysAreEquivalent( + key, mRecoverableKeyStorage.loadFromAndroidKeyStore(TEST_KEY_ALIAS)); + } + + @Test + public void importIntoAndroidKeyStore_importsWithKeyProperties() throws Exception { + mRecoverableKeyStorage.importIntoAndroidKeyStore( + TEST_KEY_ALIAS, + generateKey(), + getKeyProperties()); + + SecretKey key = mRecoverableKeyStorage.loadFromAndroidKeyStore(TEST_KEY_ALIAS); + + Mac mac = Mac.getInstance("HmacSHA256"); + try { + // Fails because missing PURPOSE_SIGN or PURPOSE_VERIFY + mac.init(key); + fail("Was able to initialize Mac with an ENCRYPT/DECRYPT-only key."); + } catch (InvalidKeyException e) { + // expect exception + } + } + + @Test + public void removeFromAndroidKeyStore_removesAnEntry() throws Exception { + mRecoverableKeyStorage.importIntoAndroidKeyStore( + TEST_KEY_ALIAS, + generateKey(), + getKeyProperties()); + + mRecoverableKeyStorage.removeFromAndroidKeyStore(TEST_KEY_ALIAS); + + assertNull(mRecoverableKeyStorage.loadFromAndroidKeyStore(TEST_KEY_ALIAS)); + } + + private static KeyProtection getKeyProperties() { + return new KeyProtection.Builder( + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build(); + } + + /** + * Asserts that {@code b} key can decrypt data encrypted with {@code a} key. Otherwise throws. + */ + private static void assertKeysAreEquivalent(SecretKey a, SecretKey b) throws Exception { + byte[] plaintext = "doge".getBytes(StandardCharsets.UTF_8); + byte[] nonce = generateGcmNonce(); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, a, new GCMParameterSpec(GCM_TAG_LENGTH_BITS, nonce)); + byte[] encrypted = cipher.doFinal(plaintext); + + cipher.init(Cipher.DECRYPT_MODE, b, new GCMParameterSpec(GCM_TAG_LENGTH_BITS, nonce)); + byte[] decrypted = cipher.doFinal(encrypted); + + assertArrayEquals(decrypted, plaintext); + } + + /** + * Returns a new random GCM nonce. + */ + private static byte[] generateGcmNonce() { + Random random = new Random(); + byte[] nonce = new byte[GCM_NONCE_LENGTH_BYTES]; + random.nextBytes(nonce); + return nonce; + } + + private static SecretKey generateKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); + keyGenerator.init(/*keySize=*/ 256); + return keyGenerator.generateKey(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java index 4cd5631c9b8d..fa73722ea060 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java @@ -16,7 +16,9 @@ package com.android.server.locksettings.recoverablekeystore; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import android.security.keystore.AndroidKeyStoreSecretKey; import android.security.keystore.KeyGenParameterSpec; @@ -29,6 +31,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.security.KeyStore; +import java.util.HashMap; +import java.util.Map; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; @@ -70,6 +74,36 @@ public class WrappedKeyTest { assertEquals(rawKey, unwrappedKey); } + @Test + public void decryptWrappedKeys_decryptsWrappedKeys() throws Exception { + String alias = "karlin"; + SecretKey platformKey = generateAndroidKeyStoreKey(); + SecretKey appKey = generateKey(); + WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, appKey); + HashMap<String, WrappedKey> keysByAlias = new HashMap<>(); + keysByAlias.put(alias, wrappedKey); + + Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(platformKey, keysByAlias); + + assertEquals(1, unwrappedKeys.size()); + assertTrue(unwrappedKeys.containsKey(alias)); + assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).getEncoded()); + } + + @Test + public void decryptWrappedKeys_doesNotDieIfSomeKeysAreUnwrappable() throws Exception { + String alias = "karlin"; + SecretKey appKey = generateKey(); + WrappedKey wrappedKey = WrappedKey.fromSecretKey(generateKey(), appKey); + HashMap<String, WrappedKey> keysByAlias = new HashMap<>(); + keysByAlias.put(alias, wrappedKey); + + Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys( + generateAndroidKeyStoreKey(), keysByAlias); + + assertEquals(0, unwrappedKeys.size()); + } + private SecretKey generateKey() throws Exception { KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); keyGenerator.init(/*keySize=*/ 256); @@ -81,7 +115,8 @@ public class WrappedKeyTest { KEY_ALGORITHM, ANDROID_KEY_STORE_PROVIDER); keyGenerator.init(new KeyGenParameterSpec.Builder( - WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + WRAPPING_KEY_ALIAS, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index fafae6c71355..b073ee5f64ee 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -305,7 +305,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); - assertThat(testPkgSetting01.origPackage, is(nullValue())); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); final PackageUserState userState = testPkgSetting01.readUserState(0); @@ -339,7 +338,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); - assertThat(testPkgSetting01.origPackage, is(nullValue())); assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM)); assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)); final PackageUserState userState = testPkgSetting01.readUserState(0); @@ -419,7 +417,6 @@ public class PackageManagerSettingsTests { assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); - assertSame(testPkgSetting01.origPackage, originalPkgSetting01); // signatures object must be different assertNotSame(testPkgSetting01.signatures, originalSignatures); assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE)); @@ -457,7 +454,6 @@ public class PackageManagerSettingsTests { assertThat(testPkgSetting01.appId, is(0)); assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); - assertThat(testPkgSetting01.origPackage, is(nullValue())); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64")); @@ -506,7 +502,6 @@ public class PackageManagerSettingsTests { assertThat(testPkgSetting01.appId, is(10064)); assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); - assertThat(testPkgSetting01.origPackage, is(nullValue())); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64")); @@ -551,7 +546,6 @@ public class PackageManagerSettingsTests { assertThat(testPkgSetting01.appId, is(10064)); assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); - assertThat(testPkgSetting01.origPackage, is(nullValue())); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); @@ -658,8 +652,6 @@ public class PackageManagerSettingsTests { // oldCodePaths is _not_ copied // assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths); // assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths))); - assertSame(origPkgSetting.origPackage, testPkgSetting.origPackage); - assertThat(origPkgSetting.origPackage, is(testPkgSetting.origPackage)); assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName); assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName)); assertSame(origPkgSetting.pkg, testPkgSetting.pkg); diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 86ce90c03ae4..b792d821ae93 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -18,9 +18,14 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; import static android.app.usage.UsageEvents.Event.USER_INTERACTION; +import static android.app.usage.UsageStatsManager.REASON_DEFAULT; +import static android.app.usage.UsageStatsManager.REASON_FORCED; import static android.app.usage.UsageStatsManager.REASON_PREDICTED; +import static android.app.usage.UsageStatsManager.REASON_TIMEOUT; +import static android.app.usage.UsageStatsManager.REASON_USAGE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; @@ -79,6 +84,7 @@ public class AppStandbyControllerTests { private static final long RARE_THRESHOLD = 48 * HOUR_MS; private MyInjector mInjector; + private AppStandbyController mController; static class MyContextWrapper extends ContextWrapper { PackageManager mockPm = mock(PackageManager.class); @@ -237,24 +243,23 @@ public class AppStandbyControllerTests { public void setUp() throws Exception { MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext()); mInjector = new MyInjector(myContext, Looper.getMainLooper()); + mController = setupController(); } @Test public void testCharging() throws Exception { - AppStandbyController controller = setupController(); - - setChargingState(controller, true); + setChargingState(mController, true); mInjector.mElapsedRealtime = RARE_THRESHOLD + 1; - assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID, + assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime, false)); - setChargingState(controller, false); + setChargingState(mController, false); mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2; - controller.checkIdleStates(USER_ID); - assertTrue(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID, + mController.checkIdleStates(USER_ID); + assertTrue(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime, false)); - setChargingState(controller, true); - assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID, + setChargingState(mController, true); + assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID, mInjector.mElapsedRealtime, false)); } @@ -282,112 +287,142 @@ public class AppStandbyControllerTests { @Test public void testBuckets() throws Exception { - AppStandbyController controller = setupController(); - - assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER); + assertTimeout(mController, 0, STANDBY_BUCKET_NEVER); - reportEvent(controller, USER_INTERACTION, 0); + reportEvent(mController, USER_INTERACTION, 0); // ACTIVE bucket - assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE); + assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE); // WORKING_SET bucket - assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET); + assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET); // WORKING_SET bucket - assertTimeout(controller, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET); + assertTimeout(mController, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET); // FREQUENT bucket - assertTimeout(controller, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT); + assertTimeout(mController, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT); // RARE bucket - assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE); + assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE); - reportEvent(controller, USER_INTERACTION, RARE_THRESHOLD + 1); + reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1); - assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE); + assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE); // RARE bucket - assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); + assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); } @Test public void testScreenTimeAndBuckets() throws Exception { - AppStandbyController controller = setupController(); mInjector.setDisplayOn(false); - assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER); + assertTimeout(mController, 0, STANDBY_BUCKET_NEVER); - reportEvent(controller, USER_INTERACTION, 0); + reportEvent(mController, USER_INTERACTION, 0); // ACTIVE bucket - assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE); + assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE); // WORKING_SET bucket - assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET); + assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET); // RARE bucket, should fail because the screen wasn't ON. mInjector.mElapsedRealtime = RARE_THRESHOLD + 1; - controller.checkIdleStates(USER_ID); - assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller)); + mController.checkIdleStates(USER_ID); + assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController)); mInjector.setDisplayOn(true); - assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); + assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); } @Test public void testForcedIdle() throws Exception { - AppStandbyController controller = setupController(); - setChargingState(controller, false); + setChargingState(mController, false); - controller.forceIdleState(PACKAGE_1, USER_ID, true); - assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller)); - assertTrue(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + mController.forceIdleState(PACKAGE_1, USER_ID, true); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); - controller.forceIdleState(PACKAGE_1, USER_ID, false); - assertEquals(STANDBY_BUCKET_ACTIVE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0, + mController.forceIdleState(PACKAGE_1, USER_ID, false); + assertEquals(STANDBY_BUCKET_ACTIVE, mController.getAppStandbyBucket(PACKAGE_1, USER_ID, 0, true)); - assertFalse(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); } @Test public void testNotificationEvent() throws Exception { - AppStandbyController controller = setupController(); - setChargingState(controller, false); + setChargingState(mController, false); - reportEvent(controller, USER_INTERACTION, 0); - assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller)); + reportEvent(mController, USER_INTERACTION, 0); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); mInjector.mElapsedRealtime = 1; - reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime); - assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller)); + reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); - controller.forceIdleState(PACKAGE_1, USER_ID, true); - reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime); - assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller)); + mController.forceIdleState(PACKAGE_1, USER_ID, true); + reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime); + assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController)); } @Test public void testPredictionTimedout() throws Exception { - AppStandbyController controller = setupController(); - setChargingState(controller, false); - controller.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, - REASON_PREDICTED + "CTS", 1 * HOUR_MS); + setChargingState(mController, false); + // Set it to timeout or usage, so that prediction can override it + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_TIMEOUT, 1 * HOUR_MS); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController)); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_PREDICTED + ":CTS", 1 * HOUR_MS); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); // Fast forward 12 hours mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD; - controller.checkIdleStates(USER_ID); + mController.checkIdleStates(USER_ID); // Should still be in predicted bucket, since prediction timeout is 1 day since prediction - assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller)); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); // Fast forward two more hours mInjector.mElapsedRealtime += 2 * HOUR_MS; - controller.checkIdleStates(USER_ID); + mController.checkIdleStates(USER_ID); // Should have now applied prediction timeout - assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller)); + assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController)); // Fast forward RARE bucket mInjector.mElapsedRealtime += RARE_THRESHOLD; - controller.checkIdleStates(USER_ID); + mController.checkIdleStates(USER_ID); // Should continue to apply prediction timeout - assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller)); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController)); + } + + @Test + public void testOverrides() throws Exception { + setChargingState(mController, false); + // Can force to NEVER + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, + REASON_FORCED, 1 * HOUR_MS); + assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController)); + + // Prediction can't override FORCED reason + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, + REASON_FORCED, 1 * HOUR_MS); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_PREDICTED, 1 * HOUR_MS); + assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController)); + + // Prediction can't override NEVER + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, + REASON_DEFAULT, 2 * HOUR_MS); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_PREDICTED, 2 * HOUR_MS); + assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController)); + + // Prediction can't set to NEVER + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_USAGE, 2 * HOUR_MS); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, + REASON_PREDICTED, 2 * HOUR_MS); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java index 89447a912c21..6070516669b5 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -33,6 +33,7 @@ import android.view.InputChannel; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + /** * Tests for the {@link TaskPositioningController} class. * @@ -78,9 +79,9 @@ public class TaskPositioningControllerTests extends WindowTestsBase { assertNotNull(mTarget.getDragWindowHandleLocked()); } - assertTrue(sWm.mH.runWithScissors(() -> { - mTarget.finishPositioning(); - }, TIMEOUT_MS)); + mTarget.finishTaskPositioning(); + // Wait until the looper processes finishTaskPositioning. + assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS)); assertFalse(mTarget.isPositioningLocked()); assertNull(mTarget.getDragWindowHandleLocked()); @@ -99,15 +100,17 @@ public class TaskPositioningControllerTests extends WindowTestsBase { assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow()); mTarget.handleTapOutsideTask(content, 0, 0); + // Wait until the looper processes finishTaskPositioning. + assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS)); synchronized (sWm.mWindowMap) { assertTrue(mTarget.isPositioningLocked()); assertNotNull(mTarget.getDragWindowHandleLocked()); } - assertTrue(sWm.mH.runWithScissors(() -> { - mTarget.finishPositioning(); - }, TIMEOUT_MS)); + mTarget.finishTaskPositioning(); + // Wait until the looper processes finishTaskPositioning. + assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS)); assertFalse(mTarget.isPositioningLocked()); assertNull(mTarget.getDragWindowHandleLocked()); diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 46efbd059d52..cc0259ddaa2b 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -24,6 +24,7 @@ import static android.app.usage.UsageStatsManager.REASON_USAGE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; @@ -133,7 +134,7 @@ public class AppStandbyController { @GuardedBy("mAppIdleLock") private AppIdleHistory mAppIdleHistory; - @GuardedBy("mAppIdleLock") + @GuardedBy("mPackageAccessListeners") private ArrayList<AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>(); @@ -592,7 +593,7 @@ public class AppStandbyController { } void addListener(AppIdleStateChangeListener listener) { - synchronized (mAppIdleLock) { + synchronized (mPackageAccessListeners) { if (!mPackageAccessListeners.contains(listener)) { mPackageAccessListeners.add(listener); } @@ -600,7 +601,7 @@ public class AppStandbyController { } void removeListener(AppIdleStateChangeListener listener) { - synchronized (mAppIdleLock) { + synchronized (mPackageAccessListeners) { mPackageAccessListeners.remove(listener); } } @@ -789,6 +790,19 @@ public class AppStandbyController { void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, String reason, long elapsedRealtime) { synchronized (mAppIdleLock) { + AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName, + userId, elapsedRealtime); + boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED); + // Don't allow changing bucket if higher than ACTIVE + if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return; + // Don't allow prediction to change from or to NEVER + if ((app.currentBucket == STANDBY_BUCKET_NEVER + || newBucket == STANDBY_BUCKET_NEVER) + && predicted) { + return; + } + // If the bucket was forced, don't allow prediction to override + if (app.bucketingReason.equals(REASON_FORCED) && predicted) return; mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason); } @@ -852,15 +866,19 @@ public class AppStandbyController { void informListeners(String packageName, int userId, int bucket) { final boolean idle = bucket >= STANDBY_BUCKET_RARE; - for (AppIdleStateChangeListener listener : mPackageAccessListeners) { - listener.onAppIdleStateChanged(packageName, userId, idle, bucket); + synchronized (mPackageAccessListeners) { + for (AppIdleStateChangeListener listener : mPackageAccessListeners) { + listener.onAppIdleStateChanged(packageName, userId, idle, bucket); + } } } void informParoleStateChanged() { final boolean paroled = isParoledOrCharging(); - for (AppIdleStateChangeListener listener : mPackageAccessListeners) { - listener.onParoleStateChanged(paroled); + synchronized (mPackageAccessListeners) { + for (AppIdleStateChangeListener listener : mPackageAccessListeners) { + listener.onParoleStateChanged(paroled); + } } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 15284d5e3dec..07c860b54413 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -728,6 +728,10 @@ public class UsageStatsService extends SystemService implements } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } + final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID; + final String reason = shellCaller + ? UsageStatsManager.REASON_FORCED + : UsageStatsManager.REASON_PREDICTED + ":" + callingUid; final long token = Binder.clearCallingIdentity(); try { // Caller cannot set their own standby state @@ -735,8 +739,7 @@ public class UsageStatsService extends SystemService implements PackageManager.MATCH_ANY_USER, userId) == callingUid) { throw new IllegalArgumentException("Cannot set your own standby bucket"); } - mAppStandby.setAppStandbyBucket(packageName, userId, bucket, - UsageStatsManager.REASON_PREDICTED + ":" + callingUid, + mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason, SystemClock.elapsedRealtime()); } finally { Binder.restoreCallingIdentity(token); @@ -779,6 +782,10 @@ public class UsageStatsService extends SystemService implements } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } + final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID; + final String reason = shellCaller + ? UsageStatsManager.REASON_FORCED + : UsageStatsManager.REASON_PREDICTED + ":" + callingUid; final long token = Binder.clearCallingIdentity(); try { final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -796,8 +803,7 @@ public class UsageStatsService extends SystemService implements PackageManager.MATCH_ANY_USER, userId) == callingUid) { throw new IllegalArgumentException("Cannot set your own standby bucket"); } - mAppStandby.setAppStandbyBucket(packageName, userId, bucket, - UsageStatsManager.REASON_PREDICTED + ":" + callingUid, + mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason, elapsedRealtime); } } finally { diff --git a/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java new file mode 100644 index 000000000000..5d16dd5b30ee --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal; + +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; + +import com.android.ims.ImsCallProfile; +import com.android.ims.ImsConferenceState; +import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsStreamMediaProfile; +import com.android.ims.ImsSuppServiceNotification; +import com.android.ims.internal.ImsCallSession; + +/** + * Proxy class for interfacing with the framework's Call session for an ongoing IMS call. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsCallSessionListener maintained by other ImsServices. + * + * @hide + */ +public class ImsCallSessionListener { + + private final IImsCallSessionListener mListener; + + public ImsCallSessionListener(IImsCallSessionListener l) { + mListener = l; + } + + /** + * Called when a request is sent out to initiate a new session + * and 1xx response is received from the network. + */ + public void callSessionProgressing(ImsStreamMediaProfile profile) + throws RemoteException { + mListener.callSessionProgressing(profile); + } + + /** + * Called when the session is initiated. + * + * @param profile the associated {@link ImsCallSession}. + */ + public void callSessionInitiated(ImsCallProfile profile) throws RemoteException { + mListener.callSessionInitiated(profile); + } + + /** + * Called when the session establishment has failed. + * + * @param reasonInfo detailed reason of the session establishment failure + */ + public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionInitiatedFailed(reasonInfo); + } + + /** + * Called when the session is terminated. + * + * @param reasonInfo detailed reason of the session termination + */ + public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionTerminated(reasonInfo); + } + + /** + * Called when the session is on hold. + */ + public void callSessionHeld(ImsCallProfile profile) throws RemoteException { + mListener.callSessionHeld(profile); + } + + /** + * Called when the session hold has failed. + * + * @param reasonInfo detailed reason of the session hold failure + */ + public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHoldFailed(reasonInfo); + } + + /** + * Called when the session hold is received from the remote user. + */ + public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionHoldReceived(profile); + } + + /** + * Called when the session resume is done. + */ + public void callSessionResumed(ImsCallProfile profile) throws RemoteException { + mListener.callSessionResumed(profile); + } + + /** + * Called when the session resume has failed. + * + * @param reasonInfo detailed reason of the session resume failure + */ + public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionResumeFailed(reasonInfo); + } + + /** + * Called when the session resume is received from the remote user. + */ + public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionResumeReceived(profile); + } + + /** + * Called when the session merge has been started. At this point, the {@code newSession} + * represents the session which has been initiated to the IMS conference server for the + * new merged conference. + * + * @param newSession the session object that is merged with an active & hold session + */ + public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile) + throws RemoteException { + mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null, + profile); + } + + /** + * Called when the session merge is successful and the merged session is active. + * + * @param newSession the new session object that is used for the conference + */ + public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException { + mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null); + } + + /** + * Called when the session merge has failed. + * + * @param reasonInfo detailed reason of the call merge failure + */ + public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionMergeFailed(reasonInfo); + } + + /** + * Called when the session is updated (except for hold/unhold). + */ + public void callSessionUpdated(ImsCallProfile profile) throws RemoteException { + mListener.callSessionUpdated(profile); + } + + /** + * Called when the session update has failed. + * + * @param reasonInfo detailed reason of the session update failure + */ + public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionUpdateFailed(reasonInfo); + } + + /** + * Called when the session update is received from the remote user. + */ + public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionUpdateReceived(profile); + } + + /** + * Called when the session has been extended to a conference session. + * + * @param newSession the session object that is extended to the conference + * from the active session + */ + public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile) + throws RemoteException { + mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null, + profile); + } + + /** + * Called when the conference extension has failed. + * + * @param reasonInfo detailed reason of the conference extension failure + */ + public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionConferenceExtendFailed(reasonInfo); + } + + /** + * Called when the conference extension is received from the remote user. + */ + public void callSessionConferenceExtendReceived(ImsCallSession newSession, + ImsCallProfile profile) throws RemoteException { + mListener.callSessionConferenceExtendReceived(newSession != null + ? newSession.getSession() : null, profile); + } + + /** + * Called when the invitation request of the participants is delivered to the conference + * server. + */ + public void callSessionInviteParticipantsRequestDelivered() throws RemoteException { + mListener.callSessionInviteParticipantsRequestDelivered(); + } + + /** + * Called when the invitation request of the participants has failed. + * + * @param reasonInfo detailed reason of the conference invitation failure + */ + public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) + throws RemoteException { + mListener.callSessionInviteParticipantsRequestFailed(reasonInfo); + } + + /** + * Called when the removal request of the participants is delivered to the conference + * server. + */ + public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException { + mListener.callSessionRemoveParticipantsRequestDelivered(); + } + + /** + * Called when the removal request of the participants has failed. + * + * @param reasonInfo detailed reason of the conference removal failure + */ + public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) + throws RemoteException { + mListener.callSessionInviteParticipantsRequestFailed(reasonInfo); + } + + /** + * Notifies the framework of the updated Call session conference state. + * + * @param state the new {@link ImsConferenceState} associated with the conference. + */ + public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException { + mListener.callSessionConferenceStateUpdated(state); + } + + /** + * Notifies the incoming USSD message. + */ + public void callSessionUssdMessageReceived(int mode, String ussdMessage) + throws RemoteException { + mListener.callSessionUssdMessageReceived(mode, ussdMessage); + } + + /** + * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially + * handover from one radio technology to another. + * + * @param srcAccessTech The source radio access technology; one of the access technology + * constants defined in {@link android.telephony.ServiceState}. For + * example + * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}. + * @param targetAccessTech The target radio access technology; one of the access technology + * constants defined in {@link android.telephony.ServiceState}. For + * example + * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}. + */ + public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) + throws RemoteException { + mListener.callSessionMayHandover(srcAccessTech, targetAccessTech); + } + + /** + * Called when session access technology changes. + * + * @param srcAccessTech original access technology + * @param targetAccessTech new access technology + * @param reasonInfo + */ + public void callSessionHandover(int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo); + } + + /** + * Called when session access technology change fails. + * + * @param srcAccessTech original access technology + * @param targetAccessTech new access technology + * @param reasonInfo handover failure reason + */ + public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo); + } + + /** + * Called when the TTY mode is changed by the remote party. + * + * @param mode one of the following: - + * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} + */ + public void callSessionTtyModeReceived(int mode) throws RemoteException { + mListener.callSessionTtyModeReceived(mode); + } + + /** + * Called when the multiparty state is changed for this {@code ImsCallSession}. + * + * @param isMultiParty {@code true} if the session became multiparty, + * {@code false} otherwise. + */ + + public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException { + mListener.callSessionMultipartyStateChanged(isMultiParty); + } + + /** + * Called when the supplementary service information is received for the current session. + */ + public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification) + throws RemoteException { + mListener.callSessionSuppServiceReceived(suppSrvNotification); + } + + /** + * Received RTT modify request from the remote party. + * + * @param callProfile ImsCallProfile with updated attributes + */ + public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) + throws RemoteException { + mListener.callSessionRttModifyRequestReceived(callProfile); + } + + /** + * @param status the received response for RTT modify request. + */ + public void callSessionRttModifyResponseReceived(int status) throws RemoteException { + mListener.callSessionRttModifyResponseReceived(status); + } + + /** + * Device received RTT message from Remote UE. + * + * @param rttMessage RTT message received + */ + public void callSessionRttMessageReceived(String rttMessage) throws RemoteException { + mListener.callSessionRttMessageReceived(rttMessage); + } +} + diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java new file mode 100644 index 000000000000..b7c8ca0f9799 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/ImsService.java @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.CarrierConfigManager; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsRcsFeature; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsServiceController; +import android.telephony.ims.internal.aidl.IImsServiceControllerListener; +import android.telephony.ims.internal.feature.ImsFeature; +import android.telephony.ims.internal.feature.MmTelFeature; +import android.telephony.ims.internal.feature.RcsFeature; +import android.telephony.ims.internal.stub.ImsConfigImplBase; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.util.Log; +import android.util.SparseArray; + +import com.android.ims.internal.IImsFeatureStatusCallback; +import com.android.internal.annotations.VisibleForTesting; + +/** + * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend + * ImsService must register the service in their AndroidManifest to be detected by the framework. + * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE" + * permission. Then, the ImsService definition in the manifest must follow the following format: + * + * ... + * <service android:name=".EgImsService" + * android:permission="android.permission.BIND_IMS_SERVICE" > + * <!-- Apps must declare which features they support as metadata. The different categories are + * defined below. In this example, the RCS_FEATURE feature is supported. --> + * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" /> + * <intent-filter> + * <action android:name="android.telephony.ims.ImsService" /> + * </intent-filter> + * </service> + * ... + * + * The telephony framework will then bind to the ImsService you have defined in your manifest + * if you are either: + * 1) Defined as the default ImsService for the device in the device overlay using + * "config_ims_package". + * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using + * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}. + * + * The features that are currently supported in an ImsService are: + * - RCS_FEATURE: This ImsService implements the RcsFeature class. + * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class. + * @hide + */ +public class ImsService extends Service { + + private static final String LOG_TAG = "ImsService"; + + /** + * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. + * @hide + */ + public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService"; + + // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that + // slot. + // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and + // call ImsFeature#onFeatureRemoved. + private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>(); + + private IImsServiceControllerListener mListener; + + + /** + * Listener that notifies the framework of ImsService changes. + */ + public static class Listener extends IImsServiceControllerListener.Stub { + /** + * The IMS features that this ImsService supports has changed. + * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s + * that this ImsService supports. This may trigger the addition/removal of feature + * in this service. + */ + public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) { + } + } + + /** + * @hide + */ + protected final IBinder mImsServiceController = new IImsServiceController.Stub() { + @Override + public void setListener(IImsServiceControllerListener l) { + mListener = l; + } + + @Override + public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) { + return createMmTelFeatureInternal(slotId, c); + } + + @Override + public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) { + return createRcsFeatureInternal(slotId, c); + } + + @Override + public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) + throws RemoteException { + ImsService.this.removeImsFeature(slotId, featureType, c); + } + + @Override + public ImsFeatureConfiguration querySupportedImsFeatures() { + return ImsService.this.querySupportedImsFeatures(); + } + + @Override + public void notifyImsServiceReadyForFeatureCreation() { + ImsService.this.readyForFeatureCreation(); + } + + @Override + public void notifyImsFeatureReady(int slotId, int featureType) + throws RemoteException { + ImsService.this.notifyImsFeatureReady(slotId, featureType); + } + + @Override + public IImsConfig getConfig(int slotId) throws RemoteException { + ImsConfigImplBase c = ImsService.this.getConfig(slotId); + return c != null ? c.getBinder() : null; + } + + @Override + public IImsRegistration getRegistration(int slotId) throws RemoteException { + ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId); + return r != null ? r.getBinder() : null; + } + }; + + /** + * @hide + */ + @Override + public IBinder onBind(Intent intent) { + if(SERVICE_INTERFACE.equals(intent.getAction())) { + Log.i(LOG_TAG, "ImsService Bound."); + return mImsServiceController; + } + return null; + } + + /** + * @hide + */ + @VisibleForTesting + public SparseArray<ImsFeature> getFeatures(int slotId) { + return mFeaturesBySlot.get(slotId); + } + + private IImsMmTelFeature createMmTelFeatureInternal(int slotId, + IImsFeatureStatusCallback c) { + MmTelFeature f = createMmTelFeature(slotId); + if (f != null) { + setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c); + return f.getBinder(); + } else { + Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); + return null; + } + } + + private IImsRcsFeature createRcsFeatureInternal(int slotId, + IImsFeatureStatusCallback c) { + RcsFeature f = createRcsFeature(slotId); + if (f != null) { + setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c); + return f.getBinder(); + } else { + Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned."); + return null; + } + } + + private void setupFeature(ImsFeature f, int slotId, int featureType, + IImsFeatureStatusCallback c) { + f.addImsFeatureStatusCallback(c); + f.initialize(this, slotId); + addImsFeature(slotId, featureType, f); + } + + private void addImsFeature(int slotId, int featureType, ImsFeature f) { + synchronized (mFeaturesBySlot) { + // Get SparseArray for Features, by querying slot Id + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + // Populate new SparseArray of features if it doesn't exist for this slot yet. + features = new SparseArray<>(); + mFeaturesBySlot.put(slotId, features); + } + features.put(featureType, f); + } + } + + private void removeImsFeature(int slotId, int featureType, + IImsFeatureStatusCallback c) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot " + + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f == null) { + Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type " + + featureType + " exists on slot " + slotId); + return; + } + f.removeImsFeatureStatusCallback(c); + f.onFeatureRemoved(); + features.remove(featureType); + } + } + + private void notifyImsFeatureReady(int slotId, int featureType) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " + + "slot " + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f == null) { + Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type " + + featureType + " exists on slot " + slotId); + return; + } + f.onFeatureReady(); + } + } + + /** + * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently + * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond + * to the {@link ImsFeature.FeatureType}s configured here. + * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports, + * defined in {@link ImsFeature.FeatureType}. + */ + public ImsFeatureConfiguration querySupportedImsFeatures() { + // Return empty for base implementation + return new ImsFeatureConfiguration(); + } + + /** + * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated + * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may + * trigger the framework to add/remove new ImsFeatures, depending on the configuration. + */ + public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) + throws RemoteException { + if (mListener == null) { + throw new IllegalStateException("Framework is not ready"); + } + mListener.onUpdateSupportedImsFeatures(c); + } + + /** + * The ImsService has been bound and is ready for ImsFeature creation based on the Features that + * the ImsService has registered for with the framework, either in the manifest or via + * The ImsService should use this signal instead of onCreate/onBind or similar to perform + * feature initialization because the framework may bind to this service multiple times to + * query the ImsService's {@link ImsFeatureConfiguration} via + * {@link #querySupportedImsFeatures()}before creating features. + */ + public void readyForFeatureCreation() { + } + + /** + * When called, the framework is requesting that a new MmTelFeature is created for the specified + * slot. + * + * @param slotId The slot ID that the MMTel Feature is being created for. + * @return The newly created MmTelFeature associated with the slot or null if the feature is not + * supported. + */ + public MmTelFeature createMmTelFeature(int slotId) { + return null; + } + + /** + * When called, the framework is requesting that a new RcsFeature is created for the specified + * slot + * + * @param slotId The slot ID that the RCS Feature is being created for. + * @return The newly created RcsFeature associated with the slot or null if the feature is not + * supported. + */ + public RcsFeature createRcsFeature(int slotId) { + return null; + } + + /** + * @param slotId The slot that the IMS configuration is associated with. + * @return ImsConfig implementation that is associated with the specified slot. + */ + public ImsConfigImplBase getConfig(int slotId) { + return new ImsConfigImplBase(); + } + + /** + * @param slotId The slot that is associated with the IMS Registration. + * @return the ImsRegistration implementation associated with the slot. + */ + public ImsRegistrationImplBase getRegistration(int slotId) { + return new ImsRegistrationImplBase(); + } +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl new file mode 100644 index 000000000000..2fb67442fa34 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import com.android.ims.ImsStreamMediaProfile; +import com.android.ims.ImsCallProfile; +import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsConferenceState; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.ImsSuppServiceNotification; + +/** + * A listener type for receiving notification on IMS call session events. + * When an event is generated for an {@link IImsCallSession}, the application is notified + * by having one of the methods called on the {@link IImsCallSessionListener}. + * {@hide} + */ +oneway interface IImsCallSessionListener { + /** + * Notifies the result of the basic session operation (setup / terminate). + */ + void callSessionProgressing(in ImsStreamMediaProfile profile); + void callSessionInitiated(in ImsCallProfile profile); + void callSessionInitiatedFailed(in ImsReasonInfo reasonInfo); + void callSessionTerminated(in ImsReasonInfo reasonInfo); + + /** + * Notifies the result of the call hold/resume operation. + */ + void callSessionHeld(in ImsCallProfile profile); + void callSessionHoldFailed(in ImsReasonInfo reasonInfo); + void callSessionHoldReceived(in ImsCallProfile profile); + void callSessionResumed(in ImsCallProfile profile); + void callSessionResumeFailed(in ImsReasonInfo reasonInfo); + void callSessionResumeReceived(in ImsCallProfile profile); + + /** + * Notifies the result of call merge operation. + */ + void callSessionMergeStarted(IImsCallSession newSession, in ImsCallProfile profile); + void callSessionMergeComplete(IImsCallSession session); + void callSessionMergeFailed(in ImsReasonInfo reasonInfo); + + /** + * Notifies the result of call upgrade / downgrade or any other call updates. + */ + void callSessionUpdated(in ImsCallProfile profile); + void callSessionUpdateFailed(in ImsReasonInfo reasonInfo); + void callSessionUpdateReceived(in ImsCallProfile profile); + + /** + * Notifies the result of conference extension. + */ + void callSessionConferenceExtended(IImsCallSession newSession, in ImsCallProfile profile); + void callSessionConferenceExtendFailed(in ImsReasonInfo reasonInfo); + void callSessionConferenceExtendReceived(IImsCallSession newSession, + in ImsCallProfile profile); + + /** + * Notifies the result of the participant invitation / removal to/from the conference session. + */ + void callSessionInviteParticipantsRequestDelivered(); + void callSessionInviteParticipantsRequestFailed(in ImsReasonInfo reasonInfo); + void callSessionRemoveParticipantsRequestDelivered(); + void callSessionRemoveParticipantsRequestFailed(in ImsReasonInfo reasonInfo); + + /** + * Notifies the changes of the conference info. in the conference session. + */ + void callSessionConferenceStateUpdated(in ImsConferenceState state); + + /** + * Notifies the incoming USSD message. + */ + void callSessionUssdMessageReceived(int mode, String ussdMessage); + + /** + * Notifies of handover information for this call + */ + void callSessionHandover(int srcAccessTech, int targetAccessTech, + in ImsReasonInfo reasonInfo); + void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech, + in ImsReasonInfo reasonInfo); + void callSessionMayHandover(int srcAccessTech, int targetAccessTech); + + /** + * Notifies the TTY mode change by remote party. + * @param mode one of the following: + * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} + */ + void callSessionTtyModeReceived(int mode); + + /** + * Notifies of a change to the multiparty state for this {@code ImsCallSession}. + * + * @param session The call session. + * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise. + */ + void callSessionMultipartyStateChanged(boolean isMultiParty); + + /** + * Notifies the supplementary service information for the current session. + */ + void callSessionSuppServiceReceived(in ImsSuppServiceNotification suppSrvNotification); + + /** + * Device received RTT modify request from Remote UE + * @param session ImsCallProfile with updated attribute + */ + void callSessionRttModifyRequestReceived(in ImsCallProfile callProfile); + + /* Device issued RTT modify request and inturn received response + * from Remote UE + * @param status Will be one of the following values from: + * - {@link Connection.RttModifyStatus} + */ + void callSessionRttModifyResponseReceived(int status); + + /* + * While in call, device received RTT message from Remote UE + * @param rttMessage Received RTT message + */ + void callSessionRttMessageReceived(in String rttMessage); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl new file mode 100644 index 000000000000..fd2eb24610ec --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +/** + * See ImsFeature#CapabilityCallback for more information. + * {@hide} + */ +oneway interface IImsCapabilityCallback { + void onQueryCapabilityConfiguration(int capability, int radioTech, boolean enabled); + void onChangeCapabilityConfigurationError(int capability, int radioTech, int reason); + void onCapabilitiesStatusChanged(int config); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl new file mode 100644 index 000000000000..3d424a33012d --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsConfigCallback; + +import com.android.ims.ImsConfigListener; + +/** + * Provides APIs to get/set the IMS service feature/capability/parameters. + * The config items include items provisioned by the operator. + * + * {@hide} + */ +interface IImsConfig { + + void addImsConfigCallback(IImsConfigCallback c); + void removeImsConfigCallback(IImsConfigCallback c); + int getConfigInt(int item); + String getConfigString(int item); + // Return result code defined in ImsConfig#OperationStatusConstants + int setConfigInt(int item, int value); + // Return result code defined in ImsConfig#OperationStatusConstants + int setConfigString(int item, String value); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl new file mode 100644 index 000000000000..52efd2322c17 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +/** + * Provides callback interface for ImsConfig when a value has changed. + * + * {@hide} + */ +oneway interface IImsConfigCallback { + void onIntConfigChanged(int item, int value); + void onStringConfigChanged(int item, String value); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl new file mode 100644 index 000000000000..712578117e44 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import android.os.Message; +import android.telephony.ims.internal.aidl.IImsMmTelListener; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; +import android.telephony.ims.internal.feature.CapabilityChangeRequest; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsUt; + +/** + * See MmTelFeature for more information. + * {@hide} + */ +interface IImsMmTelFeature { + void setListener(IImsMmTelListener l); + int getFeatureState(); + ImsCallProfile createCallProfile(int callSessionType, int callType); + IImsCallSession createCallSession(in ImsCallProfile profile, IImsCallSessionListener listener); + IImsUt getUtInterface(); + IImsEcbm getEcbmInterface(); + void setUiTtyMode(int uiTtyMode, in Message onCompleteMessage); + IImsMultiEndpoint getMultiEndpointInterface(); + int queryCapabilityStatus(); + oneway void addCapabilityCallback(IImsCapabilityCallback c); + oneway void removeCapabilityCallback(IImsCapabilityCallback c); + oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest request, + IImsCapabilityCallback c); + oneway void queryCapabilityConfiguration(int capability, int radioTech, + IImsCapabilityCallback c); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl new file mode 100644 index 000000000000..8332bc024e37 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import com.android.ims.internal.IImsCallSession; + +/** + * See MmTelFeature#Listener for more information. + * {@hide} + */ +oneway interface IImsMmTelListener { + void onIncomingCall(IImsCallSession c); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl new file mode 100644 index 000000000000..f6005b66bd3c --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +/** + * See RcsFeature for more information. + * {@hide} + */ +interface IImsRcsFeature { + //Empty Default Implementation +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl new file mode 100644 index 000000000000..687b7ca408d5 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsRegistrationCallback; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +/** + * See ImsRegistration for more information. + * + * {@hide} + */ +interface IImsRegistration { + int getRegistrationTechnology(); + oneway void addRegistrationCallback(IImsRegistrationCallback c); + oneway void removeRegistrationCallback(IImsRegistrationCallback c); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl new file mode 100644 index 000000000000..a50575b96865 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +import com.android.ims.ImsReasonInfo; + +/** + * See ImsRegistrationImplBase.Callback for more information. + * + * {@hide} + */ +oneway interface IImsRegistrationCallback { + void onRegistered(int imsRadioTech); + void onRegistering(int imsRadioTech); + void onDeregistered(in ImsReasonInfo info); + void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl new file mode 100644 index 000000000000..8afb95588b01 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsRcsFeature; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsServiceControllerListener; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +import com.android.ims.internal.IImsFeatureStatusCallback; + +/** + * See ImsService and MmTelFeature for more information. + * {@hide} + */ +interface IImsServiceController { + void setListener(IImsServiceControllerListener l); + IImsMmTelFeature createMmTelFeature(int slotId, in IImsFeatureStatusCallback c); + IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c); + ImsFeatureConfiguration querySupportedImsFeatures(); + // Synchronous call to ensure the ImsService is ready before continuing with feature creation. + void notifyImsServiceReadyForFeatureCreation(); + // Synchronous call to ensure the new ImsFeature is ready before using the Feature. + void notifyImsFeatureReady(int slotId, int featureType); + void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c); + IImsConfig getConfig(int slotId); + IImsRegistration getRegistration(int slotId); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl new file mode 100644 index 000000000000..01cca2db0978 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +/** + * See ImsService#Listener for more information. + * {@hide} + */ +oneway interface IImsServiceControllerListener { + void onUpdateSupportedImsFeatures(in ImsFeatureConfiguration c); +} diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl new file mode 100644 index 000000000000..f4ec0eb38f34 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +parcelable CapabilityChangeRequest; diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java new file mode 100644 index 000000000000..4d188734c10e --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.util.ArraySet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Request to send to IMS provider, which will try to enable/disable capabilities that are added to + * the request. + * {@hide} + */ +public class CapabilityChangeRequest implements Parcelable { + + public static class CapabilityPair { + private final int mCapability; + private final int radioTech; + + public CapabilityPair(int capability, int radioTech) { + this.mCapability = capability; + this.radioTech = radioTech; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CapabilityPair)) return false; + + CapabilityPair that = (CapabilityPair) o; + + if (getCapability() != that.getCapability()) return false; + return getRadioTech() == that.getRadioTech(); + } + + @Override + public int hashCode() { + int result = getCapability(); + result = 31 * result + getRadioTech(); + return result; + } + + public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() { + return mCapability; + } + + public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() { + return radioTech; + } + } + + // Pair contains <radio tech, mCapability> + private final Set<CapabilityPair> mCapabilitiesToEnable; + // Pair contains <radio tech, mCapability> + private final Set<CapabilityPair> mCapabilitiesToDisable; + + public CapabilityChangeRequest() { + mCapabilitiesToEnable = new ArraySet<>(); + mCapabilitiesToDisable = new ArraySet<>(); + } + + /** + * Add one or many capabilities to the request to be enabled. + * + * @param capabilities A bitfield of capabilities to enable, valid values are defined in + * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * @param radioTech the radio tech that these capabilities should be enabled for, valid + * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. + */ + public void addCapabilitiesToEnableForTech( + @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech); + } + + /** + * Add one or many capabilities to the request to be disabled. + * @param capabilities A bitfield of capabilities to diable, valid values are defined in + * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * @param radioTech the radio tech that these capabilities should be disabled for, valid + * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. + */ + public void addCapabilitiesToDisableForTech( + @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech); + } + + /** + * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled. + */ + public List<CapabilityPair> getCapabilitiesToEnable() { + return new ArrayList<>(mCapabilitiesToEnable); + } + + /** + * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled. + */ + public List<CapabilityPair> getCapabilitiesToDisable() { + return new ArrayList<>(mCapabilitiesToDisable); + } + + // Iterate through capabilities bitfield and add each one as a pair associated with the radio + // technology + private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) { + long highestCapability = Long.highestOneBit(capabilities); + for (int i = 1; i <= highestCapability; i *= 2) { + if ((i & capabilities) > 0) { + set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech)); + } + } + } + + protected CapabilityChangeRequest(Parcel in) { + int enableSize = in.readInt(); + mCapabilitiesToEnable = new ArraySet<>(enableSize); + for (int i = 0; i < enableSize; i++) { + mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(), + /*radioTech*/ in.readInt())); + } + int disableSize = in.readInt(); + mCapabilitiesToDisable = new ArraySet<>(disableSize); + for (int i = 0; i < disableSize; i++) { + mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(), + /*radioTech*/ in.readInt())); + } + } + + public static final Creator<CapabilityChangeRequest> CREATOR = + new Creator<CapabilityChangeRequest>() { + @Override + public CapabilityChangeRequest createFromParcel(Parcel in) { + return new CapabilityChangeRequest(in); + } + + @Override + public CapabilityChangeRequest[] newArray(int size) { + return new CapabilityChangeRequest[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mCapabilitiesToEnable.size()); + for (CapabilityPair pair : mCapabilitiesToEnable) { + dest.writeInt(pair.getCapability()); + dest.writeInt(pair.getRadioTech()); + } + dest.writeInt(mCapabilitiesToDisable.size()); + for (CapabilityPair pair : mCapabilitiesToDisable) { + dest.writeInt(pair.getCapability()); + dest.writeInt(pair.getRadioTech()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CapabilityChangeRequest)) return false; + + CapabilityChangeRequest that = (CapabilityChangeRequest) o; + + if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false; + return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable); + } + + @Override + public int hashCode() { + int result = mCapabilitiesToEnable.hashCode(); + result = 31 * result + mCapabilitiesToDisable.hashCode(); + return result; + } +} diff --git a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java new file mode 100644 index 000000000000..9f82ad241eaf --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; +import android.os.IInterface; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.SubscriptionManager; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.util.Log; + +import com.android.ims.internal.IImsFeatureStatusCallback; +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * Base class for all IMS features that are supported by the framework. + * + * @hide + */ +public abstract class ImsFeature { + + private static final String LOG_TAG = "ImsFeature"; + + /** + * Action to broadcast when ImsService is up. + * Internal use only. + * Only defined here separately for compatibility purposes with the old ImsService. + * + * @hide + */ + public static final String ACTION_IMS_SERVICE_UP = + "com.android.ims.IMS_SERVICE_UP"; + + /** + * Action to broadcast when ImsService is down. + * Internal use only. + * Only defined here separately for compatibility purposes with the old ImsService. + * + * @hide + */ + public static final String ACTION_IMS_SERVICE_DOWN = + "com.android.ims.IMS_SERVICE_DOWN"; + + /** + * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents. + * A long value; the phone ID corresponding to the IMS service coming up or down. + * Only defined here separately for compatibility purposes with the old ImsService. + * + * @hide + */ + public static final String EXTRA_PHONE_ID = "android:phone_id"; + + // Invalid feature value + public static final int FEATURE_INVALID = -1; + // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously + // defined values in ImsServiceClass for compatibility purposes. + public static final int FEATURE_EMERGENCY_MMTEL = 0; + public static final int FEATURE_MMTEL = 1; + public static final int FEATURE_RCS = 2; + // Total number of features defined + public static final int FEATURE_MAX = 3; + + // Integer values defining IMS features that are supported in ImsFeature. + @IntDef(flag = true, + value = { + FEATURE_EMERGENCY_MMTEL, + FEATURE_MMTEL, + FEATURE_RCS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FeatureType {} + + // Integer values defining the state of the ImsFeature at any time. + @IntDef(flag = true, + value = { + STATE_UNAVAILABLE, + STATE_INITIALIZING, + STATE_READY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsState {} + + public static final int STATE_UNAVAILABLE = 0; + public static final int STATE_INITIALIZING = 1; + public static final int STATE_READY = 2; + + // Integer values defining the result codes that should be returned from + // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability. + @IntDef(flag = true, + value = { + CAPABILITY_ERROR_GENERIC, + CAPABILITY_SUCCESS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsCapabilityError {} + + public static final int CAPABILITY_ERROR_GENERIC = -1; + public static final int CAPABILITY_SUCCESS = 0; + + + /** + * The framework implements this callback in order to register for Feature Capability status + * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability + * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error + * callbacks when the ImsService can not change the capability as requested, via + * {@link #onChangeCapabilityConfigurationError}. + */ + public static class CapabilityCallback extends IImsCapabilityCallback.Stub { + + @Override + public final void onCapabilitiesStatusChanged(int config) throws RemoteException { + onCapabilitiesStatusChanged(new Capabilities(config)); + } + + /** + * Returns the result of a query for the capability configuration of a requested capability. + * + * @param capability The capability that was requested. + * @param radioTech The IMS radio technology associated with the capability. + * @param isEnabled true if the capability is enabled, false otherwise. + */ + @Override + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + + } + + /** + * Called when a change to the capability configuration has returned an error. + * + * @param capability The capability that was requested to be changed. + * @param radioTech The IMS radio technology associated with the capability. + * @param reason error associated with the failure to change configuration. + */ + @Override + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + int reason) { + } + + /** + * The status of the feature's capabilities has changed to either available or unavailable. + * If unavailable, the feature is not able to support the unavailable capability at this + * time. + * + * @param config The new availability of the capabilities. + */ + public void onCapabilitiesStatusChanged(Capabilities config) { + } + } + + /** + * Used by the ImsFeature to call back to the CapabilityCallback that the framework has + * provided. + */ + protected static class CapabilityCallbackProxy { + private final IImsCapabilityCallback mCallback; + + public CapabilityCallbackProxy(IImsCapabilityCallback c) { + mCallback = c; + } + + /** + * This method notifies the provided framework callback that the request to change the + * indicated capability has failed and has not changed. + * + * @param capability The Capability that will be notified to the framework. + * @param radioTech The radio tech that this capability failed for. + * @param reason The reason this capability was unable to be changed. + */ + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + @ImsCapabilityError int reason) { + try { + mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason); + } catch (RemoteException e) { + Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder."); + } + } + + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + try { + mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled); + } catch (RemoteException e) { + Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder."); + } + } + } + + /** + * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask. + */ + public static class Capabilities { + protected int mCapabilities = 0; + + public Capabilities() { + } + + protected Capabilities(int capabilities) { + mCapabilities = capabilities; + } + + /** + * @param capabilities Capabilities to be added to the configuration in the form of a + * bit mask. + */ + public void addCapabilities(int capabilities) { + mCapabilities |= capabilities; + } + + /** + * @param capabilities Capabilities to be removed to the configuration in the form of a + * bit mask. + */ + public void removeCapabilities(int capabilities) { + mCapabilities &= ~capabilities; + } + + /** + * @return true if all of the capabilities specified are capable. + */ + public boolean isCapable(int capabilities) { + return (mCapabilities & capabilities) == capabilities; + } + + public Capabilities copy() { + return new Capabilities(mCapabilities); + } + + /** + * @return a bitmask containing the capability flags directly. + */ + public int getMask() { + return mCapabilities; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Capabilities)) return false; + + Capabilities that = (Capabilities) o; + + return mCapabilities == that.mCapabilities; + } + + @Override + public int hashCode() { + return mCapabilities; + } + } + + private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap( + new WeakHashMap<IImsFeatureStatusCallback, Boolean>()); + private @ImsState int mState = STATE_UNAVAILABLE; + private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; + private Context mContext; + private final Object mLock = new Object(); + private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks + = new RemoteCallbackList<>(); + private Capabilities mCapabilityStatus = new Capabilities(); + + public final void initialize(Context context, int slotId) { + mContext = context; + mSlotId = slotId; + } + + public final int getFeatureState() { + synchronized (mLock) { + return mState; + } + } + + protected final void setFeatureState(@ImsState int state) { + synchronized (mLock) { + if (mState != state) { + mState = state; + notifyFeatureState(state); + } + } + } + + // Not final for testing, but shouldn't be extended! + @VisibleForTesting + public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { + try { + // If we have just connected, send queued status. + c.notifyImsFeatureStatus(getFeatureState()); + // Add the callback if the callback completes successfully without a RemoteException. + synchronized (mLock) { + mStatusCallbacks.add(c); + } + } catch (RemoteException e) { + Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); + } + } + + @VisibleForTesting + // Not final for testing, but should not be extended! + public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { + synchronized (mLock) { + mStatusCallbacks.remove(c); + } + } + + /** + * Internal method called by ImsFeature when setFeatureState has changed. + */ + private void notifyFeatureState(@ImsState int state) { + synchronized (mLock) { + for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator(); + iter.hasNext(); ) { + IImsFeatureStatusCallback callback = iter.next(); + try { + Log.i(LOG_TAG, "notifying ImsFeatureState=" + state); + callback.notifyImsFeatureStatus(state); + } catch (RemoteException e) { + // remove if the callback is no longer alive. + iter.remove(); + Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); + } + } + } + sendImsServiceIntent(state); + } + + /** + * Provide backwards compatibility using deprecated service UP/DOWN intents. + */ + private void sendImsServiceIntent(@ImsState int state) { + if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + return; + } + Intent intent; + switch (state) { + case ImsFeature.STATE_UNAVAILABLE: + case ImsFeature.STATE_INITIALIZING: + intent = new Intent(ACTION_IMS_SERVICE_DOWN); + break; + case ImsFeature.STATE_READY: + intent = new Intent(ACTION_IMS_SERVICE_UP); + break; + default: + intent = new Intent(ACTION_IMS_SERVICE_DOWN); + } + intent.putExtra(EXTRA_PHONE_ID, mSlotId); + mContext.sendBroadcast(intent); + } + + public final void addCapabilityCallback(IImsCapabilityCallback c) { + mCapabilityCallbacks.register(c); + } + + public final void removeCapabilityCallback(IImsCapabilityCallback c) { + mCapabilityCallbacks.unregister(c); + } + + /** + * @return the cached capabilities status for this feature. + */ + @VisibleForTesting + public Capabilities queryCapabilityStatus() { + synchronized (mLock) { + return mCapabilityStatus.copy(); + } + } + + // Called internally to request the change of enabled capabilities. + @VisibleForTesting + public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request, + IImsCapabilityCallback c) throws RemoteException { + if (request == null) { + throw new IllegalArgumentException( + "ImsFeature#requestChangeEnabledCapabilities called with invalid params."); + } + changeEnabledCapabilities(request, new CapabilityCallbackProxy(c)); + } + + /** + * Called by the ImsFeature when the capabilities status has changed. + * + * @param c A {@link Capabilities} containing the new Capabilities status. + */ + protected final void notifyCapabilitiesStatusChanged(Capabilities c) { + synchronized (mLock) { + mCapabilityStatus = c.copy(); + } + int count = mCapabilityCallbacks.beginBroadcast(); + try { + for (int i = 0; i < count; i++) { + try { + mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged( + c.mCapabilities); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " + + "callback."); + } + } + } finally { + mCapabilityCallbacks.finishBroadcast(); + } + } + + /** + * Features should override this method to receive Capability preference change requests from + * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities + * in the {@link CapabilityChangeRequest} are not able to be completed due to an error, + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for + * each failed capability. + * + * @param request A {@link CapabilityChangeRequest} containing requested capabilities to + * enable/disable. + * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework + * setting a subset of these capabilities fail, using + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}. + */ + public abstract void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c); + + /** + * Called when the framework is removing this feature and it needs to be cleaned up. + */ + public abstract void onFeatureRemoved(); + + /** + * Called when the feature has been initialized and communication with the framework is set up. + * Any attempt by this feature to access the framework before this method is called will return + * with an {@link IllegalStateException}. + * The IMS provider should use this method to trigger registration for this feature on the IMS + * network, if needed. + */ + public abstract void onFeatureReady(); + + /** + * @return Binder instance that the framework will use to communicate with this feature. + */ + protected abstract IInterface getBinder(); +} diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java new file mode 100644 index 000000000000..f183a57e77fc --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +import android.annotation.IntDef; +import android.os.Message; +import android.os.RemoteException; +import android.telecom.TelecomManager; +import android.telephony.ims.internal.ImsCallSessionListener; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsMmTelListener; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.ImsEcbmImplBase; +import android.telephony.ims.stub.ImsMultiEndpointImplBase; +import android.telephony.ims.stub.ImsUtImplBase; +import android.util.Log; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsUt; +import com.android.ims.internal.ImsCallSession; +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support. + * + * Any class wishing to use MmTelFeature should extend this class and implement all methods that the + * service supports. + * @hide + */ + +public class MmTelFeature extends ImsFeature { + + private static final String LOG_TAG = "MmTelFeature"; + + private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() { + + @Override + public void setListener(IImsMmTelListener l) throws RemoteException { + synchronized (mLock) { + MmTelFeature.this.setListener(l); + } + } + + @Override + public int getFeatureState() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getFeatureState(); + } + } + + + @Override + public ImsCallProfile createCallProfile(int callSessionType, int callType) + throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.createCallProfile(callSessionType, callType); + } + } + + @Override + public IImsCallSession createCallSession(ImsCallProfile profile, + IImsCallSessionListener listener) throws RemoteException { + synchronized (mLock) { + ImsCallSession s = MmTelFeature.this.createCallSession(profile, + new ImsCallSessionListener(listener)); + return s != null ? s.getSession() : null; + } + } + + @Override + public IImsUt getUtInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getUt(); + } + } + + @Override + public IImsEcbm getEcbmInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getEcbm(); + } + } + + @Override + public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException { + synchronized (mLock) { + MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage); + } + } + + @Override + public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getMultiEndpoint(); + } + } + + @Override + public int queryCapabilityStatus() throws RemoteException { + return MmTelFeature.this.queryCapabilityStatus().mCapabilities; + } + + @Override + public void addCapabilityCallback(IImsCapabilityCallback c) { + MmTelFeature.this.addCapabilityCallback(c); + } + + @Override + public void removeCapabilityCallback(IImsCapabilityCallback c) { + MmTelFeature.this.removeCapabilityCallback(c); + } + + @Override + public void changeCapabilitiesConfiguration(CapabilityChangeRequest request, + IImsCapabilityCallback c) throws RemoteException { + MmTelFeature.this.requestChangeEnabledCapabilities(request, c); + } + + @Override + public void queryCapabilityConfiguration(int capability, int radioTech, + IImsCapabilityCallback c) { + queryCapabilityConfigurationInternal(capability, radioTech, c); + } + }; + + /** + * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask. + * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}. + * + * The capabilities of this MmTelFeature will be set by the framework and can be queried with + * {@link #queryCapabilityStatus()}. + * + * This MmTelFeature can then return the status of each of these capabilities (enabled or not) + * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current + * status can also be queried using {@link #queryCapabilityStatus()}. + */ + public static class MmTelCapabilities extends Capabilities { + + @VisibleForTesting + public MmTelCapabilities() { + super(); + } + + public MmTelCapabilities(Capabilities c) { + mCapabilities = c.mCapabilities; + } + + @IntDef(flag = true, + value = { + CAPABILITY_TYPE_VOICE, + CAPABILITY_TYPE_VIDEO, + CAPABILITY_TYPE_UT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MmTelCapability {} + + /** + * This MmTelFeature supports Voice calling (IR.92) + */ + public static final int CAPABILITY_TYPE_VOICE = 1 << 0; + + /** + * This MmTelFeature supports Video (IR.94) + */ + public static final int CAPABILITY_TYPE_VIDEO = 1 << 1; + + /** + * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92) + */ + public static final int CAPABILITY_TYPE_UT = 1 << 2; + + @Override + public final void addCapabilities(@MmTelCapability int capabilities) { + super.addCapabilities(capabilities); + } + + @Override + public final void removeCapabilities(@MmTelCapability int capability) { + super.removeCapabilities(capability); + } + + @Override + public final boolean isCapable(@MmTelCapability int capabilities) { + return super.isCapable(capabilities); + } + } + + /** + * Listener that the framework implements for communication from the MmTelFeature. + */ + public static class Listener extends IImsMmTelListener.Stub { + + @Override + public final void onIncomingCall(IImsCallSession c) { + onIncomingCall(new ImsCallSession(c)); + } + + /** + * Called when the IMS provider receives an incoming call. + * @param c The {@link ImsCallSession} associated with the new call. + */ + public void onIncomingCall(ImsCallSession c) { + } + } + + // Lock for feature synchronization + private final Object mLock = new Object(); + private IImsMmTelListener mListener; + + /** + * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and + * notifies the framework. + */ + private void setListener(IImsMmTelListener listener) { + synchronized (mLock) { + mListener = listener; + } + } + + private void queryCapabilityConfigurationInternal(int capability, int radioTech, + IImsCapabilityCallback c) { + boolean enabled = queryCapabilityConfiguration(capability, radioTech); + try { + if (c != null) { + c.onQueryCapabilityConfiguration(capability, radioTech, enabled); + } + } catch (RemoteException e) { + Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!"); + } + } + + /** + * The current capability status that this MmTelFeature has defined is available. This + * configuration will be used by the platform to figure out which capabilities are CURRENTLY + * available to be used. + * + * Should be a subset of the capabilities that are enabled by the framework in + * {@link #changeEnabledCapabilities}. + * @return A copy of the current MmTelFeature capability status. + */ + @Override + public final MmTelCapabilities queryCapabilityStatus() { + return new MmTelCapabilities(super.queryCapabilityStatus()); + } + + /** + * Notify the framework that the status of the Capabilities has changed. Even though the + * MmTelFeature capability may be enabled by the framework, the status may be disabled due to + * the feature being unavailable from the network. + * @param c The current capability status of the MmTelFeature. If a capability is disabled, then + * the status of that capability is disabled. This can happen if the network does not currently + * support the capability that is enabled. A capability that is disabled by the framework (via + * {@link #changeEnabledCapabilities}) should also show the status as disabled. + */ + protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) { + super.notifyCapabilitiesStatusChanged(c); + } + + /** + * Notify the framework of an incoming call. + * @param c The {@link ImsCallSession} of the new incoming call. + * + * @throws RemoteException if the connection to the framework is not available. If this happens, + * the call should be no longer considered active and should be cleaned up. + * */ + protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException { + synchronized (mLock) { + if (mListener == null) { + throw new IllegalStateException("Session is not available."); + } + mListener.onIncomingCall(c.getSession()); + } + } + + /** + * Provides the MmTelFeature with the ability to return the framework Capability Configuration + * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and + * includes a capability A to enable or disable, this method should return the correct enabled + * status for capability A. + * @param capability The capability that we are querying the configuration for. + * @return true if the capability is enabled, false otherwise. + */ + public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + // Base implementation - Override to provide functionality + return false; + } + + /** + * The MmTelFeature should override this method to handle the enabling/disabling of + * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes + * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities + * could not be set to their new values, + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called + * individually for each capability whose processing resulted in an error. + * + * Enabling/Disabling a capability here indicates that the capability should be registered or + * deregistered (depending on the capability change) and become available or unavailable to + * the framework. + */ + @Override + public void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c) { + // Base implementation, no-op + } + + /** + * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. + * + * @param callSessionType a service type that is specified in {@link ImsCallProfile} + * {@link ImsCallProfile#SERVICE_TYPE_NONE} + * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} + * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} + * @param callType a call type that is specified in {@link ImsCallProfile} + * {@link ImsCallProfile#CALL_TYPE_VOICE} + * {@link ImsCallProfile#CALL_TYPE_VT} + * {@link ImsCallProfile#CALL_TYPE_VT_TX} + * {@link ImsCallProfile#CALL_TYPE_VT_RX} + * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} + * {@link ImsCallProfile#CALL_TYPE_VS} + * {@link ImsCallProfile#CALL_TYPE_VS_TX} + * {@link ImsCallProfile#CALL_TYPE_VS_RX} + * @return a {@link ImsCallProfile} object + */ + public ImsCallProfile createCallProfile(int callSessionType, int callType) { + // Base Implementation - Should be overridden + return null; + } + + /** + * Creates an {@link ImsCallSession} with the specified call profile. + * Use other methods, if applicable, instead of interacting with + * {@link ImsCallSession} directly. + * + * @param profile a call profile to make the call + * @param listener An implementation of IImsCallSessionListener. + */ + public ImsCallSession createCallSession(ImsCallProfile profile, + ImsCallSessionListener listener) { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Ut interface for the supplementary service configuration. + */ + public ImsUtImplBase getUt() { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Emergency call-back mode interface for emergency VoLTE calls that support it. + */ + public ImsEcbmImplBase getEcbm() { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Emergency call-back mode interface for emergency VoLTE calls that support it. + */ + public ImsMultiEndpointImplBase getMultiEndpoint() { + // Base Implementation - Should be overridden + return null; + } + + /** + * Sets the current UI TTY mode for the MmTelFeature. + * @param mode An integer containing the new UI TTY Mode, can consist of + * {@link TelecomManager#TTY_MODE_OFF}, + * {@link TelecomManager#TTY_MODE_FULL}, + * {@link TelecomManager#TTY_MODE_HCO}, + * {@link TelecomManager#TTY_MODE_VCO} + * @param onCompleteMessage A {@link Message} to be used when the mode has been set. + */ + void setUiTtyMode(int mode, Message onCompleteMessage) { + // Base Implementation - Should be overridden + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureRemoved() { + // Base Implementation - Should be overridden + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureReady() { + // Base Implementation - Should be overridden + } + + /** + * @hide + */ + @Override + public final IImsMmTelFeature getBinder() { + return mImsMMTelBinder; + } +} diff --git a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java new file mode 100644 index 000000000000..8d1bd9d27f7c --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +import android.telephony.ims.internal.aidl.IImsRcsFeature; + +/** + * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend + * this class and provide implementations of the RcsFeature methods that they support. + * @hide + */ + +public class RcsFeature extends ImsFeature { + + private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() { + // Empty Default Implementation. + }; + + + public RcsFeature() { + super(); + } + + @Override + public void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c) { + // Do nothing for base implementation. + } + + @Override + public void onFeatureRemoved() { + + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureReady() { + + } + + @Override + public final IImsRcsFeature getBinder() { + return mImsRcsBinder; + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java new file mode 100644 index 000000000000..33aec5dfb8af --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.stub; + +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsConfigCallback; + +import com.android.ims.ImsConfig; + +/** + * Controls the modification of IMS specific configurations. For more information on the supported + * IMS configuration constants, see {@link ImsConfig}. + * + * @hide + */ + +public class ImsConfigImplBase { + + //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config + // work first. + private final IImsConfig mBinder = new IImsConfig.Stub() { + + @Override + public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { + ImsConfigImplBase.this.addImsConfigCallback(c); + } + + @Override + public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException { + ImsConfigImplBase.this.removeImsConfigCallback(c); + } + + @Override + public int getConfigInt(int item) throws RemoteException { + return Integer.MIN_VALUE; + } + + @Override + public String getConfigString(int item) throws RemoteException { + return null; + } + + @Override + public int setConfigInt(int item, int value) throws RemoteException { + return Integer.MIN_VALUE; + } + + @Override + public int setConfigString(int item, String value) throws RemoteException { + return Integer.MIN_VALUE; + } + }; + + public class Callback extends IImsConfigCallback.Stub { + + @Override + public final void onIntConfigChanged(int item, int value) throws RemoteException { + onConfigChanged(item, value); + } + + @Override + public final void onStringConfigChanged(int item, String value) throws RemoteException { + onConfigChanged(item, value); + } + + /** + * Called when the IMS configuration has changed. + * @param item the IMS configuration key constant, as defined in ImsConfig. + * @param value the new integer value of the IMS configuration constant. + */ + public void onConfigChanged(int item, int value) { + // Base Implementation + } + + /** + * Called when the IMS configuration has changed. + * @param item the IMS configuration key constant, as defined in ImsConfig. + * @param value the new String value of the IMS configuration constant. + */ + public void onConfigChanged(int item, String value) { + // Base Implementation + } + } + + private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>(); + + /** + * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration + * changes. + * @param c callback to add. + */ + private void addImsConfigCallback(IImsConfigCallback c) { + mCallbacks.register(c); + } + /** + * Removes a {@link Callback} to the list of callbacks notified when a value in the + * configuration changes. + * + * @param c callback to remove. + */ + private void removeImsConfigCallback(IImsConfigCallback c) { + mCallbacks.unregister(c); + } + + public final IImsConfig getBinder() { + return mBinder; + } + + /** + * Sets the value for IMS service/capabilities parameters by the operator device + * management entity. It sets the config item value in the provisioned storage + * from which the master value is derived. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in Integer format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + public int setConfig(int item, int value) { + // Base Implementation - To be overridden. + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Sets the value for IMS service/capabilities parameters by the operator device + * management entity. It sets the config item value in the provisioned storage + * from which the master value is derived. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in String format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + public int setConfig(int item, String value) { + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Gets the value for ims service/capabilities parameters from the provisioned + * value storage. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in Integer format. + */ + public int getConfigInt(int item) { + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Gets the value for ims service/capabilities parameters from the provisioned + * value storage. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in String format. + */ + public String getConfigString(int item) { + return null; + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl new file mode 100644 index 000000000000..e890cf8756f3 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.stub; + +parcelable ImsFeatureConfiguration; diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java new file mode 100644 index 000000000000..244c9578f6b4 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.stub; + +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.internal.feature.ImsFeature; +import android.util.ArraySet; + +import java.util.Arrays; +import java.util.Set; + +/** + * Container class for IMS Feature configuration. This class contains the features that the + * ImsService supports, which are defined in {@link ImsFeature.FeatureType}. + * @hide + */ +public class ImsFeatureConfiguration implements Parcelable { + /** + * Features that this ImsService supports. + */ + private final Set<Integer> mFeatures; + + /** + * Creates an ImsFeatureConfiguration with the features + */ + public static class Builder { + ImsFeatureConfiguration mConfig; + public Builder() { + mConfig = new ImsFeatureConfiguration(); + } + + /** + * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service + * supports. + * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration. + */ + public Builder addFeature(@ImsFeature.FeatureType int feature) { + mConfig.addFeature(feature); + return this; + } + + public ImsFeatureConfiguration build() { + return mConfig; + } + } + + /** + * Creates with all registration features empty. + * + * Consider using the provided {@link Builder} to create this configuration instead. + */ + public ImsFeatureConfiguration() { + mFeatures = new ArraySet<>(); + } + + /** + * Configuration of the ImsService, which describes which features the ImsService supports + * (for registration). + * @param features an array of feature integers defined in {@link ImsFeature} that describe + * which features this ImsService supports. + */ + public ImsFeatureConfiguration(int[] features) { + mFeatures = new ArraySet<>(); + + if (features != null) { + for (int i : features) { + mFeatures.add(i); + } + } + } + + /** + * @return an int[] containing the features that this ImsService supports. + */ + public int[] getServiceFeatures() { + return mFeatures.stream().mapToInt(i->i).toArray(); + } + + void addFeature(int feature) { + mFeatures.add(feature); + } + + protected ImsFeatureConfiguration(Parcel in) { + int[] features = in.createIntArray(); + if (features != null) { + mFeatures = new ArraySet<>(features.length); + for(Integer i : features) { + mFeatures.add(i); + } + } else { + mFeatures = new ArraySet<>(); + } + } + + public static final Creator<ImsFeatureConfiguration> CREATOR + = new Creator<ImsFeatureConfiguration>() { + @Override + public ImsFeatureConfiguration createFromParcel(Parcel in) { + return new ImsFeatureConfiguration(in); + } + + @Override + public ImsFeatureConfiguration[] newArray(int size) { + return new ImsFeatureConfiguration[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ImsFeatureConfiguration)) return false; + + ImsFeatureConfiguration that = (ImsFeatureConfiguration) o; + + return mFeatures.equals(that.mFeatures); + } + + @Override + public int hashCode() { + return mFeatures.hashCode(); + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java new file mode 100644 index 000000000000..558b009ab4c2 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.stub; + +import android.annotation.IntDef; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsRegistrationCallback; +import android.util.Log; + +import com.android.ims.ImsReasonInfo; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Controls IMS registration for this ImsService and notifies the framework when the IMS + * registration for this ImsService has changed status. + * @hide + */ + +public class ImsRegistrationImplBase { + + private static final String LOG_TAG = "ImsRegistrationImplBase"; + + // Defines the underlying radio technology type that we have registered for IMS over. + @IntDef(flag = true, + value = { + REGISTRATION_TECH_NONE, + REGISTRATION_TECH_LTE, + REGISTRATION_TECH_IWLAN + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsRegistrationTech {} + /** + * No registration technology specified, used when we are not registered. + */ + public static final int REGISTRATION_TECH_NONE = -1; + /** + * IMS is registered to IMS via LTE. + */ + public static final int REGISTRATION_TECH_LTE = 0; + /** + * IMS is registered to IMS via IWLAN. + */ + public static final int REGISTRATION_TECH_IWLAN = 1; + + // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current + // state. + private static final int REGISTRATION_STATE_NOT_REGISTERED = 0; + private static final int REGISTRATION_STATE_REGISTERING = 1; + private static final int REGISTRATION_STATE_REGISTERED = 2; + + + /** + * Callback class for receiving Registration callback events. + */ + public static class Callback extends IImsRegistrationCallback.Stub { + + /** + * Notifies the framework when the IMS Provider is connected to the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + @Override + public void onRegistered(@ImsRegistrationTech int imsRadioTech) { + } + + /** + * Notifies the framework when the IMS Provider is trying to connect the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + @Override + public void onRegistering(@ImsRegistrationTech int imsRadioTech) { + } + + /** + * Notifies the framework when the IMS Provider is disconnected from the IMS network. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + */ + @Override + public void onDeregistered(ImsReasonInfo info) { + } + + /** + * A failure has occurred when trying to handover registration to another technology type, + * defined in {@link ImsRegistrationTech} + * + * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed + * @param info A {@link ImsReasonInfo} that identifies the reason for failure. + */ + @Override + public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, + ImsReasonInfo info) { + } + } + + private final IImsRegistration mBinder = new IImsRegistration.Stub() { + + @Override + public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { + return getConnectionType(); + } + + @Override + public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + ImsRegistrationImplBase.this.addRegistrationCallback(c); + } + + @Override + public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + ImsRegistrationImplBase.this.removeRegistrationCallback(c); + } + }; + + private final RemoteCallbackList<IImsRegistrationCallback> mCallbacks + = new RemoteCallbackList<>(); + private final Object mLock = new Object(); + // Locked on mLock + private @ImsRegistrationTech + int mConnectionType = REGISTRATION_TECH_NONE; + // Locked on mLock + private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED; + // Locked on mLock + private ImsReasonInfo mLastDisconnectCause; + + public final IImsRegistration getBinder() { + return mBinder; + } + + private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + mCallbacks.register(c); + updateNewCallbackWithState(c); + } + + private void removeRegistrationCallback(IImsRegistrationCallback c) { + mCallbacks.unregister(c); + } + + /** + * Notify the framework that the device is connected to the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { + updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED); + mCallbacks.broadcast((c) -> { + try { + c.onRegistered(imsRadioTech); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " + + "callback."); + } + }); + } + + /** + * Notify the framework that the device is trying to connect the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { + updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING); + mCallbacks.broadcast((c) -> { + try { + c.onRegistering(imsRadioTech); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " + + "callback."); + } + }); + } + + /** + * Notify the framework that the device is disconnected from the IMS network. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + */ + public final void onDeregistered(ImsReasonInfo info) { + updateToDisconnectedState(info); + mCallbacks.broadcast((c) -> { + try { + c.onDeregistered(info); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " + + "callback."); + } + }); + } + + public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, + ImsReasonInfo info) { + mCallbacks.broadcast((c) -> { + try { + c.onTechnologyChangeFailed(imsRadioTech, info); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " + + "callback."); + } + }); + } + + private void updateToState(@ImsRegistrationTech int connType, int newState) { + synchronized (mLock) { + mConnectionType = connType; + mRegistrationState = newState; + mLastDisconnectCause = null; + } + } + + private void updateToDisconnectedState(ImsReasonInfo info) { + synchronized (mLock) { + updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED); + if (info != null) { + mLastDisconnectCause = info; + } else { + Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); + mLastDisconnectCause = new ImsReasonInfo(); + } + } + } + + private @ImsRegistrationTech int getConnectionType() { + synchronized (mLock) { + return mConnectionType; + } + } + + /** + * @param c the newly registered callback that will be updated with the current registration + * state. + */ + private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException { + int state; + ImsReasonInfo disconnectInfo; + synchronized (mLock) { + state = mRegistrationState; + disconnectInfo = mLastDisconnectCause; + } + switch (state) { + case REGISTRATION_STATE_NOT_REGISTERED: { + c.onDeregistered(disconnectInfo); + break; + } + case REGISTRATION_STATE_REGISTERING: { + c.onRegistering(getConnectionType()); + break; + } + case REGISTRATION_STATE_REGISTERED: { + c.onRegistered(getConnectionType()); + break; + } + } + } +} diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java index ccb0f3b07d0f..0f40b4562b0d 100644 --- a/tests/net/java/android/net/IpSecManagerTest.java +++ b/tests/net/java/android/net/IpSecManagerTest.java @@ -80,7 +80,7 @@ public class IpSecManagerTest { int resourceId = 1; IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI); - when(mMockIpSecService.reserveSecurityParameterIndex( + when(mMockIpSecService.allocateSecurityParameterIndex( eq(IpSecTransform.DIRECTION_IN), eq(GOOGLE_DNS_4.getHostAddress()), eq(DROID_SPI), @@ -88,7 +88,7 @@ public class IpSecManagerTest { .thenReturn(spiResp); IpSecManager.SecurityParameterIndex droidSpi = - mIpSecManager.reserveSecurityParameterIndex( + mIpSecManager.allocateSecurityParameterIndex( IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI); assertEquals(DROID_SPI, droidSpi.getSpi()); @@ -102,7 +102,7 @@ public class IpSecManagerTest { int resourceId = 1; IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI); - when(mMockIpSecService.reserveSecurityParameterIndex( + when(mMockIpSecService.allocateSecurityParameterIndex( eq(IpSecTransform.DIRECTION_OUT), eq(GOOGLE_DNS_4.getHostAddress()), eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX), @@ -110,7 +110,7 @@ public class IpSecManagerTest { .thenReturn(spiResp); IpSecManager.SecurityParameterIndex randomSpi = - mIpSecManager.reserveSecurityParameterIndex( + mIpSecManager.allocateSecurityParameterIndex( IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4); assertEquals(DROID_SPI, randomSpi.getSpi()); @@ -127,12 +127,13 @@ public class IpSecManagerTest { public void testAllocSpiResUnavaiableExeption() throws Exception { IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0); - when(mMockIpSecService.reserveSecurityParameterIndex( + when(mMockIpSecService.allocateSecurityParameterIndex( anyInt(), anyString(), anyInt(), anyObject())) .thenReturn(spiResp); try { - mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4); + mIpSecManager.allocateSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4); fail("ResourceUnavailableException was not thrown"); } catch (IpSecManager.ResourceUnavailableException e) { } @@ -144,12 +145,13 @@ public class IpSecManagerTest { @Test public void testAllocSpiSpiUnavaiableExeption() throws Exception { IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0); - when(mMockIpSecService.reserveSecurityParameterIndex( + when(mMockIpSecService.allocateSecurityParameterIndex( anyInt(), anyString(), anyInt(), anyObject())) .thenReturn(spiResp); try { - mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4); + mIpSecManager.allocateSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4); fail("ResourceUnavailableException was not thrown"); } catch (IpSecManager.ResourceUnavailableException e) { } @@ -161,7 +163,7 @@ public class IpSecManagerTest { @Test public void testRequestAllocInvalidSpi() throws Exception { try { - mIpSecManager.reserveSecurityParameterIndex( + mIpSecManager.allocateSecurityParameterIndex( IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0); fail("Able to allocate invalid spi"); } catch (IllegalArgumentException e) { diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 20a48971dd1f..80e42a33b3cc 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -125,7 +125,7 @@ public class IpSecServiceParameterizedTest { .thenReturn(TEST_SPI_OUT); IpSecSpiResponse spiResp = - mIpSecService.reserveSecurityParameterIndex( + mIpSecService.allocateSecurityParameterIndex( IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder()); assertEquals(IpSecManager.Status.OK, spiResp.status); assertEquals(TEST_SPI_OUT, spiResp.spi); @@ -142,7 +142,7 @@ public class IpSecServiceParameterizedTest { .thenReturn(TEST_SPI_OUT); IpSecSpiResponse spiResp = - mIpSecService.reserveSecurityParameterIndex( + mIpSecService.allocateSecurityParameterIndex( IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder()); mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); @@ -178,7 +178,7 @@ public class IpSecServiceParameterizedTest { .thenReturn(TEST_SPI_OUT); IpSecSpiResponse spiResp = - mIpSecService.reserveSecurityParameterIndex( + mIpSecService.allocateSecurityParameterIndex( IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder()); IpSecService.UserRecord userRecord = @@ -212,7 +212,7 @@ public class IpSecServiceParameterizedTest { .thenReturn(returnSpi); IpSecSpiResponse spi = - mIpSecService.reserveSecurityParameterIndex( + mIpSecService.allocateSecurityParameterIndex( direction, NetworkUtils.numericToInetAddress(remoteAddress).getHostAddress(), IpSecManager.INVALID_SECURITY_PARAMETER_INDEX, diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 354ad2a14838..8683c12e192e 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -287,7 +287,7 @@ public class IpSecServiceTest { for (String address : invalidAddresses) { try { IpSecSpiResponse spiResp = - mIpSecService.reserveSecurityParameterIndex( + mIpSecService.allocateSecurityParameterIndex( IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder()); fail("Invalid address was passed through IpSecService validation: " + address); } catch (IllegalArgumentException e) { @@ -368,7 +368,7 @@ public class IpSecServiceTest { // Reserve spis until it fails. for (int i = 0; i < MAX_NUM_SPIS; i++) { IpSecSpiResponse newSpi = - mIpSecService.reserveSecurityParameterIndex( + mIpSecService.allocateSecurityParameterIndex( 0x1, InetAddress.getLoopbackAddress().getHostAddress(), DROID_SPI + i, @@ -384,7 +384,7 @@ public class IpSecServiceTest { // Try to reserve one more SPI, and should fail. IpSecSpiResponse extraSpi = - mIpSecService.reserveSecurityParameterIndex( + mIpSecService.allocateSecurityParameterIndex( 0x1, InetAddress.getLoopbackAddress().getHostAddress(), DROID_SPI + MAX_NUM_SPIS, @@ -398,7 +398,7 @@ public class IpSecServiceTest { // Should successfully reserve one more spi. extraSpi = - mIpSecService.reserveSecurityParameterIndex( + mIpSecService.allocateSecurityParameterIndex( 0x1, InetAddress.getLoopbackAddress().getHostAddress(), DROID_SPI + MAX_NUM_SPIS, diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk index 94686c095bd8..2da294ce88ad 100644 --- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk +++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk @@ -20,6 +20,7 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT_NAMESPACES := true LOCAL_PACKAGE_NAME := AaptTestNamespace_App +LOCAL_EXPORT_PACKAGE_RESOURCES := true LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_STATIC_ANDROID_LIBRARIES := \ diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml index 1b80d9542881..174e746e55ce 100644 --- a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml +++ b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml @@ -26,4 +26,4 @@ @com.android.aapt.namespace.libtwo:string/public_string </item> </style> -</resources>
\ No newline at end of file +</resources> diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk new file mode 100644 index 000000000000..a35f6edd77a5 --- /dev/null +++ b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk @@ -0,0 +1,28 @@ +# +# Copyright (C) 2017 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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_USE_AAPT2 := true +LOCAL_AAPT_NAMESPACES := true +LOCAL_PACKAGE_NAME := AaptTestNamespace_Split +LOCAL_MODULE_TAGS := tests +LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_APK_LIBRARIES := AaptTestNamespace_App +LOCAL_RES_LIBRARIES := AaptTestNamespace_App +LOCAL_AAPT_FLAGS := --package-id 0x80 --rename-manifest-package com.android.aapt.namespace.app +include $(BUILD_PACKAGE) diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml new file mode 100644 index 000000000000..bc9583b70ef8 --- /dev/null +++ b/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.aapt.namespace.split" + featureSplit="split"> + + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/> + + <application> + <activity android:name=".SplitActivity" android:theme="@style/ActivityTheme"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml new file mode 100644 index 000000000000..63da5529f757 --- /dev/null +++ b/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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. +--> + +<resources> + <string name="activity_name">Namespace Split</string> + + <style name="ActivityTheme" parent="com.android.aapt.namespace.libone:style/Theme"> + <item name="android:colorPrimary">#FF9800</item> + <item name="android:colorPrimaryDark">#E65100</item> + <item name="android:colorAccent">#FFD180</item> + </style> +</resources> diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java b/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java new file mode 100644 index 000000000000..3fff3cf38dc4 --- /dev/null +++ b/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 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.aapt.namespace.split; + +import android.app.Activity; +import android.os.Bundle; + +public class SplitActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } +} diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index 2d517c76944b..0cfc0bdfaaa6 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -30,7 +30,8 @@ #include "ValueVisitor.h" #include "util/Util.h" -using android::StringPiece; +using ::android::StringPiece; +using ::android::StringPiece16; namespace aapt { @@ -291,11 +292,34 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( const std::u16string package16 = util::Utf8ToUtf16(name.package); const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type)); const std::u16string entry16 = util::Utf8ToUtf16(name.entry); + const std::u16string mangled_entry16 = + util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry)); + + uint32_t type_spec_flags; + ResourceId res_id; + + // There can be mangled resources embedded within other packages. Here we will + // look into each package and look-up the mangled name until we find the resource. + const size_t count = table.getBasePackageCount(); + for (size_t i = 0; i < count; i++) { + const android::String16 package_name = table.getBasePackageName(i); + StringPiece16 real_package16 = package16; + StringPiece16 real_entry16 = entry16; + std::u16string scratch_entry16; + if (StringPiece16(package_name) != package16) { + real_entry16 = mangled_entry16; + real_package16 = package_name.string(); + } + + type_spec_flags = 0; + res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(), + type16.size(), real_package16.data(), real_package16.size(), + &type_spec_flags); + if (res_id.is_valid()) { + break; + } + } - uint32_t type_spec_flags = 0; - ResourceId res_id = table.identifierForName( - entry16.data(), entry16.size(), type16.data(), type16.size(), - package16.data(), package16.size(), &type_spec_flags); if (!res_id.is_valid()) { return {}; } diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp index fd8a5080278b..1f59d7034300 100644 --- a/tools/aapt2/process/SymbolTable_test.cpp +++ b/tools/aapt2/process/SymbolTable_test.cpp @@ -16,7 +16,15 @@ #include "process/SymbolTable.h" +#include "SdkConstants.h" +#include "format/binary/TableFlattener.h" #include "test/Test.h" +#include "util/BigBuffer.h" + +using ::testing::Eq; +using ::testing::IsNull; +using ::testing::Ne; +using ::testing::NotNull; namespace aapt { @@ -30,13 +38,13 @@ TEST(ResourceTableSymbolSourceTest, FindSymbols) { .Build(); ResourceTableSymbolSource symbol_source(table.get()); - EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/foo"))); - EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/bar"))); + EXPECT_THAT(symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")), NotNull()); + EXPECT_THAT(symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")), NotNull()); std::unique_ptr<SymbolTable::Symbol> s = symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo")); - ASSERT_NE(nullptr, s); - EXPECT_NE(nullptr, s->attribute); + ASSERT_THAT(s, NotNull()); + EXPECT_THAT(s->attribute, NotNull()); } TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) { @@ -49,8 +57,8 @@ TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) { ResourceTableSymbolSource symbol_source(table.get()); std::unique_ptr<SymbolTable::Symbol> s = symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo")); - ASSERT_NE(nullptr, s); - EXPECT_NE(nullptr, s->attribute); + ASSERT_THAT(s, NotNull()); + EXPECT_THAT(s->attribute, NotNull()); } TEST(SymbolTableTest, FindByName) { @@ -64,8 +72,52 @@ TEST(SymbolTableTest, FindByName) { SymbolTable symbol_table(&mangler); symbol_table.AppendSource(util::make_unique<ResourceTableSymbolSource>(table.get())); - EXPECT_NE(nullptr, symbol_table.FindByName(test::ParseNameOrDie("id/foo"))); - EXPECT_NE(nullptr, symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo"))); + EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("id/foo")), NotNull()); + EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull()); +} + +TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) { + using namespace android; + + std::unique_ptr<IAaptContext> context = + test::ContextBuilder() + .SetCompilationPackage("com.android.app") + .SetPackageId(0x7f) + .SetPackageType(PackageType::kApp) + .SetMinSdkVersion(SDK_LOLLIPOP_MR1) + .SetNameManglerPolicy(NameManglerPolicy{"com.android.app"}) + .Build(); + + // Create a ResourceTable with a mangled resource, simulating a static library being merged into + // the main application package. + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddSimple("com.android.app:id/" + NameMangler::MangleEntry("com.android.lib", "foo"), + ResourceId(0x7f020000)) + .AddSimple("com.android.app:id/bar", ResourceId(0x7f020001)) + .Build(); + + BigBuffer buffer(1024u); + TableFlattener flattener({}, &buffer); + ASSERT_TRUE(flattener.Consume(context.get(), table.get())); + + std::unique_ptr<uint8_t[]> data = util::Copy(buffer); + + // Construct the test AssetManager. + auto asset_manager_source = util::make_unique<AssetManagerSymbolSource>(); + ResTable& res_table = const_cast<ResTable&>( + asset_manager_source->GetAssetManager()->getResources(false /*required*/)); + ASSERT_THAT(res_table.add(data.get(), buffer.size()), Eq(NO_ERROR)); + + SymbolTable symbol_table(context->GetNameMangler()); + symbol_table.AppendSource(std::move(asset_manager_source)); + + EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull()); + EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.app:id/bar")), NotNull()); + + EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.app:id/foo")), IsNull()); + EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/bar")), IsNull()); + EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.other:id/foo")), IsNull()); } } // namespace aapt |