Merge "Remove recents button and longpress home when connected to service (1/2)"
diff --git a/Android.bp b/Android.bp
index 015e59f..979ed1c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -463,6 +463,17 @@
"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 @@
],
aidl: {
- local_include_dirs: [
+ export_include_dirs: [
// From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
"core/java",
"graphics/java",
@@ -624,7 +635,7 @@
name: "framework-statslog-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out)",
- out: ["android/util/StatsLog.java"],
+ out: ["android/util/StatsLogInternal.java"],
}
gensrcs {
diff --git a/Android.mk b/Android.mk
index d4a1dcf..1035362 100644
--- a/Android.mk
+++ b/Android.mk
@@ -278,7 +278,7 @@
$(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 a01de4f..1bc38d4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6418,6 +6418,7 @@
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);
@@ -7019,6 +7020,7 @@
method public android.net.Uri getUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
+ field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
field public static final java.lang.String HINT_ACTIONS = "actions";
field public static final java.lang.String HINT_HORIZONTAL = "horizontal";
field public static final java.lang.String HINT_LARGE = "large";
@@ -7028,11 +7030,13 @@
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";
field public static final java.lang.String SUBTYPE_SLIDER = "slider";
field public static final java.lang.String SUBTYPE_SOURCE = "source";
+ field public static final java.lang.String SUBTYPE_TOGGLE = "toggle";
}
public static class Slice.Builder {
@@ -7090,6 +7094,20 @@
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 +7116,8 @@
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 +8139,94 @@
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 +8246,7 @@
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 +26028,12 @@
}
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 +32232,7 @@
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";
@@ -37512,6 +37622,25 @@
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
}
+ public final class EditDistanceScorer implements android.os.Parcelable android.service.autofill.Scorer {
+ method public int describeContents();
+ method public static android.service.autofill.EditDistanceScorer getInstance();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
+ }
+
+ public final class FieldClassification implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
+ }
+
+ public static final class FieldClassification.Match {
+ method public java.lang.String getRemoteId();
+ method public float getScore();
+ }
+
public final class FillCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess(android.service.autofill.FillResponse);
@@ -37537,6 +37666,7 @@
method public java.util.Map<android.view.autofill.AutofillId, java.lang.String> getChangedFields();
method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
+ method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
method public java.util.Set<java.lang.String> getIgnoredDatasetIds();
method public java.util.Map<android.view.autofill.AutofillId, java.util.Set<java.lang.String>> getManuallyEnteredField();
method public java.util.Set<java.lang.String> getSelectedDatasetIds();
@@ -37574,6 +37704,7 @@
method public android.service.autofill.FillResponse.Builder disableAutofill(long);
method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setFlags(int);
method public android.service.autofill.FillResponse.Builder setFooter(android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
@@ -37657,6 +37788,9 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
+ public abstract interface Scorer {
+ }
+
public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
method public int describeContents();
@@ -37667,6 +37801,22 @@
public abstract interface Transformation {
}
+ public final class UserData implements android.os.Parcelable {
+ method public int describeContents();
+ method public static int getMaxFieldClassificationIdsSize();
+ method public static int getMaxUserDataSize();
+ method public static int getMaxValueLength();
+ method public static int getMinValueLength();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
+ }
+
+ public static final class UserData.Builder {
+ ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
+ method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
+ method public android.service.autofill.UserData build();
+ }
+
public abstract interface Validator {
}
@@ -41300,41 +41450,6 @@
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 +41462,6 @@
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 +41478,6 @@
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 +41485,6 @@
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 +41498,9 @@
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 +41796,6 @@
}
-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 {
@@ -44574,6 +44440,12 @@
field public static final int[] WILD_CARD;
}
+ public final class StatsLog {
+ method public static boolean logEvent(int);
+ method public static boolean logStart(int);
+ method public static boolean logStop(int);
+ }
+
public class StringBuilderPrinter implements android.util.Printer {
ctor public StringBuilderPrinter(java.lang.StringBuilder);
method public void println(java.lang.String);
@@ -47067,6 +46939,7 @@
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();
@@ -48753,9 +48626,11 @@
method public void cancel();
method public void commit();
method public void disableAutofillServices();
+ method public android.service.autofill.UserData getUserData();
method public boolean hasEnabledAutofillServices();
method public boolean isAutofillSupported();
method public boolean isEnabled();
+ method public boolean isFieldClassificationEnabled();
method public void notifyValueChanged(android.view.View);
method public void notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
method public void notifyViewEntered(android.view.View);
@@ -48767,6 +48642,7 @@
method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(android.view.View);
method public void requestAutofill(android.view.View, int, android.graphics.Rect);
+ method public void setUserData(android.service.autofill.UserData);
method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
diff --git a/api/system-current.txt b/api/system-current.txt
index 30e4cbc..227b483 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3531,6 +3531,11 @@
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+ field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 3fc5cd6..5635b56 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -463,34 +463,11 @@
}
public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
- method public int describeContents();
- method public static android.service.autofill.EditDistanceScorer getInstance();
method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
}
- public final class FieldClassification implements android.os.Parcelable {
- method public int describeContents();
- method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
- }
-
- public static final class FieldClassification.Match implements android.os.Parcelable {
- method public int describeContents();
- method public java.lang.String getRemoteId();
- method public float getScore();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification.Match> CREATOR;
- }
-
- public static final class FillEventHistory.Event {
- method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
- }
-
- public static final class FillResponse.Builder {
- method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
+ public final class FillResponse implements android.os.Parcelable {
+ method public int getFlags();
}
public final class ImageTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -523,29 +500,10 @@
method public boolean isValid(android.service.autofill.ValueFinder);
}
- public abstract interface Scorer {
- }
-
public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
}
- public final class UserData implements android.os.Parcelable {
- method public int describeContents();
- method public static int getMaxFieldClassificationIdsSize();
- method public static int getMaxUserDataSize();
- method public static int getMaxValueLength();
- method public static int getMinValueLength();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
- }
-
- public static final class UserData.Builder {
- ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
- method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
- method public android.service.autofill.UserData build();
- }
-
public abstract interface ValueFinder {
method public abstract java.lang.String findByAutofillId(android.view.autofill.AutofillId);
}
@@ -993,12 +951,6 @@
ctor public AutofillId(int);
}
- public final class AutofillManager {
- method public android.service.autofill.UserData getUserData();
- method public boolean isFieldClassificationEnabled();
- method public void setUserData(android.service.autofill.UserData);
- }
-
}
package android.widget {
diff --git a/cmds/ime/ime b/cmds/ime/ime
index 180dc76..7d2f72f 100755
--- a/cmds/ime/ime
+++ b/cmds/ime/ime
@@ -1,2 +1,2 @@
#!/system/bin/sh
-exec cmd input_method "$@"
+exec cmd input_method ime "$@"
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index e8b4083..4777dcd 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -317,7 +317,7 @@
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/atoms.proto b/cmds/statsd/src/atoms.proto
index 716fee6..a39acb2 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -79,6 +79,7 @@
IsolatedUidChanged isolated_uid_changed = 43;
PacketWakeupOccurred packet_wakeup_occurred = 44;
DropboxErrorChanged dropbox_error_changed = 45;
+ AppHook app_hook = 46;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -97,19 +98,29 @@
CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
WifiActivityEnergyInfoPulled wifi_activity_energy_info_pulled = 1011;
ModemActivityInfoPulled modem_activity_info_pulled = 1012;
+ AttributionChainDummyAtom attribution_chain_dummy_atom = 10000;
}
}
/**
- * A WorkSource represents the chained attribution of applications that
- * resulted in a particular bit of work being done.
+ * An attribution represents an application or module that is part of process where a particular bit
+ * of work is done.
*/
-message WorkSource {
- // The uid for a given element in the attribution chain.
- repeated int32 uid = 1;
- // The (optional) string tag for an element in the attribution chain. If the
- // element has no tag, it is encoded as an empty string.
- repeated string tag = 2;
+message Attribution {
+ // The uid for an application or module.
+ optional int32 uid = 1;
+ // The string tag for the attribution node.
+ optional string tag = 2;
+}
+
+/**
+ * An attribution chain represents the chained attributions of applications or modules that
+ * resulted in a particular bit of work being done.
+ * The ordering of the attributions is that of calls, that is uid = [A, B, C] if A calls B that
+ * calls C.
+ */
+message AttributionChain {
+ repeated Attribution attribution = 1;
}
/*
@@ -127,7 +138,7 @@
* - The CamelCase name of the message type should match the
* underscore_separated name as defined in Atom.
* - If an atom represents work that can be attributed to an app, there can
- * be exactly one WorkSource field. It must be field number 1.
+ * be exactly one AttributionChain field. It must be field number 1.
* - A field that is a uid should be a string field, tagged with the [xxx]
* annotation. The generated code on android will be represented by UIDs,
* and those UIDs will be translated in xxx to those strings.
@@ -138,6 +149,11 @@
* *****************************************************************************
*/
+message AttributionChainDummyAtom {
+ optional AttributionChain attribution_chain = 1;
+ optional int32 value = 2;
+}
+
/**
* Logs when the screen state changes.
*
@@ -801,6 +817,29 @@
optional int32 is_foreground = 7;
}
+/*
+ * Allows other apps to push events into statsd.
+ * Logged from:
+ * frameworks/base/core/java/android/util/StatsLog.java
+ */
+message AppHook {
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1;
+
+ // An arbitrary label chosen by the developer. For Android P, the label should be in [0, 16).
+ optional int32 label = 2;
+
+ // Allows applications to easily use a custom event as start/stop boundaries (ie, define custom
+ // predicates for the metrics).
+ enum State {
+ UNKNOWN = 0;
+ UNSPECIFIED = 1; // For events that are known to not represent START/STOP.
+ STOP = 2;
+ START = 3;
+ }
+ optional State state = 3;
+}
+
/**
* Pulls bytes transferred via wifi (Sum of foreground and background usage).
*
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index e52b273..164f88f 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -29,6 +29,12 @@
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 @@
}
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::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::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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 90ea6c0..ea42a35 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 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 @@
* 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 @@
/**
* 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 @@
/**
* 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 ae297d9..7b865c2 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -66,7 +66,7 @@
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 @@
}
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::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 @@
// 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 @@
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 @@
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 @@
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 8a17169..59995d2 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -76,8 +76,6 @@
// 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 c268798..6afbe45 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -68,8 +68,8 @@
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 @@
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::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 @@
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 @@
// 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 14504c1..e7aca7f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -75,7 +75,7 @@
// 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 bcecf16..4752997 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -55,7 +55,7 @@
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 @@
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::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 49ba9d8..d720ead 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -67,8 +67,6 @@
// 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 fffb2bf..ae9b86f 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -67,8 +67,8 @@
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 @@
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::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::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 @@
// 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 @@
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 @@
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 ee4f40c..6e6f2bb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -87,7 +87,7 @@
// 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 269bd43..d4a2195 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -38,9 +38,10 @@
// 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 @@
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 b0f0135..3d0e20c 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -50,11 +50,6 @@
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 @@
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 aabe5af..1eabf11 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -73,13 +73,13 @@
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 @@
}
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 @@
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 @@
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::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 @@
}
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;
}
@@ -296,11 +294,15 @@
} else { // for pushed events
interval.sum += value;
}
+
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, interval.sum);
+ }
}
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 {
@@ -329,6 +331,12 @@
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
+
+ for (auto& tracker : mAnomalyTrackers) {
+ if (tracker != nullptr) {
+ tracker->addPastBucket(slice.first, info.mValue, info.mBucketNum);
+ }
+ }
}
VLOG("%d tainted pairs in the bucket", tainted);
@@ -342,7 +350,7 @@
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 4c49927..51af83d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -19,6 +19,7 @@
#include <gtest/gtest_prod.h>
#include <utils/threads.h>
#include <list>
+#include "../anomaly/AnomalyTracker.h"
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
@@ -74,7 +75,7 @@
// 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;
@@ -117,6 +118,7 @@
FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 3a4dfda..9919abf 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::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 4c9abe5..caf5b8b 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -66,7 +66,7 @@
/**
* 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/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 146a19d..6f117d3 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -238,6 +238,79 @@
EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
}
+TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name(metricName);
+ alert.set_trigger_if_sum_gt(130);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(3);
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
+
+ ValueMetric metric;
+ metric.set_name(metricName);
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_value_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ -1 /*not pulled*/, bucketStartTimeNs);
+ valueProducer.addAnomalyTracker(anomalyTracker);
+
+
+ shared_ptr<LogEvent> event1
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC);
+ event1->write(161);
+ event1->write(10); // value of interest
+ event1->init();
+ shared_ptr<LogEvent> event2
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC);
+ event2->write(162);
+ event2->write(20); // value of interest
+ event2->init();
+ shared_ptr<LogEvent> event3
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC);
+ event3->write(163);
+ event3->write(130); // value of interest
+ event3->init();
+ shared_ptr<LogEvent> event4
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC);
+ event4->write(35);
+ event4->write(1); // value of interest
+ event4->init();
+ shared_ptr<LogEvent> event5
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
+ event5->write(45);
+ event5->write(150); // value of interest
+ event5->init();
+ shared_ptr<LogEvent> event6
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
+ event6->write(25);
+ event6->write(160); // value of interest
+ event6->init();
+
+ // Two events in bucket #0.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL); // Value sum == 30 <= 130.
+
+ // One event in bucket #2. No alarm as bucket #0 is trashed out.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL); // Value sum == 130 <= 130.
+
+ // Three events in bucket #3.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
+ // Anomaly at event 4 since Value sum == 131 > 130!
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event4->GetTimestampNs());
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
+ // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event4->GetTimestampNs());
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
+ // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event6->GetTimestampNs());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
index 3997897..5d35c29 100644
--- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
+++ b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
@@ -111,6 +111,23 @@
android:text="@string/screen_off"/>
</LinearLayout>
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/custom_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/custom_start" />
+
+ <Button
+ android:id="@+id/custom_stop"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/custom_stop" />
+ </LinearLayout>
+
<Button android:id="@+id/dump"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/cmds/statsd/tools/dogfood/res/values/strings.xml b/cmds/statsd/tools/dogfood/res/values/strings.xml
index 7690df6..0eab0f4 100644
--- a/cmds/statsd/tools/dogfood/res/values/strings.xml
+++ b/cmds/statsd/tools/dogfood/res/values/strings.xml
@@ -47,6 +47,9 @@
<string name="screen_on">Screen On</string>
<string name="screen_off">Screen Off</string>
+ <string name="custom_start">App hook start</string>
+ <string name="custom_stop">App hook stop</string>
+
<string name="dump">DumpReport</string>
<string name="report_header">Report details</string>
</resources>
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index c83802e..70dd634 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -140,6 +140,20 @@
}
});
+ findViewById(R.id.custom_start).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ StatsLog.logStart(8);
+ }
+ });
+
+ findViewById(R.id.custom_stop).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ StatsLog.logStop(8);
+ }
+ });
+
mReportText = (TextView) findViewById(R.id.report_text);
findViewById(R.id.dump).setOnClickListener(new View.OnClickListener() {
diff --git a/cmds/uiautomator/instrumentation/Android.mk b/cmds/uiautomator/instrumentation/Android.mk
index 008bb93..ed99f3e 100644
--- a/cmds/uiautomator/instrumentation/Android.mk
+++ b/cmds/uiautomator/instrumentation/Android.mk
@@ -21,7 +21,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, testrunner-src) \
$(call all-java-files-under, ../library/core-src)
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_MODULE := uiautomator-instrumentation
# TODO: change this to 18 when it's available
diff --git a/cmds/wm/Android.mk b/cmds/wm/Android.mk
index 3f3795f..693c6e7 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 8defb33..0000000
--- 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 16d6bd6..cb45be2 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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 6666fcc..998d77f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -309,4 +309,11 @@
* Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
*/
public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
+
+ public interface ScreenObserver {
+ public void onAwakeStateChanged(boolean isAwake);
+ public void onKeyguardStateChanged(boolean isShowing);
+ }
+
+ public abstract void registerScreenObserver(ScreenObserver observer);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ef446c2..da57efa 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -377,7 +377,7 @@
ActivityInfo activityInfo;
CompatibilityInfo compatInfo;
- public LoadedApk packageInfo;
+ public LoadedApk loadedApk;
List<ResultInfo> pendingResults;
List<ReferrerIntent> pendingIntents;
@@ -420,7 +420,7 @@
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 @@
}
static final class AppBindData {
- LoadedApk info;
+ LoadedApk loadedApk;
String processName;
ApplicationInfo appInfo;
List<ProviderInfo> providers;
@@ -1774,13 +1774,13 @@
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 @@
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 @@
+ mBoundApplication.processName
+ "/" + mBoundApplication.appInfo.uid);
}
- return packageInfo;
+ return loadedApk;
}
}
@@ -1822,13 +1822,13 @@
}
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 @@
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 @@
}
}
- 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 @@
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 @@
/** 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 @@
}
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 @@
}
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
break;
}
}
- ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
+ ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
}
final void handleLowMemory() {
@@ -5464,7 +5464,7 @@
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 @@
// 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 @@
}
} 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 c943574..50e3f0a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -160,7 +160,7 @@
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 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 @@
OP_CHANGE_WIFI_STATE,
OP_REQUEST_DELETE_PACKAGES,
OP_BIND_ACCESSIBILITY_SERVICE,
+ OP_PERFORM_ACCESSIBILITY_ACTION,
};
/**
@@ -587,6 +590,7 @@
null, // OP_CHANGE_WIFI_STATE
null, // OP_REQUEST_DELETE_PACKAGES
null, // OP_BIND_ACCESSIBILITY_SERVICE
+ null, // OP_PERFORM_ACCESSIBILITY_ACTION
};
/**
@@ -668,6 +672,7 @@
"CHANGE_WIFI_STATE",
"REQUEST_DELETE_PACKAGES",
"BIND_ACCESSIBILITY_SERVICE",
+ "OP_PERFORM_ACCESSIBILITY_ACTION",
};
/**
@@ -749,6 +754,7 @@
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 @@
null, // OP_CHANGE_WIFI_STATE
null, // REQUEST_DELETE_PACKAGES
null, // OP_BIND_ACCESSIBILITY_SERVICE
+ null, // OP_PERFORM_ACCESSIBILITY_ACTION
};
/**
@@ -912,6 +919,7 @@
false, // OP_CHANGE_WIFI_STATE
false, // OP_REQUEST_DELETE_PACKAGES
false, // OP_BIND_ACCESSIBILITY_SERVICE
+ false, // OP_PERFORM_ACCESSIBILITY_ACTION
};
/**
@@ -992,6 +1000,7 @@
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 @@
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 156df36..5822f5c 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -187,7 +187,7 @@
*/
/* 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 1dbdb59..8641a21 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1381,7 +1381,7 @@
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 ef66af0..45c0e0c 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -110,7 +110,7 @@
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 a2de0f4..1653430 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -159,7 +159,7 @@
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 @@
@Override
public Context getApplicationContext() {
- return (mPackageInfo != null) ?
- mPackageInfo.getApplication() : mMainThread.getApplication();
+ return (mLoadedApk != null) ?
+ mLoadedApk.getApplication() : mMainThread.getApplication();
}
@Override
@@ -302,15 +302,15 @@
@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 @@
@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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
@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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
}
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 @@
* 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 @@
}
}
- 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 @@
// 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 @@
}
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 @@
// 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 @@
}
mUser = user;
- mPackageInfo = packageInfo;
+ mLoadedApk = loadedApk;
mSplitName = splitName;
mClassLoader = classLoader;
mResourcesManager = ResourcesManager.getInstance();
@@ -2363,8 +2335,8 @@
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 @@
}
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 @@
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 490b2bf..41324d0 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1209,10 +1209,10 @@
}
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/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 9ec7f41..88e2356 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -166,7 +166,7 @@
if (item.icon == null) {
item.icon = mIconResizer.createIconThumbnail(item.resolveInfo.loadIcon(getPackageManager()));
}
- text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
+ text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, null, null, null);
}
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ab00a7d..26f4980 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -31,6 +31,7 @@
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.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 @@
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 3c4036b..89df421 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -64,6 +64,9 @@
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 @@
}
/**
+ * 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 @@
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 014d7b9..9128208 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -36,6 +36,7 @@
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 @@
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 @@
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 073d28c..8304c1c 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -134,7 +134,7 @@
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 b13067e..1aff7e9 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -125,6 +125,11 @@
*/
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
@@ -151,7 +156,6 @@
/**
* Key to retrieve an extra added to an intent when a control is changed.
- * @hide
*/
public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
/**
@@ -171,6 +175,13 @@
* Subtype to tag an item represents a slider.
*/
public static final String SUBTYPE_SLIDER = "slider";
+ /**
+ * Subtype 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 retrieved to see the new state of the toggle.
+ */
+ public static final String SUBTYPE_TOGGLE = "toggle";
private final SliceItem[] mItems;
private final @SliceHint String[] mHints;
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index f8e19c1..0c5f225d 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 @@
}
/**
+ * 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) {
+ 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 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 unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) {
+ try {
+ 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 pinSlice(Uri uri, SliceSpec[] specs) {
+ public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
try {
- mService.pinSlice(mContext.getPackageName(), uri, specs);
+ 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(Uri uri) {
+ public void unpinSlice(@NonNull Uri uri) {
try {
mService.unpinSlice(mContext.getPackageName(), uri);
} catch (RemoteException e) {
@@ -81,6 +198,7 @@
}
/**
+ * @hide
*/
public boolean hasSliceAccess() {
try {
@@ -91,41 +209,31 @@
}
/**
+ * 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 interface SliceCallback {
/**
+ * Called when slice is updated.
+ *
+ * @param s The updated slice.
+ * @see #registerSliceCallback
*/
- public SliceListener() {
- this(Handler.getMain());
- }
-
- /**
- */
- 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 7dcd2fe..8483931 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -151,13 +151,33 @@
}
/**
- * @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 4a00486..f38e462 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -35,8 +35,6 @@
*
* <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 4609d52..c05df2d 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -26,8 +26,6 @@
* registration.
*
* <p>{@see BluetoothHidDevice}
- *
- * <p>{@hide}
*/
public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
@@ -46,10 +44,8 @@
/**
* 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 2da64e5..562c559 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -28,8 +28,6 @@
* 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 6ed1965..bd19955 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -24,8 +24,6 @@
* 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 ebbc710..df2028a 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -153,8 +153,6 @@
/**
* 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 2bbcfff..e2fd82d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1559,7 +1559,7 @@
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 @@
}
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 0000000..e02e68d
--- /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 2c93a7f..7717b8d 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -296,7 +296,11 @@
&& 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 5adb119..b211700 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1094,6 +1094,12 @@
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 09bb9c6..c1c0812 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -289,6 +289,10 @@
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 1d66dc6d..5b89f54 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -271,7 +271,7 @@
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 @@
};
}
- /*
+ /**
* Helper function to generate a stub for a query transaction callback.
*
* @param transaction the transaction to unblock when complete
@@ -392,7 +392,17 @@
*/
@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 @@
*/
@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 628ebc7..db5bd36 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -70,6 +70,16 @@
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 4f4361f..4d54e31 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -161,7 +161,8 @@
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 0b1ea98..d9b57db 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -30,7 +30,7 @@
*/
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 a9e60ec..6a4b891 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -46,7 +46,7 @@
* 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 @@
*
* @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 @@
* <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 @@
* 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 @@
mRemoteAddress = remoteAddress;
try {
IpSecSpiResponse result =
- mService.reserveSecurityParameterIndex(
+ mService.allocateSecurityParameterIndex(
direction, remoteAddress.getHostAddress(), spi, new Binder());
if (result == null) {
@@ -228,7 +227,7 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 cda4ec7..7cd742b 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -47,7 +47,7 @@
* 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 @@
}
/**
- * 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 @@
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 @@
*
* <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 @@
* <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 @@
*
* @param intervalSeconds the maximum number of seconds between keepalive packets. Must be
* between 20s and 3600s.
- *
* @hide
*/
@SystemApi
@@ -418,7 +413,6 @@
* 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 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/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 3ec744d..3e8161f 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -52,7 +52,7 @@
// pushed ones to be consistent.
write4Bytes(STATS_BUFFER_TAG_ID);
mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
- mStorage.write(fields); // Indicate number of elements in this list.
+ mStorage.write(fields + 1); // Indicate number of elements in this list. +1 for the tag
mStorage.write(EVENT_TYPE_INT);
// The first element is the real atom tag number
write4Bytes(tag);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index fb60bbb..3504142 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -774,6 +774,25 @@
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/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index ec5b1c6..c94da9a 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4244,19 +4244,39 @@
/**
* The flattened {@link android.content.ComponentName} of a {@link
* android.telecom.PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to
- * call the contact with. Used by {@link CommonDataKinds.Phone}.
+ * call the contact with.
*
+ * <p> On a multi-SIM device this field can be used in a {@link CommonDataKinds.Phone} row
+ * to indicate the {@link PhoneAccountHandle} to call the number with, instead of using
+ * {@link android.telecom.TelecomManager#getDefaultOutgoingPhoneAccount(String)} or asking
+ * every time.
+ *
+ * <p>{@link android.telecom.TelecomManager#placeCall(Uri, android.os.Bundle)}
+ * should be called with {@link android.telecom.TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}
+ * set to the {@link PhoneAccountHandle} using the {@link ComponentName} from this field.
+ *
+ * @see #PREFERRED_PHONE_ACCOUNT_ID
* @see PhoneAccountHandle#getComponentName()
* @see ComponentName#flattenToString()
*/
String PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME = "preferred_phone_account_component_name";
/**
- * The ID of a {@link
+ * The ID of a {@link
* android.telecom.PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to
* call the contact with. Used by {@link CommonDataKinds.Phone}.
*
- * @see PhoneAccountHandle#getId() ()
+ * <p> On a multi-SIM device this field can be used in a {@link CommonDataKinds.Phone} row
+ * to indicate the {@link PhoneAccountHandle} to call the number with, instead of using
+ * {@link android.telecom.TelecomManager#getDefaultOutgoingPhoneAccount(String)} or asking
+ * every time.
+ *
+ * <p>{@link android.telecom.TelecomManager#placeCall(Uri, android.os.Bundle)}
+ * should be called with {@link android.telecom.TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}
+ * set to the {@link PhoneAccountHandle} using the id from this field.
+ *
+ * @see #PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME
+ * @see PhoneAccountHandle#getId()
*/
String PREFERRED_PHONE_ACCOUNT_ID = "preferred_phone_account_id";
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4ecbdf5..d0c1003 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5328,48 +5328,51 @@
public static final String AUTOFILL_SERVICE = "autofill_service";
/**
- * Experimental autofill feature.
+ * Boolean indicating if Autofill supports field classification.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
+ * @see android.service.autofill.AutofillService
+ *
* @hide
*/
+ @SystemApi
@TestApi
public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
"autofill_field_classification";
/**
- * Experimental autofill feature.
+ * Defines value returned by {@link android.service.autofill.UserData#getMaxUserDataSize()}.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
* @hide
*/
+ @SystemApi
public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
"autofill_user_data_max_user_data_size";
/**
- * Experimental autofill feature.
+ * Defines value returned by
+ * {@link android.service.autofill.UserData#getMaxFieldClassificationIdsSize()}.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
* @hide
*/
+ @SystemApi
public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
"autofill_user_data_max_field_classification_size";
/**
- * Experimental autofill feature.
+ * Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
* @hide
*/
+ @SystemApi
public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
"autofill_user_data_max_value_length";
/**
- * Experimental autofill feature.
+ * Defines value returned by {@link android.service.autofill.UserData#getMinValueLength()}.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
* @hide
*/
+ @SystemApi
public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
"autofill_user_data_min_value_length";
@@ -11145,6 +11148,19 @@
"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/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 6be0e76..6a3c55e 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -297,7 +297,7 @@
* {@link ContentResolver#applyBatch}), and if the {@link ContentValues} doesn't contain
* this column, the voicemail provider implicitly sets it to 0 if the calling package is
* the {@link #SOURCE_PACKAGE} or to 1 otherwise. To prevent this behavior, explicitly set
- * {@link #DIRTY_RETAIN} to this column in the {@link ContentValues}.
+ * {@link #DIRTY_RETAIN} to DIRTY in the {@link ContentValues}.
*
* <P>Type: INTEGER (boolean)</P>
*
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
new file mode 100644
index 0000000..fe13179
--- /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 0000000..2205c41
--- /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 0000000..1058463a
--- /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 0000000..80f5aa7
--- /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 0000000..bd76051
--- /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 0000000..087f7a2
--- /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 0000000..e1d49de
--- /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 0000000..43f9c80
--- /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 0000000..0510320
--- /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/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 97fdcef..2600f8a 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -453,13 +453,46 @@
* email address), the service should only use it locally (i.e., in the app's process) for
* heuristics purposes, but it should not be sent to external servers.
*
- * <a name="FieldsClassification"></a>
- * <h3>Metrics and fields classification</h3
+ * <a name="FieldClassification"></a>
+ * <h3>Metrics and field classification</h3
*
- * <p>TODO(b/67867469): document it or remove this section; in particular, document the relationship
- * between set/getUserData(), FillResponse.setFieldClassificationIds(), and
- * FillEventHistory.getFieldsClassification.
+ * <p>The service can call {@link #getFillEventHistory()} to get metrics representing the user
+ * actions, and then use these metrics to improve its heuristics.
+ *
+ * <p>Prior to Android {@link android.os.Build.VERSION_CODES#P}, the metrics covered just the
+ * scenarios where the service knew how to autofill an activity, but Android
+ * {@link android.os.Build.VERSION_CODES#P} introduced a new mechanism called field classification,
+ * which allows the service to dinamically classify the meaning of fields based on the existing user
+ * data known by the service.
+ *
+ * <p>Typically, field classification can be used to detect fields that can be autofilled with
+ * user data that is not associated with a specific app—such as email and physical
+ * address. Once the service identifies that a such field was manually filled by the user, the
+ * service could use this signal to improve its heuristics, either locally (i.e., in the same
+ * device) or globally (i.e., by crowdsourcing the results back to the service's server so it can
+ * be used by other users).
+ *
+ * <p>The field classification workflow involves 4 steps:
+ *
+ * <ol>
+ * <li>Set the user data through {@link AutofillManager#setUserData(UserData)}. This data is
+ * cached until the system restarts (or the service is disabled), so it doesn't need to be set for
+ * all requests.
+ * <li>Identify which fields should be analysed by calling
+ * {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)}.
+ * <li>Verify the results through {@link FillEventHistory.Event#getFieldsClassification()}.
+ * <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in future
+ * requests.
+ * </ol>
+ *
+ * <p>The field classification is an expensive operation and should be used carefully, otherwise it
+ * can reach its rate limit and get blocked by the Android System. Ideally, it should be used just
+ * in cases where the service could not determine how an activity can be autofilled, but it has a
+ * strong suspicious that it could. For example, if an activity has four or more fields and one of
+ * them is a list, chances are that these are address fields (like address, city, state, and
+ * zip code).
*/
+// TODO(b/70407264): add code snippets above???
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java
index e25cd04..0706b37 100644
--- a/core/java/android/service/autofill/EditDistanceScorer.java
+++ b/core/java/android/service/autofill/EditDistanceScorer.java
@@ -16,7 +16,6 @@
package android.service.autofill;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.autofill.AutofillValue;
@@ -24,14 +23,8 @@
/**
* Helper used to calculate the classification score between an actual {@link AutofillValue} filled
* by the user and the expected value predicted by an autofill service.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - document algorithm / copy from InternalScorer
- * - unhide / remove testApi
- * @hide
*/
-@TestApi
+// TODO(b/70291841): explain algorithm once it's fully implemented
public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable {
private static final EditDistanceScorer sInstance = new EditDistanceScorer();
@@ -46,10 +39,11 @@
private EditDistanceScorer() {
}
+ /** @hide */
@Override
public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) {
if (actualValue == null || !actualValue.isText() || userData == null) return 0;
- // TODO(b/67867469): implement edit distance - currently it's returning either 0, 100%, or
+ // TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or
// partial match when number of chars match
final String textValue = actualValue.getTextValue().toString();
final int total = textValue.length();
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 0a60208..b28c6f8 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -19,7 +19,6 @@
import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.autofill.Helper;
@@ -31,15 +30,10 @@
import java.util.List;
/**
- * Gets the <a href="#FieldsClassification">fields classification</a> results for a given field.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- *
- * @hide
+ * Represents the <a href="AutofillService.html#FieldClassification">field classification</a>
+ * results for a given field.
*/
-@TestApi
+// TODO(b/70291841): let caller handle Parcelable...
public final class FieldClassification implements Parcelable {
private final Match mMatch;
@@ -79,7 +73,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mMatch, flags);
+ mMatch.writeToParcel(parcel);
}
public static final Parcelable.Creator<FieldClassification> CREATOR =
@@ -87,7 +81,7 @@
@Override
public FieldClassification createFromParcel(Parcel parcel) {
- return new FieldClassification(parcel.readParcelable(null));
+ return new FieldClassification(Match.readFromParcel(parcel));
}
@Override
@@ -97,16 +91,12 @@
};
/**
- * Gets the score of a {@link UserData} entry for the field.
+ * Represents the score of a {@link UserData} entry for the field.
*
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- *
- * @hide
+ * <p>The score is defined by {@link #getScore()} and the entry is identified by
+ * {@link #getRemoteId()}.
*/
- @TestApi
- public static final class Match implements Parcelable {
+ public static final class Match {
private final String mRemoteId;
private final float mScore;
@@ -126,26 +116,19 @@
}
/**
- * Gets a score between the value of this field and the value of the {@link UserData} entry.
+ * Gets a classification score for the value of this field compared to the value of the
+ * {@link UserData} entry.
*
- * <p>The score is based in a case-insensitive comparisson of all characters from both the
- * field value and the user data entry, and it ranges from {@code 0} to {@code 1000000}:
+ * <p>The score is based in a comparison of the field value and the user data entry, and it
+ * ranges from {@code 0.0F} to {@code 1.0F}:
* <ul>
- * <li>{@code 1.0} represents a full match ({@code 100%}).
- * <li>{@code 0.0} represents a full mismatch ({@code 0%}).
+ * <li>{@code 1.0F} represents a full match ({@code 100%}).
+ * <li>{@code 0.0F} represents a full mismatch ({@code 0%}).
* <li>Any other value is a partial match.
* </ul>
*
- * <p>How the score is calculated depends on the algorithm used by the Android System.
- * For example, if the user data is {@code "abc"} and the field value us {@code " abc"},
- * the result could be:
- * <ul>
- * <li>{@code 1.0} if the algorithm trims the values.
- * <li>{@code 0.0} if the algorithm compares the values sequentially.
- * <li>{@code 0.75} if the algorithm consideres that 3/4 (75%) of the characters match.
- * </ul>
- *
- * <p>Currently, the autofill service cannot configure the algorithm.
+ * <p>How the score is calculated depends on the algorithm used by the {@link Scorer}
+ * implementation.
*/
public float getScore() {
return mScore;
@@ -160,33 +143,31 @@
return string.append(", score=").append(mScore).toString();
}
- /////////////////////////////////////
- // Parcelable "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
+ private void writeToParcel(@NonNull Parcel parcel) {
parcel.writeString(mRemoteId);
parcel.writeFloat(mScore);
}
- @SuppressWarnings("hiding")
- public static final Parcelable.Creator<Match> CREATOR = new Parcelable.Creator<Match>() {
+ private static Match readFromParcel(@NonNull Parcel parcel) {
+ return new Match(parcel.readString(), parcel.readFloat());
+ }
- @Override
- public Match createFromParcel(Parcel parcel) {
- return new Match(parcel.readString(), parcel.readFloat());
+ /** @hide */
+ public static Match[] readArrayFromParcel(@NonNull Parcel parcel) {
+ final int length = parcel.readInt();
+ final Match[] matches = new Match[length];
+ for (int i = 0; i < length; i++) {
+ matches[i] = readFromParcel(parcel);
}
+ return matches;
+ }
- @Override
- public Match[] newArray(int size) {
- return new Match[size];
+ /** @hide */
+ public static void writeArrayToParcel(@NonNull Parcel parcel, @NonNull Match[] matches) {
+ parcel.writeInt(matches.length);
+ for (int i = 0; i < matches.length; i++) {
+ matches[i].writeToParcel(parcel);
}
- };
+ }
}
}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index a527f16..07fab61 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -21,7 +21,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
@@ -158,7 +157,7 @@
final AutofillId[] detectedFields = event.mDetectedFieldIds;
parcel.writeParcelableArray(detectedFields, flags);
if (detectedFields != null) {
- parcel.writeParcelableArray(event.mDetectedMatches, flags);
+ Match.writeArrayToParcel(parcel, event.mDetectedMatches);
}
}
}
@@ -208,6 +207,7 @@
* ({@link #getIgnoredDatasetIds()}).
* <li>Which fields in the selected datasets were changed by the user after the dataset
* was selected ({@link #getChangedFields()}.
+ * <li>Which fields match the {@link UserData} set by the service.
* </ul>
*
* <p><b>Note: </b>This event is only generated when:
@@ -222,7 +222,6 @@
* <p>See {@link android.view.autofill.AutofillManager} for more information about autofill
* contexts.
*/
- // TODO(b/67867469): update with field detection behavior
public static final int TYPE_CONTEXT_COMMITTED = 4;
/** @hide */
@@ -356,19 +355,13 @@
}
/**
- * Gets the <a href="#FieldsClassification">fields classification</a> results.
+ * Gets the <a href="AutofillService.html#FieldClassification">field classification</a>
+ * results.
*
* <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
* service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
- * fields classification}.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- *
- * @hide
+ * field classification}.
*/
- @TestApi
@NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() {
if (mDetectedFieldIds == null) {
return Collections.emptyMap();
@@ -462,6 +455,8 @@
* and belonged to datasets.
* @param manuallyFilledDatasetIds The ids of datasets that had values matching the
* respective entry on {@code manuallyFilledFieldIds}.
+ * @param detectedMatches the field classification matches.
+ *
* @throws IllegalArgumentException If the length of {@code changedFieldIds} and
* {@code changedDatasetIds} doesn't match.
* @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
@@ -469,7 +464,6 @@
*
* @hide
*/
- // TODO(b/67867469): document field classification parameters once stable
public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
@Nullable List<String> selectedDatasetIds,
@Nullable ArraySet<String> ignoredDatasetIds,
@@ -477,7 +471,7 @@
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
- @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMaches) {
+ @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMatches) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
"eventType");
mDatasetId = datasetId;
@@ -502,7 +496,7 @@
mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
mDetectedFieldIds = detectedFieldIds;
- mDetectedMatches = detectedMaches;
+ mDetectedMatches = detectedMatches;
}
@Override
@@ -555,7 +549,7 @@
final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null,
AutofillId.class);
final Match[] detectedMatches = (detectedFieldIds != null)
- ? parcel.readParcelableArray(null, Match.class)
+ ? Match.readArrayFromParcel(parcel)
: null;
selection.addEvent(new Event(eventType, datasetId, clientState,
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 014d3e8..3a4b6bb 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -41,7 +41,7 @@
import java.util.List;
/**
- * Response for a {@link
+ * Response for an {@link
* AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
*
* <p>See the main {@link AutofillService} documentation for more details and examples.
@@ -156,6 +156,7 @@
}
/** @hide */
+ @TestApi
public int getFlags() {
return mFlags;
}
@@ -352,22 +353,18 @@
}
/**
- * Sets which fields are used for <a href="#FieldsClassification">fields classification</a>
+ * Sets which fields are used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>
*
+ * <p><b>Note:</b> This method automatically adds the
+ * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}.
+
* @throws IllegalArgumentException is length of {@code ids} args is more than
* {@link UserData#getMaxFieldClassificationIdsSize()}.
* @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
* already called.
* @throws NullPointerException if {@code ids} or any element on it is {@code null}.
- *
- * TODO(b/67867469):
- * - improve javadoc: explain relationship with UserData and how to check results
- * - unhide / remove testApi
- * - implement multiple ids
- *
- * @hide
*/
- @TestApi
public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
throwIfDestroyed();
throwIfDisableAutofillCalled();
@@ -375,6 +372,7 @@
Preconditions.checkArgumentInRange(ids.length, 1,
UserData.getMaxFieldClassificationIdsSize(), "ids length");
mFieldClassificationIds = ids;
+ mFlags |= FLAG_TRACK_CONTEXT_COMMITED;
return this;
}
@@ -423,9 +421,8 @@
* @throws IllegalStateException if either {@link #addDataset(Dataset)},
* {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
* {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
- * {link #setFieldClassificationIds(AutofillId...)} was already called.
+ * {@link #setFieldClassificationIds(AutofillId...)} was already called.
*/
- // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
public Builder disableAutofill(long duration) {
throwIfDestroyed();
if (duration <= 0) {
@@ -506,14 +503,13 @@
* {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
* {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
* {@link #setClientState(Bundle)},
- * or {link #setFieldClassificationIds(AutofillId...)}.
+ * or {@link #setFieldClassificationIds(AutofillId...)}.
* <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
* without any previous calls to {@link #addDataset(Dataset)}.
* </ol>
*
* @return A built response.
*/
- // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
public FillResponse build() {
throwIfDestroyed();
if (mAuthentication == null && mDatasets == null && mSaveInfo == null
diff --git a/core/java/android/service/autofill/Scorer.java b/core/java/android/service/autofill/Scorer.java
index f6a802a..c401855 100644
--- a/core/java/android/service/autofill/Scorer.java
+++ b/core/java/android/service/autofill/Scorer.java
@@ -15,21 +15,14 @@
*/
package android.service.autofill;
-import android.annotation.TestApi;
-
/**
* Helper class used to calculate a score.
*
- * <p>Typically used to calculate the field classification score between an actual
- * {@link android.view.autofill.AutofillValue} filled by the user and the expected value predicted
- * by an autofill service.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- * @hide
+ * <p>Typically used to calculate the
+ * <a href="AutofillService.html#FieldClassification">field classification</a> score between an
+ * actual {@link android.view.autofill.AutofillValue} filled by the user and the expected value
+ * predicted by an autofill service.
*/
-@TestApi
public interface Scorer {
}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 0d37815..f0cc360 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.ContentResolver;
import android.os.Parcel;
@@ -38,15 +37,9 @@
import java.util.ArrayList;
/**
- * Class used by service to improve autofillable fields detection by tracking the meaning of fields
- * manually edited by the user (when they match values provided by the service).
- *
- * TODO(b/67867469):
- * - improve javadoc / add link to section on AutofillService
- * - unhide / remove testApi
- * @hide
+ * Defines the user data used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
*/
-@TestApi
public final class UserData implements Parcelable {
private static final String TAG = "UserData";
@@ -110,12 +103,7 @@
/**
* A builder for {@link UserData} objects.
- *
- * TODO(b/67867469): unhide / remove testApi
- *
- * @hide
*/
- @TestApi
public static final class Builder {
private final InternalScorer mScorer;
private final ArrayList<String> mRemoteIds;
@@ -123,7 +111,7 @@
private boolean mDestroyed;
/**
- * Creates a new builder for the user data used for <a href="#FieldsClassification">fields
+ * Creates a new builder for the user data used for <a href="#FieldClassification">field
* classification</a>.
*
* @throws IllegalArgumentException if any of the following occurs:
@@ -288,14 +276,16 @@
}
/**
- * Gets the minimum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
+ * Gets the minimum length of values passed to the builder's constructor or
+ * or {@link Builder#add(String, String)}.
*/
public static int getMinValueLength() {
return getInt(AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, DEFAULT_MIN_VALUE_LENGTH);
}
/**
- * Gets the maximum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
+ * Gets the maximum length of values passed to the builder's constructor or
+ * or {@link Builder#add(String, String)}.
*/
public static int getMaxValueLength() {
return getInt(AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, DEFAULT_MAX_VALUE_LENGTH);
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index 2707f14..b94ccf9 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -33,8 +33,8 @@
* To extend this class, you must declare the service in your manifest file to require the
* {@link android.Manifest.permission#BIND_CARRIER_SERVICES} permission and include an intent
* filter with the {@link #CARRIER_SERVICE_INTERFACE}. If the service should have a long-lived
- * binding, set android.service.carrier.LONG_LIVED_BINDING to true in the service's metadata.
- * For example:
+ * binding, set <code>android.service.carrier.LONG_LIVED_BINDING</code> to <code>true</code> in the
+ * service's metadata. For example:
* </p>
*
* <pre>{@code
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 54b48b6..b7d33599 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,7 +41,7 @@
DEFAULT_FLAGS.put("new_settings_suggestion", "true");
DEFAULT_FLAGS.put("settings_search_v2", "false");
DEFAULT_FLAGS.put("settings_app_info_v2", "false");
- DEFAULT_FLAGS.put("settings_connected_device_v2", "false");
+ DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
DEFAULT_FLAGS.put("settings_battery_v2", "false");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
}
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
new file mode 100644
index 0000000..4805318
--- /dev/null
+++ b/core/java/android/util/StatsLog.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util;
+
+/**
+ * StatsLog provides an API for developers to send events to statsd. The events can be used to
+ * define custom metrics inside statsd. We will rate-limit how often the calls can be made inside
+ * statsd.
+ */
+public final class StatsLog extends StatsLogInternal {
+ private static final String TAG = "StatsManager";
+
+ private StatsLog() {}
+
+ /**
+ * Logs a start event.
+ *
+ * @param label developer-chosen label that is from [0, 16).
+ * @return True if the log request was sent to statsd.
+ */
+ public static boolean logStart(int label) {
+ if (label >= 0 && label < 16) {
+ StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__START);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Logs a stop event.
+ *
+ * @param label developer-chosen label that is from [0, 16).
+ * @return True if the log request was sent to statsd.
+ */
+ public static boolean logStop(int label) {
+ if (label >= 0 && label < 16) {
+ StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__STOP);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Logs an event that does not represent a start or stop boundary.
+ *
+ * @param label developer-chosen label that is from [0, 16).
+ * @return True if the log request was sent to statsd.
+ */
+ public static boolean logEvent(int label) {
+ if (label >= 0 && label < 16) {
+ StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__UNSPECIFIED);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ab5372a..02beee0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4299,6 +4299,38 @@
*/
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 @@
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 @@
mTooltipInfo.mTooltipPopup.hide();
mTooltipInfo.mTooltipPopup = null;
mTooltipInfo.mTooltipFromLongClick = false;
+ mTooltipInfo.clearAnchorPos();
if (mAttachInfo != null) {
mAttachInfo.mTooltipHost = null;
}
@@ -26906,11 +26941,9 @@
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 @@
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 c44c8dd..c5a94da 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -290,6 +290,7 @@
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 @@
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 @@
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 @@
}
/**
+ * @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 c6c42fa..3f8da093 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -324,6 +324,13 @@
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 @@
}
}
+ 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 @@
}
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 @@
}
if (stage != null) {
+ handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
@@ -7159,10 +7180,12 @@
}
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/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 6d84aa0..d0dbff0e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -1010,20 +1009,14 @@
}
/**
- * Gets the user data used for <a href="#FieldsClassification">fields classification</a>.
+ * Gets the user data used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
*
* <p><b>Note:</b> This method should only be called by an app providing an autofill service.
*
- * TODO(b/67867469):
- * - proper javadoc
- * - unhide / remove testApi
- *
* @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
* reset or if the caller currently does not have an enabled autofill service for the user.
- *
- * @hide
*/
- @TestApi
@Nullable public UserData getUserData() {
try {
return mService.getUserData();
@@ -1034,21 +1027,13 @@
}
/**
- * Sets the user data used for <a href="#FieldsClassification">fields classification</a>.
+ * Sets the user data used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>
*
* <p><b>Note:</b> This method should only be called by an app providing an autofill service,
* and it's ignored if the caller currently doesn't have an enabled autofill service for
* the user.
- *
- * TODO(b/67867469):
- * - proper javadoc
- * - unhide / remove testApi
- * - add unit tests:
- * - call set / get / verify
- *
- * @hide
*/
- @TestApi
public void setUserData(@Nullable UserData userData) {
try {
mService.setUserData(userData);
@@ -1058,13 +1043,17 @@
}
/**
- * TODO(b/67867469):
- * - proper javadoc
- * - mention this method in other places
- * - unhide / remove testApi
- * @hide
+ * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
+ * enabled.
+ *
+ * <p>As field classification is an expensive operation, it could be disabled, either
+ * temporarily (for example, because the service exceeded a rate-limit threshold) or
+ * permanently (for example, because the device is a low-level device).
+ *
+ * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+ * and it's ignored if the caller currently doesn't have an enabled autofill service for
+ * the user.
*/
- @TestApi
public boolean isFieldClassificationEnabled() {
try {
return mService.isFieldClassificationEnabled();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a050a3c..5d6d5bc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -11228,7 +11228,9 @@
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 @@
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 @@
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 4be6b28..e871003 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.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.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 @@
/**
* 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/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 17b98da..cc2646c 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -236,17 +236,15 @@
return INSTALL_SUCCEEDED;
}
- char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 2];
+ char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 1];
if (strlcpy(localTmpFileName, nativeLibPath.c_str(), sizeof(localTmpFileName))
!= nativeLibPath.size()) {
ALOGD("Couldn't allocate local file name for library");
return INSTALL_FAILED_INTERNAL_ERROR;
}
- *(localTmpFileName + nativeLibPath.size()) = '/';
-
if (strlcpy(localTmpFileName + nativeLibPath.size(), TMP_FILE_PATTERN,
- TMP_FILE_PATTERN_LEN - nativeLibPath.size()) != TMP_FILE_PATTERN_LEN) {
+ TMP_FILE_PATTERN_LEN + 1) != TMP_FILE_PATTERN_LEN) {
ALOGI("Couldn't allocate temporary file name for library");
return INSTALL_FAILED_INTERNAL_ERROR;
}
diff --git a/core/proto/android/graphics/point.proto b/core/proto/android/graphics/point.proto
new file mode 100644
index 0000000..5ae17cb
--- /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 915fe9d..ef7b6b0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -170,6 +170,8 @@
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/layout/activity_list_item_2.xml b/core/res/res/layout/activity_list_item_2.xml
index 608e986..af1963c 100644
--- a/core/res/res/layout/activity_list_item_2.xml
+++ b/core/res/res/layout/activity_list_item_2.xml
@@ -21,5 +21,6 @@
android:textAppearance="?attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:drawablePadding="14dip"
+ android:textAlignment="viewStart"
android:paddingStart="?attr/listPreferredItemPaddingStart"
android:paddingEnd="?attr/listPreferredItemPaddingEnd" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8377751..dc791cf 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 be7ce5f..4b2424f 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 0000000..230655d
--- /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 b03f054..1002453 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -335,6 +335,7 @@
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,
@@ -420,13 +421,6 @@
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- // TODO(b/67867469): Move autofill settings below to
- // BACKUP_BLACKLISTED_SYSTEM_SETTINGS once feature is moved out of experimental
- Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
- Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
- Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
- Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
- Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
Settings.Secure.ALWAYS_ON_VPN_APP,
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
@@ -436,6 +430,11 @@
Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+ Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
+ Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
+ Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
+ Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
+ Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
index 193e601..b255c7e 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
@@ -22,6 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := ExternalSharedPermsTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
index 9f44767..428b1f8 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
@@ -22,6 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := ExternalSharedPermsBTTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
index b4d354c..93ece86 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
@@ -22,6 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := ExternalSharedPermsDiffKeyTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
index 969e588..b348966 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
@@ -22,6 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := ExternalSharedPermsFLTestApp
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index abcccbd..c6b6c66 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
@@ -121,6 +122,21 @@
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/Credentials.java b/keystore/java/android/security/Credentials.java
index 6830a74..57db20b 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -60,10 +60,12 @@
/** Key prefix for user certificates. */
public static final String USER_CERTIFICATE = "USRCERT_";
- /** Key prefix for user private keys. */
+ /** Key prefix for user private and secret keys. */
public static final String USER_PRIVATE_KEY = "USRPKEY_";
- /** Key prefix for user secret keys. */
+ /** Key prefix for user secret keys.
+ * @deprecated use {@code USER_PRIVATE_KEY} for this category instead.
+ */
public static final String USER_SECRET_KEY = "USRSKEY_";
/** Key prefix for VPN. */
@@ -235,8 +237,7 @@
* Make sure every type is deleted. There can be all three types, so
* don't use a conditional here.
*/
- return deletePrivateKeyTypeForAlias(keystore, alias, uid)
- & deleteSecretKeyTypeForAlias(keystore, alias, uid)
+ return deleteUserKeyTypeForAlias(keystore, alias, uid)
& deleteCertificateTypesForAlias(keystore, alias, uid);
}
@@ -264,34 +265,27 @@
}
/**
- * Delete private key for a particular {@code alias}.
+ * Delete user key for a particular {@code alias}.
* Returns {@code true} if the entry no longer exists.
*/
- static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) {
- return deletePrivateKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
+ public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias) {
+ return deleteUserKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
}
/**
- * Delete private key for a particular {@code alias}.
+ * Delete user key for a particular {@code alias}.
* Returns {@code true} if the entry no longer exists.
*/
- static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
- return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid);
+ public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+ return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid) ||
+ keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
}
/**
- * Delete secret key for a particular {@code alias}.
+ * Delete legacy prefixed entry for a particular {@code alias}
* Returns {@code true} if the entry no longer exists.
*/
- public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) {
- return deleteSecretKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
- }
-
- /**
- * Delete secret key for a particular {@code alias}.
- * Returns {@code true} if the entry no longer exists.
- */
- public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+ public static boolean deleteLegacyKeyForAlias(KeyStore keystore, String alias, int uid) {
return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
}
}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index b4331b2..eca52cc 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 @@
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/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 988e32c..f1d1e16 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -305,7 +305,7 @@
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, (mKeySizeBits + 7) / 8);
int flags = 0;
- String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+ String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias();
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
boolean success = false;
try {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index f36c00c..55e6519 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -196,7 +196,7 @@
}
@NonNull
- public static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
+ private static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
@NonNull AndroidKeyStorePublicKey publicKey) {
String keyAlgorithm = publicKey.getAlgorithm();
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
@@ -212,17 +212,25 @@
}
@NonNull
- public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
+ private static KeyCharacteristics getKeyCharacteristics(@NonNull KeyStore keyStore,
+ @NonNull String alias, int uid)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(
- privateKeyAlias, null, null, uid, keyCharacteristics);
+ alias, null, null, uid, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about private key")
- .initCause(KeyStore.getKeyStoreException(errorCode));
+ new UnrecoverableKeyException("Failed to obtain information about key")
+ .initCause(KeyStore.getKeyStoreException(errorCode));
}
+ return keyCharacteristics;
+ }
+
+ @NonNull
+ private static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+ KeyCharacteristics keyCharacteristics)
+ throws UnrecoverableKeyException {
ExportResult exportResult = keyStore.exportKey(
privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid);
if (exportResult.resultCode != KeyStore.NO_ERROR) {
@@ -252,37 +260,56 @@
}
@NonNull
- public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
+ public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
@NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
+ return loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
+ getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+ }
+
+ @NonNull
+ private static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+ @NonNull KeyCharacteristics keyCharacteristics)
+ throws UnrecoverableKeyException {
AndroidKeyStorePublicKey publicKey =
- loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid);
+ loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
+ keyCharacteristics);
AndroidKeyStorePrivateKey privateKey =
AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
return new KeyPair(publicKey, privateKey);
}
@NonNull
- public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+ public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
@NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
- KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid);
+ return loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
+ getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+ }
+
+ @NonNull
+ private static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+ @NonNull KeyCharacteristics keyCharacteristics)
+ throws UnrecoverableKeyException {
+ KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
+ keyCharacteristics);
return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
}
@NonNull
- public static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String secretKeyAlias, int uid)
+ public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = keyStore.getKeyCharacteristics(
- secretKeyAlias, null, null, uid, keyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about key")
- .initCause(KeyStore.getKeyStoreException(errorCode));
- }
+ return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, privateKeyAlias, uid,
+ getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+ }
+ @NonNull
+ private static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
+ @NonNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)
+ throws UnrecoverableKeyException {
Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
if (keymasterAlgorithm == null) {
throw new UnrecoverableKeyException("Key algorithm unknown");
@@ -310,6 +337,29 @@
return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
}
+ public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid)
+ throws UnrecoverableKeyException {
+ KeyCharacteristics keyCharacteristics = getKeyCharacteristics(keyStore, userKeyAlias, uid);
+
+ Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
+ if (keymasterAlgorithm == null) {
+ throw new UnrecoverableKeyException("Key algorithm unknown");
+ }
+
+ if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC ||
+ keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES) {
+ return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid,
+ keyCharacteristics);
+ } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
+ keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
+ return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, userKeyAlias, uid,
+ keyCharacteristics);
+ } else {
+ throw new UnrecoverableKeyException("Key algorithm unknown");
+ }
+ }
+
/**
* Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
* The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 0379863..fdb885db 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -64,7 +64,10 @@
AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
String keyAliasInKeystore = keystoreKey.getAlias();
String entryAlias;
- if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+ if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
+ entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
+ } else if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)){
+ // key has legacy prefix
entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
} else {
throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index bab4010..d73a9e2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -89,18 +89,14 @@
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
UnrecoverableKeyException {
- if (isPrivateKeyEntry(alias)) {
- String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
- mKeyStore, privateKeyAlias, mUid);
- } else if (isSecretKeyEntry(alias)) {
- String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
- return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
- mKeyStore, secretKeyAlias, mUid);
- } else {
- // Key not found
- return null;
+ String userKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+ if (!mKeyStore.contains(userKeyAlias, mUid)) {
+ // try legacy prefix for backward compatibility
+ userKeyAlias = Credentials.USER_SECRET_KEY + alias;
+ if (!mKeyStore.contains(userKeyAlias, mUid)) return null;
}
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore, userKeyAlias,
+ mUid);
}
@Override
@@ -540,7 +536,7 @@
} else {
// Keep the stored private key around -- delete all other entry types
Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
+ Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
}
// Store the leaf certificate
@@ -565,7 +561,7 @@
Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
} else {
Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
+ Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
}
}
}
@@ -588,12 +584,17 @@
if (keyAliasInKeystore == null) {
throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
}
- if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
- throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
- + keyAliasInKeystore);
+ String keyAliasPrefix = Credentials.USER_PRIVATE_KEY;
+ if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
+ // try legacy prefix
+ keyAliasPrefix = Credentials.USER_SECRET_KEY;
+ if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
+ throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
+ + keyAliasInKeystore);
+ }
}
String keyEntryAlias =
- keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+ keyAliasInKeystore.substring(keyAliasPrefix.length());
if (!entryAlias.equals(keyEntryAlias)) {
throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
+ " alias: " + entryAlias + " != " + keyEntryAlias);
@@ -728,7 +729,7 @@
}
Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
- String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
+ String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + entryAlias;
int errorCode = mKeyStore.importKey(
keyAliasInKeystore,
args,
@@ -827,24 +828,10 @@
}
private boolean isKeyEntry(String alias) {
- return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
+ return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) ||
+ mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
}
- private boolean isPrivateKeyEntry(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
- }
-
- private boolean isSecretKeyEntry(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
- }
private boolean isCertificateEntry(String alias) {
if (alias == null) {
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index cf4347d..0811100 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -73,6 +73,33 @@
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 @@
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 0000000..6cf5fb2
--- /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 87677d4..1238d87 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -680,6 +680,40 @@
}
/**
+ * 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 73b489f..254b6be 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -55,7 +55,7 @@
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 @@
.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 0000000..865cad4
--- /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/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index afdd339..f41956c 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -27,9 +27,11 @@
#include <cutils/ashmem.h>
#include <log/log.h>
+#include <sstream>
#include "Properties.h"
#include "utils/TimeUtils.h"
+#include "utils/Trace.h"
namespace android {
namespace uirenderer {
@@ -150,6 +152,19 @@
(*mGlobalData)->reportJankType((JankType)i);
}
}
+
+ // Log daveys since they are weird and we don't know what they are (b/70339576)
+ if (totalDuration >= 700_ms) {
+ static int sDaveyCount = 0;
+ std::stringstream ss;
+ ss << "Davey! duration=" << ns2ms(totalDuration) << "ms; ";
+ for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
+ ss << FrameInfoNames[i] << "=" << frame[i] << ", ";
+ }
+ ALOGI("%s", ss.str().c_str());
+ // Just so we have something that counts up, the value is largely irrelevant
+ ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
+ }
}
void JankTracker::dumpData(int fd, const ProfileDataDescription* description,
diff --git a/nfc-extras/tests/Android.mk b/nfc-extras/tests/Android.mk
index be2bb53..34d6508 100644
--- a/nfc-extras/tests/Android.mk
+++ b/nfc-extras/tests/Android.mk
@@ -20,7 +20,8 @@
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
- com.android.nfc_extras
+ com.android.nfc_extras \
+ android.test.base.stubs
LOCAL_STATIC_JAVA_LIBRARIES := junit
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 27bc599..9f39321 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -49,6 +49,7 @@
private boolean mIsVerifyUnlockOnly;
private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
private SecurityCallback mSecurityCallback;
+ private AlertDialog mAlertDialog;
private final KeyguardUpdateMonitor mUpdateMonitor;
@@ -95,6 +96,10 @@
@Override
public void onPause() {
+ if (mAlertDialog != null) {
+ mAlertDialog.dismiss();
+ mAlertDialog = null;
+ }
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).onPause();
}
@@ -174,16 +179,20 @@
}
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/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index b194de4..eff84c6 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -76,6 +76,12 @@
void playTrustedSound();
/**
+ * When the bouncer is shown or hides
+ * @param shown
+ */
+ void onBouncerVisiblityChanged(boolean shown);
+
+ /**
* @return true if the screen is on
*/
boolean isScreenOn();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8fa66e0f..c4d9cf5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -613,6 +613,13 @@
}
@Override
+ public void onBouncerVisiblityChanged(boolean shown) {
+ synchronized (KeyguardViewMediator.this) {
+ adjustStatusBarLocked(shown);
+ }
+ }
+
+ @Override
public void playTrustedSound() {
KeyguardViewMediator.this.playTrustedSound();
}
@@ -1862,6 +1869,10 @@
}
private void adjustStatusBarLocked() {
+ adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */);
+ }
+
+ private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons) {
if (mStatusBarManager == null) {
mStatusBarManager = (StatusBarManager)
mContext.getSystemService(Context.STATUS_BAR_SERVICE);
@@ -1872,19 +1883,14 @@
// Disable aspects of the system/status/navigation bars that must not be re-enabled by
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
- if (mShowing) {
- // Permanently disable components not available when keyguard is enabled
- // (like recents). Temporary enable/disable (e.g. the "back" button) are
- // done in KeyguardHostView.
- flags |= StatusBarManager.DISABLE_RECENT;
- }
- if (isShowingAndNotOccluded()) {
- flags |= StatusBarManager.DISABLE_HOME;
+ if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
+ flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
}
if (DEBUG) {
Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
- + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
+ + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+ + " --> flags=0x" + Integer.toHexString(flags));
}
mStatusBarManager.disable(flags);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 663f206..8359690 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -24,6 +24,8 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.KeyguardManager;
+import android.app.trust.TrustManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -32,6 +34,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.AsyncTask.Status;
import android.os.Handler;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -225,6 +228,7 @@
}
};
+ private TrustManager mTrustManager;
protected Context mContext;
protected Handler mHandler;
TaskStackListenerImpl mTaskStackListener;
@@ -271,6 +275,8 @@
// Initialize the static configuration resources
mDummyStackView = new TaskStackView(mContext);
reloadResources();
+
+ mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
}
public void onBootCompleted() {
@@ -309,8 +315,7 @@
* {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
*/
public void onStartScreenPinning(Context context, int taskId) {
- SystemUIApplication app = (SystemUIApplication) context;
- StatusBar statusBar = app.getComponent(StatusBar.class);
+ final StatusBar statusBar = getStatusBar();
if (statusBar != null) {
statusBar.showScreenPinningRequest(taskId, false);
}
@@ -350,8 +355,8 @@
if (forceVisible || !ssp.isRecentsActivityVisible(isHomeStackVisible)) {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
- startRecentsActivity(runningTask, isHomeStackVisible.value || fromHome, animate,
- growTarget);
+ startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
+ isHomeStackVisible.value || fromHome, animate, growTarget);
}
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Failed to launch RecentsActivity", e);
@@ -442,8 +447,8 @@
// Otherwise, start the recents activity
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
- startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */,
- growTarget);
+ startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
+ isHomeStackVisible.value, true /* animate */, growTarget);
// Only close the other system windows if we are actually showing recents
ActivityManagerWrapper.getInstance().closeSystemWindows(
@@ -462,6 +467,12 @@
return;
}
+ // Skip preloading recents when keyguard is showing
+ final StatusBar statusBar = getStatusBar();
+ if (statusBar != null && statusBar.isKeyguardShowing()) {
+ return;
+ }
+
// Preload only the raw task list into a new load plan (which will be consumed by the
// RecentsActivity) only if there is a task to animate to. Post this to ensure that we
// don't block the touch feedback on the nav bar button which triggers this.
@@ -942,9 +953,27 @@
}
/**
- * Shows the recents activity
+ * Shows the recents activity after dismissing the keyguard if visible
*/
- protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
+ protected void startRecentsActivityAndDismissKeyguardIfNeeded(
+ final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
+ final boolean animate, final int growTarget) {
+ // Preload only if device for current user is unlocked
+ final StatusBar statusBar = getStatusBar();
+ if (statusBar != null && statusBar.isKeyguardShowing()) {
+ statusBar.executeRunnableDismissingKeyguard(() -> {
+ // Flush trustmanager before checking device locked per user when preloading
+ mTrustManager.reportKeyguardShowingChanged();
+ mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,
+ animate, growTarget));
+ }, null, true /* dismissShade */, false /* afterKeyguardGone */,
+ true /* deferred */);
+ } else {
+ startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);
+ }
+ }
+
+ private void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
boolean isHomeStackVisible, boolean animate, int growTarget) {
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
@@ -1033,6 +1062,10 @@
return result;
}
+ private StatusBar getStatusBar() {
+ return ((SystemUIApplication) mContext).getComponent(StatusBar.class);
+ }
+
/**
* Starts the recents activity.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c281379..b71ebfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.UserHandle;
@@ -122,6 +121,8 @@
// Split up the work over multiple frames.
DejankUtils.postAfterTraversal(mShowRunnable);
+
+ mCallback.onBouncerVisiblityChanged(true /* shown */);
}
private final Runnable mShowRunnable = new Runnable() {
@@ -182,6 +183,7 @@
mDismissCallbackRegistry.notifyDismissCancelled();
}
mFalsingManager.onBouncerHidden();
+ mCallback.onBouncerVisiblityChanged(false /* shown */);
cancelShowRunnable();
if (mKeyguardView != null) {
mKeyguardView.cancelDismissAction();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
index 01679dd..ad31cc5 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.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.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 @@
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 @@
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 @@
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 @@
@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 5ec3850..270a762 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -772,7 +772,6 @@
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
synchronized (mLock) {
mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
- onUserStateChangedLocked(getCurrentUserStateLocked());
}
}
@@ -2985,11 +2984,14 @@
}
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/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index f057112..56a9534 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -28,6 +28,7 @@
import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
+import com.android.internal.util.DumpUtils;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
@@ -45,6 +46,8 @@
private AccessibilityServiceInfo mUiAutomationServiceInfo;
+ private AccessibilityClientConnection.SystemSupport mSystemSupport;
+
private int mUiAutomationFlags;
private IBinder mUiAutomationServiceOwner;
@@ -92,6 +95,7 @@
return;
}
+ mSystemSupport = systemSupport;
mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
mainHandler, lock, securityPolicy, systemSupport, windowManagerInternal,
globalActionPerfomer);
@@ -169,6 +173,7 @@
mUiAutomationServiceOwner.unlinkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
mUiAutomationServiceOwner = null;
}
+ mSystemSupport.onClientChange(false);
}
private class UiAutomationService extends AccessibilityClientConnection {
@@ -224,6 +229,17 @@
return true;
}
+ @Override
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
+ synchronized (mLock) {
+ pw.append("Ui Automation[eventTypes="
+ + AccessibilityEvent.eventTypeToString(mEventTypes));
+ pw.append(", notificationTimeout=" + mNotificationTimeout);
+ pw.append("]");
+ }
+ }
+
// Since this isn't really an accessibility service, several methods are just stubbed here.
@Override
public boolean setSoftKeyboardShowMode(int mode) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 56f5f64..ceae93c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -512,7 +512,6 @@
}
final AutofillId[] fieldClassificationIds = response.getFieldClassificationIds();
- // TODO(b/67867469): remove once feature is finished (or use method from AFM to check)
if (fieldClassificationIds != null && !mService.isFieldClassificationEnabled()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestFlags);
@@ -1662,7 +1661,7 @@
isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
mViewStates.put(id, viewState);
- // TODO(b/67867469): for optimization purposes, should also ignore if change is
+ // TODO(b/70407264): for optimization purposes, should also ignore if change is
// detectable, and batch-send them when the session is finished (but that will
// require tracking detectable fields on AutofillManager)
if (isIgnored) {
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 4adcb99..94b06b6 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_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.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 @@
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);
+ 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());
- }
-
- // 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 @@
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 @@
+ " 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 @@
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 @@
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 @@
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 @@
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 @@
final long oldId = Binder.clearCallingIdentity();
try {
- mTransportManager.describeTransport(
+ mTransportManager.updateTransportAttributes(
transportComponent,
name,
configurationIntent,
@@ -3093,23 +3069,16 @@
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 @@
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 @@
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 @@
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 1f3ebf9..f185443 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.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 @@
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 @@
}
@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 @@
* 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 @@
@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 @@
@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 4c78348..f29a9c2 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 @@
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 690922f..b21b072 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 @@
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 @@
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 0000000..26bf92c
--- /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 a2505a8..9fb2681 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -31,6 +31,7 @@
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 924e736..04d292f 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -421,7 +421,9 @@
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 @@
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/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index 24d8d1e..9877717 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -70,7 +70,10 @@
/**
* Handler that periodically updates the entropy on disk.
*/
- private final Handler mHandler = new Handler() {
+ private final Handler mHandler = new Handler(IoThread.getHandler().getLooper()) {
+ // IMPLEMENTATION NOTE: This handler runs on the I/O thread to avoid I/O on the main thread.
+ // The reason we're using our own Handler instead of IoThread.getHandler() is to create our
+ // own ID space for the "what" parameter of messages seen by the handler.
@Override
public void handleMessage(Message msg) {
if (msg.what != ENTROPY_WHAT) {
@@ -115,7 +118,12 @@
IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
broadcastFilter.addAction(Intent.ACTION_POWER_CONNECTED);
broadcastFilter.addAction(Intent.ACTION_REBOOT);
- context.registerReceiver(mBroadcastReceiver, broadcastFilter);
+ context.registerReceiver(
+ mBroadcastReceiver,
+ broadcastFilter,
+ null, // do not require broadcaster to hold any permissions
+ mHandler // process received broadcasts on the I/O thread instead of the main thread
+ );
}
private void scheduleEntropyWriter() {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 25c3fe4..880e1e4 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -35,6 +35,7 @@
import com.android.internal.os.TransferPipe;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodClient;
@@ -4663,23 +4664,31 @@
@ShellCommandResult
@Override
public int onCommand(@Nullable String cmd) {
- if (cmd == null) {
- return handleDefaultCommands(cmd);
+ // For existing "adb shell ime <command>".
+ if ("ime".equals(cmd)) {
+ final String imeCommand = getNextArg();
+ if (imeCommand == null || "help".equals(imeCommand) || "-h".equals(imeCommand)) {
+ onImeCommandHelp();
+ return ShellCommandResult.SUCCESS;
+ }
+ switch (imeCommand) {
+ case "list":
+ return mService.handleShellCommandListInputMethods(this);
+ case "enable":
+ return mService.handleShellCommandEnableDisableInputMethod(this, true);
+ case "disable":
+ return mService.handleShellCommandEnableDisableInputMethod(this, false);
+ case "set":
+ return mService.handleShellCommandSetInputMethod(this);
+ case "reset":
+ return mService.handleShellCommandResetInputMethod(this);
+ default:
+ getOutPrintWriter().println("Unknown command: " + imeCommand);
+ return ShellCommandResult.FAILURE;
+ }
}
- switch (cmd) {
- case "list":
- return mService.handleShellCommandListInputMethods(this);
- case "enable":
- return mService.handleShellCommandEnableDisableInputMethod(this, true);
- case "disable":
- return mService.handleShellCommandEnableDisableInputMethod(this, false);
- case "set":
- return mService.handleShellCommandSetInputMethod(this);
- case "reset-ime":
- return mService.handleShellCommandResetInputMethod(this);
- default:
- return handleDefaultCommands(cmd);
- }
+
+ return handleDefaultCommands(cmd);
}
@BinderThread
@@ -4691,19 +4700,48 @@
pw.println(" Prints this help text.");
pw.println(" dump [options]");
pw.println(" Synonym of dumpsys.");
- pw.println(" list [-a] [-s]");
- pw.println(" prints all enabled input methods.");
- pw.println(" -a: see all input methods");
- pw.println(" -s: only a single summary line of each");
- pw.println(" enable <ID>");
- pw.println(" allows the given input method ID to be used.");
- pw.println(" disable <ID>");
- pw.println(" disallows the given input method ID to be used.");
- pw.println(" set <ID>");
- pw.println(" switches to the given input method ID.");
- pw.println(" reset-ime");
- pw.println(" reset currently selected/enabled IMEs to the default ones as if");
- pw.println(" the device is initially booted with the current locale.");
+ pw.println(" ime <command> [options]");
+ pw.println(" Manipulate IMEs. Run \"ime help\" for details.");
+ }
+ }
+
+ private void onImeCommandHelp() {
+ try (IndentingPrintWriter pw =
+ new IndentingPrintWriter(getOutPrintWriter(), " ", 100)) {
+ pw.println("ime <command>:");
+ pw.increaseIndent();
+
+ pw.println("list [-a] [-s]");
+ pw.increaseIndent();
+ pw.println("prints all enabled input methods.");
+ pw.increaseIndent();
+ pw.println("-a: see all input methods");
+ pw.println("-s: only a single summary line of each");
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+
+ pw.println("enable <ID>");
+ pw.increaseIndent();
+ pw.println("allows the given input method ID to be used.");
+ pw.decreaseIndent();
+
+ pw.println("disable <ID>");
+ pw.increaseIndent();
+ pw.println("disallows the given input method ID to be used.");
+ pw.decreaseIndent();
+
+ pw.println("set <ID>");
+ pw.increaseIndent();
+ pw.println("switches to the given input method ID.");
+ pw.decreaseIndent();
+
+ pw.println("reset");
+ pw.increaseIndent();
+ pw.println("reset currently selected/enabled IMEs to the default ones as if "
+ + "the device is initially booted with the current locale.");
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
}
}
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 2116d9a..e9eb3b3 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -827,15 +827,15 @@
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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7f0b508..7ecb9ce 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -31,8 +31,11 @@
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.usage.StorageStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -160,7 +163,8 @@
* watch for and manage dynamically added storage, such as SD cards and USB mass
* storage. Also decides how storage should be presented to users on the device.
*/
-class StorageManagerService extends IStorageManager.Stub implements Watchdog.Monitor {
+class StorageManagerService extends IStorageManager.Stub
+ implements Watchdog.Monitor, ScreenObserver {
// Static direct instance pointer for the tightly-coupled idle service to use
static StorageManagerService sSelf = null;
@@ -402,6 +406,7 @@
private volatile boolean mSystemReady = false;
private volatile boolean mBootCompleted = false;
private volatile boolean mDaemonConnected = false;
+ private volatile boolean mSecureKeyguardShowing = true;
private PackageManagerService mPms;
@@ -827,6 +832,7 @@
mVold.onUserStarted(userId);
mStoraged.onUserStarted(userId);
}
+ mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -878,6 +884,24 @@
}
}
+ @Override
+ public void onAwakeStateChanged(boolean isAwake) {
+ // Ignored
+ }
+
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
+ // Push down current secure keyguard status so that we ignore malicious
+ // USB devices while locked.
+ mSecureKeyguardShowing = isShowing
+ && mContext.getSystemService(KeyguardManager.class).isDeviceSecure();
+ try {
+ mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+
void runIdleMaintenance(Runnable callback) {
mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
}
@@ -1414,6 +1438,9 @@
}
private void systemReady() {
+ LocalServices.getService(ActivityManagerInternal.class)
+ .registerScreenObserver(this);
+
mSystemReady = true;
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9366f6e..51976b1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -210,6 +210,7 @@
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -1627,6 +1628,8 @@
}
}
+ final List<ScreenObserver> mScreenObservers = new ArrayList<>();
+
final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
@@ -1749,13 +1752,13 @@
static final int LOG_STACK_STATE = 60;
static final int VR_MODE_CHANGE_MSG = 61;
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
- static final int NOTIFY_VR_SLEEPING_MSG = 65;
+ static final int DISPATCH_SCREEN_AWAKE_MSG = 64;
+ static final int DISPATCH_SCREEN_KEYGUARD_MSG = 65;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
- static final int NOTIFY_VR_KEYGUARD_MSG = 74;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2391,11 +2394,17 @@
}
}
} break;
- case NOTIFY_VR_SLEEPING_MSG: {
- notifyVrManagerOfSleepState(msg.arg1 != 0);
+ case DISPATCH_SCREEN_AWAKE_MSG: {
+ final boolean isAwake = msg.arg1 != 0;
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+ }
} break;
- case NOTIFY_VR_KEYGUARD_MSG: {
- notifyVrManagerOfKeyguardState(msg.arg1 != 0);
+ case DISPATCH_SCREEN_KEYGUARD_MSG: {
+ final boolean isShowing = msg.arg1 != 0;
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
+ }
} break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
@@ -2947,9 +2956,11 @@
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()
@@ -3292,32 +3303,6 @@
mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
}
- private void sendNotifyVrManagerOfSleepState(boolean isSleeping) {
- mHandler.sendMessage(
- mHandler.obtainMessage(NOTIFY_VR_SLEEPING_MSG, isSleeping ? 1 : 0, 0));
- }
-
- private void notifyVrManagerOfSleepState(boolean isSleeping) {
- final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- if (vrService == null) {
- return;
- }
- vrService.onSleepStateChanged(isSleeping);
- }
-
- private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) {
- mHandler.sendMessage(
- mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0));
- }
-
- private void notifyVrManagerOfKeyguardState(boolean isShowing) {
- final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- if (vrService == null) {
- return;
- }
- vrService.onKeyguardStateChanged(isShowing);
- }
-
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -12432,7 +12417,8 @@
if (wasAwake != isAwake) {
// Also update state in a special way for running foreground services UI.
mServices.updateScreenStateLocked(isAwake);
- sendNotifyVrManagerOfSleepState(!isAwake);
+ mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
+ .sendToTarget();
}
}
}
@@ -12588,7 +12574,9 @@
Binder.restoreCallingIdentity(ident);
}
}
- sendNotifyVrManagerOfKeyguardState(showing);
+
+ mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, showing ? 1 : 0, 0)
+ .sendToTarget();
}
@Override
@@ -24525,6 +24513,11 @@
}
return false;
}
+
+ @Override
+ public void registerScreenObserver(ScreenObserver observer) {
+ mScreenObservers.add(observer);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 54938eb..4f60e17 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.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 @@
if (cmd == null) {
return handleDefaultCommands(cmd);
}
- PrintWriter pw = getOutPrintWriter();
+ final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
case "start":
@@ -1328,65 +1329,95 @@
@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/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 0ffc6b8..91b3315 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -2254,7 +2254,7 @@
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;
@@ -2339,7 +2339,7 @@
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);
@@ -2434,7 +2434,8 @@
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 8fdbcf6c..3064144 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 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 @@
}
@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 @@
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 @@
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 0000000..e8ac547
--- /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
similarity index 97%
rename from services/core/java/com/android/server/broadcastradio/Convert.java
rename to services/core/java/com/android/server/broadcastradio/hal1/Convert.java
index 125554f..80c7762 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
similarity index 99%
rename from services/core/java/com/android/server/broadcastradio/Tuner.java
rename to services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index 2ea4271..cce534d 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
similarity index 98%
rename from services/core/java/com/android/server/broadcastradio/TunerCallback.java
rename to services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 2460c67..673ff88 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 0000000..7629477
--- /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 0000000..c3394e9
--- /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 0000000..34c1b0c
--- /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 009f10e7..bcb57ef 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 @@
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 90c912a..1ad0cf9 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 @@
/**
* 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 @@
/**
* 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 @@
}
/**
+ * 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 @@
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 00252bc..412d43d 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 @@
/**
* 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 @@
}
/**
+ * 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/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index b324002..3bd6446 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1328,6 +1328,14 @@
} else if (!mStarted) {
// start GPS
startNavigating(singleShot);
+ } else {
+ // GNSS Engine is already ON, but no GPS_CAPABILITY_SCHEDULING
+ mAlarmManager.cancel(mTimeoutIntent);
+ if (mFixInterval >= NO_FIX_TIMEOUT) {
+ // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
+ // and our fix interval is not short
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent); }
}
} else {
updateClientUids(new WorkSource());
diff --git a/services/core/java/com/android/server/location/NanoAppStateManager.java b/services/core/java/com/android/server/location/NanoAppStateManager.java
index 81c4784..9869626 100644
--- a/services/core/java/com/android/server/location/NanoAppStateManager.java
+++ b/services/core/java/com/android/server/location/NanoAppStateManager.java
@@ -24,6 +24,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
/**
@@ -153,11 +154,12 @@
nanoAppIdSet.add(appInfo.appId);
}
- for (int nanoAppHandle : mNanoAppHash.keySet()) {
- NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppHandle);
+ Iterator<NanoAppInstanceInfo> iterator = mNanoAppHash.values().iterator();
+ while (iterator.hasNext()) {
+ NanoAppInstanceInfo info = iterator.next();
if (info.getContexthubId() == contextHubId &&
!nanoAppIdSet.contains(info.getAppId())) {
- mNanoAppHash.remove(nanoAppHandle);
+ iterator.remove();
}
}
}
diff --git a/services/core/java/com/android/server/location/OWNERS b/services/core/java/com/android/server/location/OWNERS
index d3c0ea8..5f0369b 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 0000000..e4d2b953
--- /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 cd85a97..6a189ef 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 @@
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 4e0751b..d4dede1 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
@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 0000000..457fdc1
--- /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 c97c66e..9002292 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 @@
* @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 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/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 7ebef83..f4ee0ce 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -40,6 +40,7 @@
public static final int DUMP_COMPILER_STATS = 1 << 21;
public static final int DUMP_CHANGES = 1 << 22;
public static final int DUMP_VOLUMES = 1 << 23;
+ public static final int DUMP_SERVICE_PERMISSIONS = 1 << 24;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -92,4 +93,4 @@
public void setSharedUser(SharedUserSetting user) {
mSharedUser = user;
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f106391..696d895 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -173,6 +173,7 @@
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.ParseFlags;
+import android.content.pm.PackageParser.ServiceIntentInfo;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
@@ -650,18 +651,15 @@
@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 +740,6 @@
@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 +2440,6 @@
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 +2449,7 @@
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 +2727,9 @@
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 +2898,18 @@
// 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 +8135,7 @@
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 +8488,7 @@
// 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 +8596,7 @@
}
// 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 +9617,12 @@
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,23 +9636,655 @@
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;
+ }
+
+ /**
+ * 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.
+ */
+ @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 {
+ 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);
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ /**
+ * 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 @Nullable String getRealPackageName(@NonNull PackageParser.Package pkg,
+ @Nullable String renamedPkgName) {
+ if (pkg.mOriginalPackages == null || !pkg.mOriginalPackages.contains(renamedPkgName)) {
+ return null;
+ }
+ return pkg.mRealPackage;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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);
+ }
+
+ if (Build.IS_DEBUGGABLE &&
+ pkg.isPrivileged() &&
+ !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
+ }
+
+ // Initialize package source and resource directories
+ final File scanFile = new File(pkg.codePath);
+ final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
+ final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
+
+ // 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;
+
+ if (!needToDeriveAbi) {
+ if (pkgSetting != null) {
+ primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
+ secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
+ } else {
+ // Re-scanning a system package after uninstalling updates; need to derive ABI
+ needToDeriveAbi = true;
+ }
+ }
+
+ 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;
+ }
+
+ 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);
+
+ // File a report about this.
+ String msg = "New package " + pkgSetting.realName
+ + " renamed to replace old package " + pkgSetting.name;
+ reportSettingsProblem(Log.WARN, msg);
+ }
+
+ 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 (!isPlatformPackage) {
+ // Get all of our default paths setup
+ pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
+ }
+
+ final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
+
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ if (needToDeriveAbi) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+ final boolean extractNativeLibs = !pkg.isLibrary();
+ derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ // Some system apps still use directory structure for native libraries
+ // in which case we might end up not detecting abi solely based on apk
+ // structure. Try to detect abi based on directory structure.
+ if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
+ pkg.applicationInfo.primaryCpuAbi == null) {
+ setBundledAppAbisAndRoots(pkg, pkgSetting);
+ setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ }
+ } else {
+ // This is not a first boot or an upgrade, don't bother deriving the
+ // ABI during the scan. Instead, trust the value that was stored in the
+ // package setting.
+ pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
+ pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
+
+ 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);
+ }
+ }
+ } else {
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new codepath.
+ pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
+ pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
+ }
+
+ // Set native library paths again. For moves, the path will be updated based on the
+ // 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, 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 (isPlatformPackage) {
+ pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?
+ Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
+ }
+
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
+ Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
+ " for package " + pkg.packageName);
+ }
+ }
+
+ pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
+ pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
+ pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
+
+ // Copy the derived override back to the parsed package, so that we can
+ // update the package settings accordingly.
+ pkg.cpuAbiOverride = cpuAbiOverride;
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName
+ + " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
+ + pkg.applicationInfo.nativeLibraryRootRequiresIsa);
+ }
+
+ // Push the derived path down into PackageSettings so we know what to
+ // clean up at uninstall time.
+ pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
+
+ if (DEBUG_ABI_SELECTION) {
+ Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
+ " primary=" + pkg.applicationInfo.primaryCpuAbi +
+ " secondary=" + pkg.applicationInfo.secondaryCpuAbi);
+ }
+
+ 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.
+ //
+ // 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.
+ changedAbiCodePath =
+ adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
+ }
+
+ if (isUnderFactoryTest && pkg.requestedPermissions.contains(
+ android.Manifest.permission.FACTORY_TEST)) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
+ }
+
+ if (isSystemApp(pkg)) {
+ pkgSetting.isOrphaned = true;
+ }
+
+ // Take care of first install / last update times.
+ final long scanFileTime = getLastModifiedTime(pkg);
+ if (currentTime != 0) {
+ if (pkgSetting.firstInstallTime == 0) {
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
+ } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
+ pkgSetting.lastUpdateTime = currentTime;
+ }
+ } else if (pkgSetting.firstInstallTime == 0) {
+ // We need *something*. Take time time stamp of the file.
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
+ } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+ if (scanFileTime != pkgSetting.timeStamp) {
+ // A package on the system image has changed; consider this
+ // to be an update.
+ pkgSetting.lastUpdateTime = scanFileTime;
+ }
+ }
+ pkgSetting.setTimeStamp(scanFileTime);
+
+ return new ScanResult(true, pkgSetting, changedAbiCodePath);
}
/**
@@ -9701,490 +10335,6 @@
}
}
- private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
- final @ParseFlags int parseFlags, final @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user)
- throws PackageManagerException {
- 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)) {
- PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
- }
-
- // Initialize package source and resource directories
- final File scanFile = new File(pkg.codePath);
- 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);
- } 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);
- }
-
- 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;
-
-
- // 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);
- }
- }
-
- 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);
- }
- }
-
- 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);
- }
- }
- }
- }
- }
-
- pkg.applicationInfo.processName = fixProcessName(
- pkg.applicationInfo.packageName,
- pkg.applicationInfo.processName);
-
- if (pkg != mPlatformPackage) {
- // Get all of our default paths setup
- pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
- }
-
- final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
-
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- if (needToDeriveAbi) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final boolean extractNativeLibs = !pkg.isLibrary();
- derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs, mAppLib32InstallDir);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Some system apps still use directory structure for native libraries
- // in which case we might end up not detecting abi solely based on apk
- // structure. Try to detect abi based on directory structure.
- if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
- pkg.applicationInfo.primaryCpuAbi == null) {
- setBundledAppAbisAndRoots(pkg, pkgSetting);
- setNativeLibraryPaths(pkg, mAppLib32InstallDir);
- }
- } else {
- // This is not a first boot or an upgrade, don't bother deriving the
- // ABI during the scan. Instead, trust the value that was stored in the
- // package setting.
- pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
- pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
-
- setNativeLibraryPaths(pkg, mAppLib32InstallDir);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
- pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +
- pkg.applicationInfo.secondaryCpuAbi);
- }
- }
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new codepath.
- pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
- pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // 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);
- }
-
- // 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) {
- pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?
- Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
- }
-
- // If there's a mismatch between the abi-override in the package setting
- // and the abiOverride specified for the install. Warn about this because we
- // would've already compiled the app without taking the package setting into
- // account.
- if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
- Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
- " for package " + pkg.packageName);
- }
- }
-
- pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
- pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
- pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
-
- // Copy the derived override back to the parsed package, so that we can
- // update the package settings accordingly.
- pkg.cpuAbiOverride = cpuAbiOverride;
-
- if (DEBUG_ABI_SELECTION) {
- Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName
- + " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
- + pkg.applicationInfo.nativeLibraryRootRequiresIsa);
- }
-
- // Push the derived path down into PackageSettings so we know what to
- // clean up at uninstall time.
- pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
-
- if (DEBUG_ABI_SELECTION) {
- Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
- " primary=" + pkg.applicationInfo.primaryCpuAbi +
- " 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.
- //
- // 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);
- }
-
- if (mFactoryTest && pkg.requestedPermissions.contains(
- android.Manifest.permission.FACTORY_TEST)) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
- }
-
- if (isSystemApp(pkg)) {
- pkgSetting.isOrphaned = true;
- }
-
- // Take care of first install / last update times.
- final long scanFileTime = getLastModifiedTime(pkg);
- if (currentTime != 0) {
- if (pkgSetting.firstInstallTime == 0) {
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
- } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
- pkgSetting.lastUpdateTime = currentTime;
- }
- } else if (pkgSetting.firstInstallTime == 0) {
- // We need *something*. Take time time stamp of the file.
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
- } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
- if (scanFileTime != pkgSetting.timeStamp) {
- // A package on the system image has changed; consider this
- // to be an update.
- pkgSetting.lastUpdateTime = scanFileTime;
- }
- }
- pkgSetting.setTimeStamp(scanFileTime);
-
- if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
- if (nonMutatedPs != null) {
- synchronized (mPackages) {
- mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs);
- }
- }
- } 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;
- }
-
/**
* Applies policy to the parsed package based upon the given policy flags.
* Ensures the package is in a good state.
@@ -10192,7 +10342,7 @@
* 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 +10731,7 @@
* 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 +11084,11 @@
* 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 +11238,7 @@
// 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 +11254,9 @@
* 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 +11326,15 @@
+ (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) {
@@ -12409,7 +12559,12 @@
out.print(' ');
filter.service.printComponentShortName(out);
out.print(" filter ");
- out.println(Integer.toHexString(System.identityHashCode(filter)));
+ out.print(Integer.toHexString(System.identityHashCode(filter)));
+ if (filter.service.info.permission != null) {
+ out.print(" permission "); out.println(filter.service.info.permission);
+ } else {
+ out.println();
+ }
}
@Override
@@ -16621,7 +16776,7 @@
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 +17102,7 @@
return name.startsWith("vmdl") && name.endsWith(".tmp");
}
};
- for (File file : mDrmAppPrivateInstallDir.listFiles(filter)) {
+ for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
file.delete();
}
}
@@ -20402,6 +20557,7 @@
pw.println(" dexopt: dump dexopt state");
pw.println(" compiler-stats: dump compiler statistics");
pw.println(" enabled-overlays: dump list of enabled overlay packages");
+ pw.println(" service-permissions: dump permissions required by services");
pw.println(" <package.name>: info about given package");
return;
} else if ("--checkin".equals(opt)) {
@@ -20537,6 +20693,8 @@
dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
} else if ("changes".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_CHANGES);
+ } else if ("service-permissions".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
} else if ("write".equals(cmd)) {
synchronized (mPackages) {
mSettings.writeLPr();
@@ -20917,6 +21075,25 @@
ipw.decreaseIndent();
}
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+ && packageName == null) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ pw.println("Service permissions:");
+
+ final Iterator<ServiceIntentInfo> filterIterator = mServices.filterIterator();
+ while (filterIterator.hasNext()) {
+ final ServiceIntentInfo info = filterIterator.next();
+ final ServiceInfo serviceInfo = info.service.info;
+ final String permission = serviceInfo.permission;
+ if (permission != null) {
+ pw.print(" ");
+ pw.print(serviceInfo.getComponentName().flattenToShortString());
+ pw.print(": ");
+ pw.println(permission);
+ }
+ }
+ }
+
if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
if (dumpState.onTitlePrinted()) pw.println();
dumpDexoptStateLPr(pw, packageName);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 9733624..809e16c 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 @@
* 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 @@
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 f0ce3c9..fbf3d82 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 @@
// 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 @@
* 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 @@
}
synchronized (sPolicies) {
- sPolicies = policies;
+ sPolicies.clear();
+ sPolicies.addAll(policies);
+ sPolicyRead = true;
if (DEBUG_POLICY_ORDER) {
for (Policy policy : sPolicies) {
@@ -280,6 +290,12 @@
*/
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 4a5772f..648f847 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 @@
(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 4b13404..4564988 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 @@
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 @@
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 7c75f77..9752a57 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 @@
// 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/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 7b1e12e..35b6ad3 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -59,13 +59,6 @@
int userId, int processId, @NonNull ComponentName calling);
/**
- * Set whether the system has acquired a sleep token.
- *
- * @param isAsleep is {@code true} if the device is asleep, or {@code false} otherwise.
- */
- public abstract void onSleepStateChanged(boolean isAsleep);
-
- /**
* Set whether the display used for VR output is on.
*
* @param isScreenOn is {@code true} if the display is on and can receive commands,
@@ -74,13 +67,6 @@
public abstract void onScreenStateChanged(boolean isScreenOn);
/**
- * Set whether the keyguard is currently active/showing.
- *
- * @param isShowing is {@code true} if the keyguard is active/showing.
- */
- public abstract void onKeyguardStateChanged(boolean isShowing);
-
- /**
* Return NO_ERROR if the given package is installed on the device and enabled as a
* VrListenerService for the given current user, or a negative error code indicating a failure.
*
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 7d55b68..21c6889 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.INotificationManager;
@@ -105,7 +106,8 @@
*
* @hide
*/
-public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
+public class VrManagerService extends SystemService
+ implements EnabledComponentChangeListener, ScreenObserver {
public static final String TAG = "VrManagerService";
static final boolean DBG = false;
@@ -237,15 +239,17 @@
}
}
- private void setSleepState(boolean isAsleep) {
- setSystemState(FLAG_AWAKE, !isAsleep);
- }
-
private void setScreenOn(boolean isScreenOn) {
setSystemState(FLAG_SCREEN_ON, isScreenOn);
}
- private void setKeyguardShowing(boolean isShowing) {
+ @Override
+ public void onAwakeStateChanged(boolean isAwake) {
+ setSystemState(FLAG_AWAKE, isAwake);
+ }
+
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing);
}
@@ -706,21 +710,11 @@
}
@Override
- public void onSleepStateChanged(boolean isAsleep) {
- VrManagerService.this.setSleepState(isAsleep);
- }
-
- @Override
public void onScreenStateChanged(boolean isScreenOn) {
VrManagerService.this.setScreenOn(isScreenOn);
}
@Override
- public void onKeyguardStateChanged(boolean isShowing) {
- VrManagerService.this.setKeyguardShowing(isShowing);
- }
-
- @Override
public boolean isCurrentVrListener(String packageName, int userId) {
return VrManagerService.this.isCurrentVrListener(packageName, userId);
}
@@ -773,6 +767,9 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ LocalServices.getService(ActivityManagerInternal.class)
+ .registerScreenObserver(this);
+
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 95b139a..2bda80d 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 @@
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 9f65ba3..b214d35 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.FileOutputStream;
import java.io.DataOutputStream;
+import android.os.StrictMode;
import android.util.Slog;
import android.os.Debug;
@@ -40,22 +41,28 @@
}
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 a12c2c4..d2cbf88 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.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 @@
@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 b08eb18..e653e7d 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 @@
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 ca9f3a9..fa7ea2f 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 @@
// 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 bb5706c..4dfe290 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 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 @@
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 @@
}
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 @@
}
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 @@
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 84ad576..5abda27 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 @@
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 a787543..9fc9f3c 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 @@
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());
@@ -4609,7 +4609,6 @@
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;
@@ -4623,8 +4622,6 @@
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;
@@ -4901,17 +4898,6 @@
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);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 2358695..b9dc9db 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 @@
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 @@
*/
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 6bebcf4..ddc1eac 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.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 @@
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 @@
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 222ac5b..9892146 100644
--- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
+++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
@@ -257,7 +257,7 @@
{ "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 @@
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 @@
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 63339e9..42eb873 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -592,18 +592,18 @@
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 d0ba005..39f2c05 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -406,7 +406,7 @@
}
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 @@
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 @@
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 f5381a8..be1ad72 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -678,7 +678,7 @@
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 9163037..5b9e3a1 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 @@
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 @@
* @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 2080b72..bead31f 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.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 @@
}
@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 @@
@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 @@
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 @@
// 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 2cb4a69..13623e5 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 @@
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.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 @@
(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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull();
assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
- mTransport2.stub);
+ mTransport2.binderInterface);
}
@Test
@@ -356,7 +391,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 ec09f90..0000000
--- 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/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index dbebd01..45ecbfb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -153,13 +153,14 @@
}
@Test
- public void uiAutomationBinderDiesBeforeConnecting_shouldNotCrash() throws Exception {
+ public void uiAutomationBinderDiesBeforeConnecting_notifiesSystem() throws Exception {
register(0);
ArgumentCaptor<IBinder.DeathRecipient> captor = ArgumentCaptor.forClass(
IBinder.DeathRecipient.class);
verify(mMockOwner).linkToDeath(captor.capture(), anyInt());
captor.getValue().binderDied();
mMessageCapturingHandler.sendAllMessages();
+ verify(mMockSystemSupport).onClientChange(false);
}
private void register(int flags) {
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 5520bd7..c91e22f 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 @@
@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 @@
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 362856c..f4c5442 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 @@
"dataManagementLabel");
verify(mTransportManager)
- .describeTransport(
+ .updateTransportAttributes(
eq(TRANSPORT_COMPONENT),
eq(TRANSPORT_NAME),
eq(configurationIntent),
@@ -247,7 +247,7 @@
null);
verify(mTransportManager)
- .describeTransport(
+ .updateTransportAttributes(
eq(TRANSPORT_COMPONENT),
eq(TRANSPORT_NAME),
eq(configurationIntent),
@@ -274,7 +274,7 @@
"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 0000000..c918e8c
--- /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 0000000..fb4e75e
--- /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 4cd5631..fa73722 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.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 @@
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 @@
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 fafae6c..b073ee5 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 86ce90c..b792d82 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 @@
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 @@
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 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 @@
@Test
public void testBuckets() throws Exception {
- AppStandbyController controller = setupController();
+ assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
- assertTimeout(controller, 0, UsageStatsManager.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 89447a9..6070516 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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+
/**
* Tests for the {@link TaskPositioningController} class.
*
@@ -78,9 +79,9 @@
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 @@
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 46efbd0..cc0259d 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.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 @@
@GuardedBy("mAppIdleLock")
private AppIdleHistory mAppIdleHistory;
- @GuardedBy("mAppIdleLock")
+ @GuardedBy("mPackageAccessListeners")
private ArrayList<AppIdleStateChangeListener>
mPackageAccessListeners = new ArrayList<>();
@@ -592,7 +593,7 @@
}
void addListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
+ synchronized (mPackageAccessListeners) {
if (!mPackageAccessListeners.contains(listener)) {
mPackageAccessListeners.add(listener);
}
@@ -600,7 +601,7 @@
}
void removeListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
+ synchronized (mPackageAccessListeners) {
mPackageAccessListeners.remove(listener);
}
}
@@ -789,6 +790,19 @@
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 @@
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 15284d5..07c860b 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 @@
} 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 @@
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 @@
} 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 @@
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/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ead8849..dedce17 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -300,10 +300,10 @@
* If true all networks are considered as home network a.k.a non-roaming. When false,
* the 2 pairs of CMDA and GSM roaming/non-roaming arrays are consulted.
*
- * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
- * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
- * @see KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
- * @see KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see #KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+ * @see #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see #KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+ * @see #KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
*/
public static final String
KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
@@ -2059,7 +2059,7 @@
* {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
* arbitrary thread.
* </p>
- * @see #hasCarrierPrivileges
+ * @see TelephonyManager#hasCarrierPrivileges
*/
public void notifyConfigChangedForSubId(int subId) {
try {
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 0000000..5d16dd5
--- /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 0000000..b7c8ca0
--- /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 0000000..2fb6744
--- /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 0000000..fd2eb24
--- /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 0000000..3d424a3
--- /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 0000000..52efd23
--- /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 0000000..7125781
--- /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 0000000..8332bc0
--- /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 0000000..f6005b6
--- /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 0000000..687b7ca
--- /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 0000000..a50575b
--- /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 0000000..8afb955
--- /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 0000000..01cca2db
--- /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 0000000..f4ec0eb
--- /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 0000000..4d18873
--- /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 0000000..9f82ad2
--- /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 0000000..f183a57
--- /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 0000000..8d1bd9d
--- /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 0000000..33aec5d
--- /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 0000000..e890cf8
--- /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 0000000..244c957
--- /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 0000000..558b009
--- /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/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index d267ad2..0dbc186 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -441,10 +441,11 @@
@Override
public String toString() {
- return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x" +
- Integer.toHexString(mSerialNumber) +
- ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier) +
- ", DCS=0x" + Integer.toHexString(mDataCodingScheme) +
- ", page " + mPageIndex + " of " + mNrOfPages + '}';
+ return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x"
+ + Integer.toHexString(mSerialNumber)
+ + ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier)
+ + ", format=" + mFormat
+ + ", DCS=0x" + Integer.toHexString(mDataCodingScheme)
+ + ", page " + mPageIndex + " of " + mNrOfPages + '}';
}
}
\ No newline at end of file
diff --git a/test-base/Android.bp b/test-base/Android.bp
index a3fd345..30c9af1 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -25,6 +25,7 @@
srcs: ["src/**/*.java"],
no_framework_libs: true,
+ hostdex: true,
libs: [
"framework",
],
diff --git a/tests/SurfaceComposition/Android.mk b/tests/SurfaceComposition/Android.mk
index 1962791..f59458d 100644
--- a/tests/SurfaceComposition/Android.mk
+++ b/tests/SurfaceComposition/Android.mk
@@ -29,7 +29,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
LOCAL_PACKAGE_NAME := SurfaceComposition
diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk
index 8aac8a1..7800a80 100644
--- a/tests/WindowAnimationJank/Android.mk
+++ b/tests/WindowAnimationJank/Android.mk
@@ -29,6 +29,8 @@
ub-janktesthelper \
junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index ccb0f3b..0f40b45 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -80,7 +80,7 @@
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 @@
.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 @@
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 @@
.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 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 @@
@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 @@
@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 20a4897..80e42a3 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -125,7 +125,7 @@
.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 @@
.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 @@
.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 @@
.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 354ad2a..8683c12 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -287,7 +287,7 @@
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 @@
// 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 @@
// 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 @@
// 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 94686c0..2da294c 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -20,6 +20,7 @@
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 1b80d95..174e746 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 0000000..a35f6ed
--- /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 0000000..bc9583b
--- /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 0000000..63da552
--- /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 0000000..3fff3cf
--- /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 2d517c7..0cfc0bd 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 @@
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 = 0;
- ResourceId res_id = table.identifierForName(
- entry16.data(), entry16.size(), type16.data(), type16.size(),
- package16.data(), package16.size(), &type_spec_flags);
+ 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;
+ }
+ }
+
if (!res_id.is_valid()) {
return {};
}
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index fd8a508..1f59d70 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 @@
.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 @@
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 @@
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
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 468864b..e301eb1 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -97,6 +97,7 @@
cflags: [
"-Wall",
"-Werror",
+ "-fexceptions",
],
export_generated_headers: ["statslog.h"],
shared_libs: [
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index f76196d..a25784d 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -111,8 +111,9 @@
return JAVA_TYPE_UNKNOWN;
case FieldDescriptor::TYPE_MESSAGE:
// TODO: not the final package name
- if (field->message_type()->full_name() == "android.os.statsd.WorkSource") {
- return JAVA_TYPE_WORK_SOURCE;
+ if (field->message_type()->full_name() ==
+ "android.os.statsd.AttributionChain") {
+ return JAVA_TYPE_ATTRIBUTION_CHAIN;
} else {
return JAVA_TYPE_OBJECT;
}
@@ -136,138 +137,153 @@
}
/**
+ * Gather the info about an atom proto.
+ */
+int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
+ vector<java_type_t> *signature) {
+
+ int errorCount = 0;
+ // Build a sorted list of the fields. Descriptor has them in source file
+ // order.
+ map<int, const FieldDescriptor *> fields;
+ for (int j = 0; j < atom->field_count(); j++) {
+ const FieldDescriptor *field = atom->field(j);
+ fields[field->number()] = field;
+ }
+
+ // Check that the parameters start at 1 and go up sequentially.
+ int expectedNumber = 1;
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const int number = it->first;
+ const FieldDescriptor *field = it->second;
+ if (number != expectedNumber) {
+ print_error(field,
+ "Fields must be numbered consecutively starting at 1:"
+ " '%s' is %d but should be %d\n",
+ field->name().c_str(), number, expectedNumber);
+ errorCount++;
+ expectedNumber = number;
+ continue;
+ }
+ expectedNumber++;
+ }
+
+ // Check that only allowed types are present. Remove any invalid ones.
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const FieldDescriptor *field = it->second;
+
+ java_type_t javaType = java_type(field);
+
+ if (javaType == JAVA_TYPE_UNKNOWN) {
+ print_error(field, "Unkown type for field: %s\n", field->name().c_str());
+ errorCount++;
+ continue;
+ } else if (javaType == JAVA_TYPE_OBJECT) {
+ // Allow attribution chain, but only at position 1.
+ print_error(field, "Message type not allowed for field: %s\n",
+ field->name().c_str());
+ errorCount++;
+ continue;
+ } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
+ print_error(field, "Raw bytes type not allowed for field: %s\n",
+ field->name().c_str());
+ errorCount++;
+ continue;
+ }
+ }
+
+ // Check that if there's an attribution chain, it's at position 1.
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ int number = it->first;
+ if (number != 1) {
+ const FieldDescriptor *field = it->second;
+ java_type_t javaType = java_type(field);
+ if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ print_error(
+ field,
+ "AttributionChain fields must have field id 1, in message: '%s'\n",
+ atom->name().c_str());
+ errorCount++;
+ }
+ }
+ }
+
+ // Build the type signature and the atom data.
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const FieldDescriptor *field = it->second;
+ java_type_t javaType = java_type(field);
+
+ AtomField atField(field->name(), javaType);
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature->push_back(JAVA_TYPE_INT);
+ const EnumDescriptor *enumDescriptor = field->enum_type();
+ for (int i = 0; i < enumDescriptor->value_count(); i++) {
+ atField.enumValues[enumDescriptor->value(i)->number()] =
+ enumDescriptor->value(i)->name().c_str();
+ }
+ } else {
+ signature->push_back(javaType);
+ }
+ atomDecl->fields.push_back(atField);
+ }
+
+ return errorCount;
+}
+
+/**
* Gather the info about the atoms.
*/
-int
-collate_atoms(const Descriptor* descriptor, Atoms* atoms)
-{
- int errorCount = 0;
- const bool dbg = false;
+int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
+ int errorCount = 0;
+ const bool dbg = false;
- for (int i=0; i<descriptor->field_count(); i++) {
- const FieldDescriptor* atomField = descriptor->field(i);
-
- if (dbg) {
- printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
- }
-
- // StatsEvent only has one oneof, which contains only messages. Don't allow other types.
- if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
- print_error(atomField,
- "Bad type for atom. StatsEvent can only have message type fields: %s\n",
- atomField->name().c_str());
- errorCount++;
- continue;
- }
-
- const Descriptor* atom = atomField->message_type();
-
- // Build a sorted list of the fields. Descriptor has them in source file order.
- map<int,const FieldDescriptor*> fields;
- for (int j=0; j<atom->field_count(); j++) {
- const FieldDescriptor* field = atom->field(j);
- fields[field->number()] = field;
- }
-
- // Check that the parameters start at 1 and go up sequentially.
- int expectedNumber = 1;
- for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- const int number = it->first;
- const FieldDescriptor* field = it->second;
- if (number != expectedNumber) {
- print_error(field, "Fields must be numbered consecutively starting at 1:"
- " '%s' is %d but should be %d\n",
- field->name().c_str(), number, expectedNumber);
- errorCount++;
- expectedNumber = number;
- continue;
- }
- expectedNumber++;
- }
-
- // Check that only allowed types are present. Remove any invalid ones.
- for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- const FieldDescriptor* field = it->second;
-
- java_type_t javaType = java_type(field);
-
- if (javaType == JAVA_TYPE_UNKNOWN) {
- print_error(field, "Unkown type for field: %s\n", field->name().c_str());
- errorCount++;
- continue;
- } else if (javaType == JAVA_TYPE_OBJECT) {
- // Allow WorkSources, but only at position 1.
- print_error(field, "Message type not allowed for field: %s\n",
- field->name().c_str());
- errorCount++;
- continue;
- } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
- print_error(field, "Raw bytes type not allowed for field: %s\n",
- field->name().c_str());
- errorCount++;
- continue;
- }
- }
-
- // Check that if there's a WorkSource, it's at position 1.
- for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- int number = it->first;
- if (number != 1) {
- const FieldDescriptor* field = it->second;
- java_type_t javaType = java_type(field);
- if (javaType == JAVA_TYPE_WORK_SOURCE) {
- print_error(field, "WorkSource fields must have field id 1, in message: '%s'\n",
- atom->name().c_str());
- errorCount++;
- }
- }
- }
-
- AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
-
- // Build the type signature and the atom data.
- vector<java_type_t> signature;
- for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- const FieldDescriptor* field = it->second;
- java_type_t javaType = java_type(field);
-
- AtomField atField(field->name(), javaType);
- if (javaType == JAVA_TYPE_ENUM) {
- // All enums are treated as ints when it comes to function signatures.
- signature.push_back(JAVA_TYPE_INT);
- const EnumDescriptor* enumDescriptor = field->enum_type();
- for (int i = 0; i < enumDescriptor->value_count(); i++) {
- atField.enumValues[enumDescriptor->value(i)->number()] =
- enumDescriptor->value(i)->name().c_str();
- }
- } else {
- signature.push_back(javaType);
- }
- atomDecl.fields.push_back(atField);
- }
-
- atoms->signatures.insert(signature);
- atoms->decls.insert(atomDecl);
- }
+ for (int i = 0; i < descriptor->field_count(); i++) {
+ const FieldDescriptor *atomField = descriptor->field(i);
if (dbg) {
- printf("signatures = [\n");
- for (set<vector<java_type_t>>::const_iterator it = atoms->signatures.begin();
- it != atoms->signatures.end(); it++) {
- printf(" ");
- for (vector<java_type_t>::const_iterator jt = it->begin(); jt != it->end(); jt++) {
- printf(" %d", (int)*jt);
- }
- printf("\n");
- }
- printf("]\n");
+ printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
}
- return errorCount;
+ // StatsEvent only has one oneof, which contains only messages. Don't allow
+ // other types.
+ if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
+ print_error(atomField,
+ "Bad type for atom. StatsEvent can only have message type "
+ "fields: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ }
+
+ const Descriptor *atom = atomField->message_type();
+ AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
+ vector<java_type_t> signature;
+ errorCount += collate_atom(atom, &atomDecl, &signature);
+ atoms->signatures.insert(signature);
+ atoms->decls.insert(atomDecl);
+ }
+
+ if (dbg) {
+ printf("signatures = [\n");
+ for (set<vector<java_type_t>>::const_iterator it =
+ atoms->signatures.begin();
+ it != atoms->signatures.end(); it++) {
+ printf(" ");
+ for (vector<java_type_t>::const_iterator jt = it->begin();
+ jt != it->end(); jt++) {
+ printf(" %d", (int)*jt);
+ }
+ printf("\n");
+ }
+ printf("]\n");
+ }
+
+ return errorCount;
}
} // namespace stats_log_api_gen
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 2f840d7..cd0625c 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -37,22 +37,21 @@
* The types for atom parameters.
*/
typedef enum {
- JAVA_TYPE_UNKNOWN = 0,
+ JAVA_TYPE_UNKNOWN = 0,
- JAVA_TYPE_WORK_SOURCE = 1,
- JAVA_TYPE_BOOLEAN = 2,
- JAVA_TYPE_INT = 3,
- JAVA_TYPE_LONG = 4,
- JAVA_TYPE_FLOAT = 5,
- JAVA_TYPE_DOUBLE = 6,
- JAVA_TYPE_STRING = 7,
- JAVA_TYPE_ENUM = 8,
+ JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
+ JAVA_TYPE_BOOLEAN = 2,
+ JAVA_TYPE_INT = 3,
+ JAVA_TYPE_LONG = 4,
+ JAVA_TYPE_FLOAT = 5,
+ JAVA_TYPE_DOUBLE = 6,
+ JAVA_TYPE_STRING = 7,
+ JAVA_TYPE_ENUM = 8,
- JAVA_TYPE_OBJECT = -1,
- JAVA_TYPE_BYTE_ARRAY = -2,
+ JAVA_TYPE_OBJECT = -1,
+ JAVA_TYPE_BYTE_ARRAY = -2,
} java_type_t;
-
/**
* The name and type for an atom field.
*/
@@ -100,9 +99,11 @@
* Gather the information about the atoms. Returns the number of errors.
*/
int collate_atoms(const Descriptor* descriptor, Atoms* atoms);
+int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
+ vector<java_type_t> *signature);
} // namespace stats_log_api_gen
} // namespace android
-#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
+#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 423d028..5e93c08 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -24,8 +24,6 @@
using android::os::statsd::Atom;
-// TODO: Support WorkSources
-
/**
* Turn lower and camel case into upper case with underscores.
*/
@@ -97,13 +95,13 @@
}
}
-static int
-write_stats_log_cpp(FILE* out, const Atoms& atoms)
-{
+static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
+ const AtomDecl &attributionDecl) {
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
+ fprintf(out, "#include <exception>\n");
fprintf(out, "#include <log/log_event_list.h>\n");
fprintf(out, "#include <log/log.h>\n");
fprintf(out, "#include <statslog.h>\n");
@@ -117,15 +115,29 @@
// Print write methods
fprintf(out, "\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
+ signature != atoms.signatures.end(); signature++) {
int argIndex;
fprintf(out, "void\n");
fprintf(out, "stats_write(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ }
argIndex++;
}
fprintf(out, ")\n");
@@ -133,15 +145,40 @@
fprintf(out, "{\n");
argIndex = 1;
fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
- fprintf(out, " event << code;\n");
+ fprintf(out, " event << code;\n\n");
for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- if (*arg == JAVA_TYPE_STRING) {
- fprintf(out, " if (arg%d == NULL) {\n", argIndex);
- fprintf(out, " arg%d = \"\";\n", argIndex);
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (const auto &chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, " if (%s_length != %s.size()) {\n",
+ attributionDecl.fields.front().name.c_str(), chainField.name.c_str());
+ fprintf(out, " throw std::invalid_argument(\"attribution fields with"
+ " diff length: %s vs %s\");\n",
+ attributionDecl.fields.front().name.c_str(),
+ chainField.name.c_str());
+ fprintf(out, " return;\n");
+ fprintf(out, " }\n");
+ }
+ }
+ fprintf(out, "\n event.begin();\n");
+ fprintf(out, " for (size_t i = 0; i < %s_length; ++i) {\n",
+ attributionDecl.fields.front().name.c_str());
+ fprintf(out, " event.begin();\n");
+ for (const auto &chainField : attributionDecl.fields) {
+ fprintf(out, " event << %s[i];\n", chainField.name.c_str());
+ }
+ fprintf(out, " event.end();\n");
fprintf(out, " }\n");
+ fprintf(out, " event.end();\n\n");
+ } else {
+ if (*arg == JAVA_TYPE_STRING) {
+ fprintf(out, " if (arg%d == NULL) {\n", argIndex);
+ fprintf(out, " arg%d = \"\";\n", argIndex);
+ fprintf(out, " }\n");
+ }
+ fprintf(out, " event << arg%d;\n", argIndex);
}
- fprintf(out, " event << arg%d;\n", argIndex);
argIndex++;
}
@@ -160,7 +197,7 @@
static int
-write_stats_log_header(FILE* out, const Atoms& atoms)
+write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
{
// Print prelude
fprintf(out, "// This file is autogenerated\n");
@@ -168,6 +205,7 @@
fprintf(out, "#pragma once\n");
fprintf(out, "\n");
fprintf(out, "#include <stdint.h>\n");
+ fprintf(out, "#include <vector>\n");
fprintf(out, "\n");
fprintf(out, "namespace android {\n");
@@ -185,7 +223,7 @@
size_t i = 0;
// Print constants
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
- atom != atoms.decls.end(); atom++) {
+ atom != atoms.decls.end(); atom++) {
string constant = make_constant_name(atom->name);
fprintf(out, "\n");
fprintf(out, " /**\n");
@@ -193,7 +231,21 @@
fprintf(out, " * Usage: stats_write(StatsLog.%s", constant.c_str());
for (vector<AtomField>::const_iterator field = atom->fields.begin();
field != atom->fields.end(); field++) {
- fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+ }
}
fprintf(out, ");\n");
fprintf(out, " */\n");
@@ -208,8 +260,7 @@
fprintf(out, "};\n");
fprintf(out, "\n");
- fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
- maxPushedAtomId);
+ fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", maxPushedAtomId);
// Print write methods
fprintf(out, "//\n");
@@ -220,8 +271,21 @@
fprintf(out, "void stats_write(int32_t code ");
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType), chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ }
argIndex++;
}
fprintf(out, ");\n");
@@ -235,7 +299,7 @@
}
static int
-write_stats_log_java(FILE* out, const Atoms& atoms)
+write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
{
// Print prelude
fprintf(out, "// This file is autogenerated\n");
@@ -247,7 +311,7 @@
fprintf(out, " * API For logging statistics events.\n");
fprintf(out, " * @hide\n");
fprintf(out, " */\n");
- fprintf(out, "public final class StatsLog {\n");
+ fprintf(out, "public class StatsLogInternal {\n");
fprintf(out, " // Constants for atom codes.\n");
// Print constants for the atom codes.
@@ -259,8 +323,15 @@
fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
fprintf(out, " * Usage: StatsLog.write(StatsLog.%s", constant.c_str());
for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+ field != atom->fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s",
+ java_type_name(chainField.javaType), chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+ }
}
fprintf(out, ");\n");
fprintf(out, " */\n");
@@ -271,33 +342,41 @@
// Print constants for the enum values.
fprintf(out, " // Constants for enum values.\n\n");
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
- atom != atoms.decls.end(); atom++) {
+ atom != atoms.decls.end(); atom++) {
for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ENUM) {
- fprintf(out, " // Values for %s.%s\n", atom->message.c_str(), field->name.c_str());
- for (map<int, string>::const_iterator value = field->enumValues.begin();
- value != field->enumValues.end(); value++) {
- fprintf(out, " public static final int %s__%s__%s = %d;\n",
- make_constant_name(atom->message).c_str(),
- make_constant_name(field->name).c_str(),
- make_constant_name(value->second).c_str(),
- value->first);
+ field != atom->fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ENUM) {
+ fprintf(out, " // Values for %s.%s\n", atom->message.c_str(),
+ field->name.c_str());
+ for (map<int, string>::const_iterator value = field->enumValues.begin();
+ value != field->enumValues.end(); value++) {
+ fprintf(out, " public static final int %s__%s__%s = %d;\n",
+ make_constant_name(atom->message).c_str(),
+ make_constant_name(field->name).c_str(),
+ make_constant_name(value->second).c_str(),
+ value->first);
+ }
+ fprintf(out, "\n");
}
- fprintf(out, "\n");
- }
}
}
// Print write methods
fprintf(out, " // Write methods\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
+ signature != atoms.signatures.end(); signature++) {
fprintf(out, " public static native void write(int code");
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s",
+ java_type_name(chainField.javaType), chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ }
argIndex++;
}
fprintf(out, ");\n");
@@ -330,12 +409,25 @@
}
}
+static const char*
+jni_array_type_name(java_type_t type)
+{
+ switch (type) {
+ case JAVA_TYPE_INT:
+ return "jintArray";
+ case JAVA_TYPE_STRING:
+ return "jobjectArray";
+ default:
+ return "UNKNOWN";
+ }
+}
+
static string
jni_function_name(const vector<java_type_t>& signature)
{
string result("StatsLog_write");
for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
+ arg != signature.end(); arg++) {
switch (*arg) {
case JAVA_TYPE_BOOLEAN:
result += "_boolean";
@@ -356,6 +448,9 @@
case JAVA_TYPE_STRING:
result += "_String";
break;
+ case JAVA_TYPE_ATTRIBUTION_CHAIN:
+ result += "_AttributionChain";
+ break;
default:
result += "_UNKNOWN";
break;
@@ -387,19 +482,26 @@
}
static string
-jni_function_signature(const vector<java_type_t>& signature)
+jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &attributionDecl)
{
string result("(I");
for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
- result += java_type_signature(*arg);
+ arg != signature.end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ result += "[";
+ result += java_type_signature(chainField.javaType);
+ }
+ } else {
+ result += java_type_signature(*arg);
+ }
}
result += ")V";
return result;
}
static int
-write_stats_log_jni(FILE* out, const Atoms& atoms)
+write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
{
// Print prelude
fprintf(out, "// This file is autogenerated\n");
@@ -408,6 +510,8 @@
fprintf(out, "#include <statslog.h>\n");
fprintf(out, "\n");
fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
+ fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
+ fprintf(out, "#include <utils/Vector.h>\n");
fprintf(out, "#include \"core_jni_helpers.h\"\n");
fprintf(out, "#include \"jni.h\"\n");
fprintf(out, "\n");
@@ -419,7 +523,7 @@
// Print write methods
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
+ signature != atoms.signatures.end(); signature++) {
int argIndex;
fprintf(out, "static void\n");
@@ -428,7 +532,14 @@
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
- fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s %s", jni_array_type_name(chainField.javaType),
+ chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+ }
argIndex++;
}
fprintf(out, ")\n");
@@ -437,10 +548,11 @@
// Prepare strings
argIndex = 1;
- bool hadString = false;
+ bool hadStringOrChain = false;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_STRING) {
+ hadStringOrChain = true;
fprintf(out, " const char* str%d;\n", argIndex);
fprintf(out, " if (arg%d != NULL) {\n", argIndex);
fprintf(out, " str%d = env->GetStringUTFChars(arg%d, NULL);\n",
@@ -448,13 +560,52 @@
fprintf(out, " } else {\n");
fprintf(out, " str%d = NULL;\n", argIndex);
fprintf(out, " }\n");
- hadString = true;
+ } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ hadStringOrChain = true;
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, " size_t %s_length = env->GetArrayLength(%s);\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ if (chainField.name != attributionDecl.fields.front().name) {
+ fprintf(out, " if (%s_length != %s_length) {\n",
+ chainField.name.c_str(),
+ attributionDecl.fields.front().name.c_str());
+ fprintf(out, " jniThrowException(env, "
+ "\"java/lang/IllegalArgumentException\", "
+ "\"invalid attribution field(%s) length.\");\n",
+ chainField.name.c_str());
+ fprintf(out, " return;\n");
+ fprintf(out, " }\n");
+ }
+ if (chainField.javaType == JAVA_TYPE_INT) {
+ fprintf(out, " jint* %s_array = env->GetIntArrayElements(%s, NULL);\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ } else if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, " std::vector<%s> %s_vec;\n",
+ cpp_type_name(chainField.javaType), chainField.name.c_str());
+ fprintf(out, " std::vector<ScopedUtfChars*> scoped_%s_vec;\n",
+ chainField.name.c_str());
+ fprintf(out, " for (size_t i = 0; i < %s_length; ++i) {\n",
+ chainField.name.c_str());
+ fprintf(out, " jstring jstr = "
+ "(jstring)env->GetObjectArrayElement(%s, i);\n",
+ chainField.name.c_str());
+ fprintf(out, " ScopedUtfChars* scoped_%s = "
+ "new ScopedUtfChars(env, jstr);\n",
+ chainField.name.c_str());
+ fprintf(out, " %s_vec.push_back(scoped_%s->c_str());\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ fprintf(out, " scoped_%s_vec.push_back(scoped_%s);\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ fprintf(out, " }\n");
+ }
+ fprintf(out, "\n");
+ }
}
argIndex++;
}
-
- // Emit this to quiet the unused parameter warning if there were no strings.
- if (!hadString) {
+ // Emit this to quiet the unused parameter warning if there were no strings or attribution
+ // chains.
+ if (!hadStringOrChain) {
fprintf(out, " (void)env;\n");
}
@@ -463,11 +614,24 @@
fprintf(out, " android::util::stats_write(code");
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
- const char* argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
- fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_INT) {
+ fprintf(out, ", (const %s*)%s_array, %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ } else if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", %s_vec", chainField.name.c_str());
+ }
+ }
+ } else {
+ const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
+ fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+ }
argIndex++;
}
fprintf(out, ");\n");
+ fprintf(out, "\n");
// Clean up strings
argIndex = 1;
@@ -478,6 +642,18 @@
fprintf(out, " env->ReleaseStringUTFChars(arg%d, str%d);\n",
argIndex, argIndex);
fprintf(out, " }\n");
+ } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_INT) {
+ fprintf(out, " env->ReleaseIntArrayElements(%s, %s_array, 0);\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ } else if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, " for (size_t i = 0; i < %s_length; ++i) {\n",
+ chainField.name.c_str());
+ fprintf(out, " delete scoped_%s_vec[i];\n", chainField.name.c_str());
+ fprintf(out, " }\n");
+ }
+ }
}
argIndex++;
}
@@ -494,8 +670,8 @@
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
signature != atoms.signatures.end(); signature++) {
fprintf(out, " { \"write\", \"%s\", (void*)%s },\n",
- jni_function_signature(*signature).c_str(),
- jni_function_name(*signature).c_str());
+ jni_function_signature(*signature, attributionDecl).c_str(),
+ jni_function_name(*signature).c_str());
}
fprintf(out, "};\n");
fprintf(out, "\n");
@@ -591,6 +767,11 @@
return 1;
}
+ AtomDecl attributionDecl;
+ vector<java_type_t> attributionSignature;
+ collate_atom(android::os::statsd::Attribution::descriptor(),
+ &attributionDecl, &attributionSignature);
+
// Write the .cpp file
if (cppFilename.size() != 0) {
FILE* out = fopen(cppFilename.c_str(), "w");
@@ -598,7 +779,8 @@
fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
return 1;
}
- errorCount = android::stats_log_api_gen::write_stats_log_cpp(out, atoms);
+ errorCount = android::stats_log_api_gen::write_stats_log_cpp(
+ out, atoms, attributionDecl);
fclose(out);
}
@@ -609,7 +791,8 @@
fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
return 1;
}
- errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms);
+ errorCount = android::stats_log_api_gen::write_stats_log_header(
+ out, atoms, attributionDecl);
fclose(out);
}
@@ -620,7 +803,8 @@
fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
return 1;
}
- errorCount = android::stats_log_api_gen::write_stats_log_java(out, atoms);
+ errorCount = android::stats_log_api_gen::write_stats_log_java(
+ out, atoms, attributionDecl);
fclose(out);
}
@@ -631,7 +815,8 @@
fprintf(stderr, "Unable to open file for write: %s\n", jniFilename.c_str());
return 1;
}
- errorCount = android::stats_log_api_gen::write_stats_log_jni(out, atoms);
+ errorCount = android::stats_log_api_gen::write_stats_log_jni(
+ out, atoms, attributionDecl);
fclose(out);
}
@@ -650,4 +835,4 @@
GOOGLE_PROTOBUF_VERIFY_VERSION;
return android::stats_log_api_gen::run(argc, argv);
-}
+}
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 6686158..84b22cda 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -39,22 +39,22 @@
}
message AllTypesAtom {
- optional android.os.statsd.WorkSource attribution = 1;
- optional double double_field = 2;
- optional float float_field = 3;
- optional int64 int64_field = 4;
- optional uint64 uint64_field = 5;
- optional int32 int32_field = 6;
- optional fixed64 fixed64_field = 7;
- optional fixed32 fixed32_field = 8;
- optional bool bool_field = 9;
- optional string string_field = 10;
- optional uint32 uint32_field = 11;
- optional AnEnum enum_field = 12;
- optional sfixed32 sfixed32_field = 13;
- optional sfixed64 sfixed64_field = 14;
- optional sint32 sint32_field = 15;
- optional sint64 sint64_field = 16;
+ optional android.os.statsd.AttributionChain attribution_chain = 1;
+ optional double double_field = 2;
+ optional float float_field = 3;
+ optional int64 int64_field = 4;
+ optional uint64 uint64_field = 5;
+ optional int32 int32_field = 6;
+ optional fixed64 fixed64_field = 7;
+ optional fixed32 fixed32_field = 8;
+ optional bool bool_field = 9;
+ optional string string_field = 10;
+ optional uint32 uint32_field = 11;
+ optional AnEnum enum_field = 12;
+ optional sfixed32 sfixed32_field = 13;
+ optional sfixed64 sfixed64_field = 14;
+ optional sint32 sint32_field = 15;
+ optional sint64 sint64_field = 16;
}
message Event {
@@ -99,14 +99,12 @@
}
}
-message BadWorkSourcePositionAtom {
- optional int32 field1 = 1;
- optional android.os.statsd.WorkSource attribution = 2;
+message BadAttributionChainPositionAtom {
+ optional int32 field1 = 1;
+ optional android.os.statsd.AttributionChain attribution = 2;
}
-message BadWorkSourcePosition {
- oneof event {
- BadWorkSourcePositionAtom bad = 1;
- }
+message BadAttributionChainPosition {
+ oneof event { BadAttributionChainPositionAtom bad = 1; }
}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 073f2cf..b2b7298 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -64,7 +64,7 @@
} \
} while(0)
-/** Expects that exactly one specific field has expected enum values. */
+/** Expects that exactly one specific field has expected enum values. */
#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values) \
do { \
for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
@@ -95,24 +95,25 @@
EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures, JAVA_TYPE_INT, JAVA_TYPE_INT);
// AllTypesAtom
- EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures,
- JAVA_TYPE_WORK_SOURCE, // WorkSource
- JAVA_TYPE_DOUBLE, // double
- JAVA_TYPE_FLOAT, // float
- JAVA_TYPE_LONG, // int64
- JAVA_TYPE_LONG, // uint64
- JAVA_TYPE_INT, // int32
- JAVA_TYPE_LONG, // fixed64
- JAVA_TYPE_INT, // fixed32
- JAVA_TYPE_BOOLEAN, // bool
- JAVA_TYPE_STRING, // string
- JAVA_TYPE_INT, // uint32
- JAVA_TYPE_INT, // AnEnum
- JAVA_TYPE_INT, // sfixed32
- JAVA_TYPE_LONG, // sfixed64
- JAVA_TYPE_INT, // sint32
- JAVA_TYPE_LONG // sint64
- );
+ EXPECT_SET_CONTAINS_SIGNATURE(
+ atoms.signatures,
+ JAVA_TYPE_ATTRIBUTION_CHAIN, // AttributionChain
+ JAVA_TYPE_DOUBLE, // double
+ JAVA_TYPE_FLOAT, // float
+ JAVA_TYPE_LONG, // int64
+ JAVA_TYPE_LONG, // uint64
+ JAVA_TYPE_INT, // int32
+ JAVA_TYPE_LONG, // fixed64
+ JAVA_TYPE_INT, // fixed32
+ JAVA_TYPE_BOOLEAN, // bool
+ JAVA_TYPE_STRING, // string
+ JAVA_TYPE_INT, // uint32
+ JAVA_TYPE_INT, // AnEnum
+ JAVA_TYPE_INT, // sfixed32
+ JAVA_TYPE_LONG, // sfixed64
+ JAVA_TYPE_INT, // sint32
+ JAVA_TYPE_LONG // sint64
+ );
set<AtomDecl>::const_iterator atom = atoms.decls.begin();
EXPECT_EQ(1, atom->code);
@@ -187,14 +188,16 @@
}
/**
- * Test that atoms that have a WorkSource not in the first position are rejected.
+ * Test that atoms that have an attribution chain not in the first position are
+ * rejected.
*/
-TEST(CollationTest, FailBadWorkSourcePosition) {
- Atoms atoms;
- int errorCount = collate_atoms(BadWorkSourcePosition::descriptor(), &atoms);
+TEST(CollationTest, FailBadAttributionChainPosition) {
+ Atoms atoms;
+ int errorCount =
+ collate_atoms(BadAttributionChainPosition::descriptor(), &atoms);
- EXPECT_EQ(1, errorCount);
+ EXPECT_EQ(1, errorCount);
}
} // namespace stats_log_api_gen
-} // namespace android
+} // namespace android
\ No newline at end of file