summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp13
-rw-r--r--Android.mk2
-rw-r--r--api/current.txt397
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp2
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp58
-rw-r--r--cmds/statsd/src/config/ConfigManager.h25
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp21
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp23
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp8
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp25
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h9
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp11
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp28
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h2
-rw-r--r--cmds/statsd/src/storage/StorageManager.cpp9
-rw-r--r--cmds/statsd/src/storage/StorageManager.h2
-rw-r--r--cmds/wm/Android.mk5
-rw-r--r--cmds/wm/src/com/android/commands/wm/Wm.java296
-rwxr-xr-xcmds/wm/wm7
-rw-r--r--core/java/android/app/ActivityThread.java178
-rw-r--r--core/java/android/app/AppOpsManager.java14
-rw-r--r--core/java/android/app/Application.java2
-rw-r--r--core/java/android/app/ApplicationPackageManager.java2
-rw-r--r--core/java/android/app/ClientTransactionHandler.java2
-rw-r--r--core/java/android/app/ContextImpl.java210
-rw-r--r--core/java/android/app/Instrumentation.java6
-rw-r--r--core/java/android/app/LoadedApk.java75
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java43
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl6
-rw-r--r--core/java/android/app/servertransaction/PendingTransactionActions.java2
-rw-r--r--core/java/android/app/slice/Slice.java5
-rw-r--r--core/java/android/app/slice/SliceManager.java174
-rw-r--r--core/java/android/app/slice/SliceProvider.java24
-rw-r--r--core/java/android/bluetooth/BluetoothHidDevice.java2
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java8
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java2
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceCallback.java2
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java2
-rw-r--r--core/java/android/content/pm/PackageParser.java4
-rw-r--r--core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java134
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java6
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java6
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java4
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java28
-rw-r--r--core/java/android/hardware/location/IContextHubService.aidl10
-rw-r--r--core/java/android/hardware/radio/RadioManager.java3
-rw-r--r--core/java/android/net/IIpSecService.aidl2
-rw-r--r--core/java/android/net/IpSecManager.java59
-rw-r--r--core/java/android/net/IpSecTransform.java22
-rw-r--r--core/java/android/os/UserManager.java19
-rw-r--r--core/java/android/provider/Settings.java13
-rw-r--r--core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl20
-rw-r--r--core/java/android/security/recoverablekeystore/KeyDerivationParameters.java112
-rw-r--r--core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl20
-rw-r--r--core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java90
-rw-r--r--core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl20
-rw-r--r--core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java115
-rw-r--r--core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl20
-rw-r--r--core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java180
-rw-r--r--core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java228
-rw-r--r--core/java/android/view/View.java40
-rw-r--r--core/java/android/view/ViewConfiguration.java12
-rw-r--r--core/java/android/view/ViewRootImpl.java177
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java16
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java7
-rw-r--r--core/proto/android/graphics/point.proto26
-rw-r--r--core/proto/android/server/windowmanagerservice.proto2
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java97
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--graphics/java/android/graphics/Point.java16
-rw-r--r--keystore/java/android/security/IKeyChainService.aidl2
-rw-r--r--keystore/java/android/security/keystore/AttestationUtils.java53
-rw-r--r--keystore/java/android/security/keystore/KeyAttestationException.java46
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java34
-rw-r--r--keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java4
-rw-r--r--keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java64
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java24
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java3
-rw-r--r--services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java210
-rw-r--r--services/backup/java/com/android/server/backup/TransportManager.java87
-rw-r--r--services/backup/java/com/android/server/backup/internal/BackupHandler.java10
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java6
-rw-r--r--services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java35
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/BatteryService.java6
-rw-r--r--services/core/java/com/android/server/IpSecService.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java87
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java32
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java3
-rw-r--r--services/core/java/com/android/server/am/RecentTasks.java13
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java10
-rw-r--r--services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java44
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java66
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/Convert.java (renamed from services/core/java/com/android/server/broadcastradio/Convert.java)2
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/Tuner.java (renamed from services/core/java/com/android/server/broadcastradio/Tuner.java)2
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java (renamed from services/core/java/com/android/server/broadcastradio/TunerCallback.java)2
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java79
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/Convert.java132
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java54
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java4
-rw-r--r--services/core/java/com/android/server/location/ContextHubService.java65
-rw-r--r--services/core/java/com/android/server/location/ContextHubTransactionManager.java72
-rw-r--r--services/core/java/com/android/server/location/OWNERS2
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java167
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java9
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java42
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java39
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java61
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java961
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java2
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java18
-rw-r--r--services/core/java/com/android/server/pm/Settings.java1
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java5
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java11
-rw-r--r--services/core/java/com/android/server/wm/RemoteEventTrace.java7
-rw-r--r--services/core/java/com/android/server/wm/RemoteSurfaceTrace.java78
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java59
-rw-r--r--services/core/java/com/android/server/wm/TaskTapPointerEventListener.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java20
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java286
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java23
-rw-r--r--services/core/jni/BroadcastRadio/BroadcastRadioService.cpp6
-rw-r--r--services/core/jni/BroadcastRadio/Tuner.cpp6
-rw-r--r--services/core/jni/BroadcastRadio/TunerCallback.cpp6
-rw-r--r--services/core/jni/BroadcastRadio/convert.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java13
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java44
-rw-r--r--services/robotests/src/com/android/server/backup/TransportManagerTest.java180
-rw-r--r--services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java179
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java82
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImplTest.java155
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java147
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java15
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java32
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java14
-rw-r--r--telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java364
-rw-r--r--telephony/java/android/telephony/ims/internal/ImsService.java339
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl141
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl27
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl40
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl28
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl52
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl27
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl25
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl32
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl34
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl44
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl27
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl19
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java197
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/ImsFeature.java462
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java422
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/RcsFeature.java59
-rw-r--r--telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java173
-rw-r--r--telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl19
-rw-r--r--telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java147
-rw-r--r--telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java276
-rw-r--r--tests/net/java/android/net/IpSecManagerTest.java20
-rw-r--r--tests/net/java/com/android/server/IpSecServiceParameterizedTest.java8
-rw-r--r--tests/net/java/com/android/server/IpSecServiceTest.java8
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/Android.mk1
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml2
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk28
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml31
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml25
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java26
-rw-r--r--tools/aapt2/process/SymbolTable.cpp34
-rw-r--r--tools/aapt2/process/SymbolTable_test.cpp68
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