diff options
68 files changed, 4059 insertions, 663 deletions
diff --git a/Android.bp b/Android.bp index 6f9d59d4fd2e..a5bbb1825ae1 100644 --- a/Android.bp +++ b/Android.bp @@ -463,6 +463,17 @@ java_library { "telecomm/java/com/android/internal/telecom/IInCallService.aidl", "telecomm/java/com/android/internal/telecom/ITelecomService.aidl", "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl", "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl", "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl", diff --git a/api/current.txt b/api/current.txt index 8b8a92832139..8a09b82bf770 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7092,6 +7092,20 @@ package android.app.slice { field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp"; } + public class SliceManager { + method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri); + method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>); + method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>); + method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler); + method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor); + method public void unpinSlice(android.net.Uri); + method public void unregisterSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback); + } + + public static abstract interface SliceManager.SliceCallback { + method public abstract void onSliceUpdated(android.app.slice.Slice); + } + public abstract class SliceProvider extends android.content.ContentProvider { ctor public SliceProvider(); method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); @@ -7100,6 +7114,8 @@ package android.app.slice { method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>); method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri); method public android.net.Uri onMapIntentToUri(android.content.Intent); + method public void onSlicePinned(android.net.Uri); + method public void onSliceUnpinned(android.net.Uri); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal); @@ -8121,6 +8137,94 @@ package android.bluetooth { method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int); } + public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile { + method public boolean connect(android.bluetooth.BluetoothDevice); + method public boolean disconnect(android.bluetooth.BluetoothDevice); + method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method public int getConnectionState(android.bluetooth.BluetoothDevice); + method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]); + method public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceCallback); + method public boolean replyReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]); + method public boolean reportError(android.bluetooth.BluetoothDevice, byte); + method public boolean sendReport(android.bluetooth.BluetoothDevice, int, byte[]); + method public boolean unregisterApp(); + field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; + field public static final byte ERROR_RSP_INVALID_PARAM = 4; // 0x4 + field public static final byte ERROR_RSP_INVALID_RPT_ID = 2; // 0x2 + field public static final byte ERROR_RSP_NOT_READY = 1; // 0x1 + field public static final byte ERROR_RSP_SUCCESS = 0; // 0x0 + field public static final byte ERROR_RSP_UNKNOWN = 14; // 0xe + field public static final byte ERROR_RSP_UNSUPPORTED_REQ = 3; // 0x3 + field public static final byte PROTOCOL_BOOT_MODE = 0; // 0x0 + field public static final byte PROTOCOL_REPORT_MODE = 1; // 0x1 + field public static final byte REPORT_TYPE_FEATURE = 3; // 0x3 + field public static final byte REPORT_TYPE_INPUT = 1; // 0x1 + field public static final byte REPORT_TYPE_OUTPUT = 2; // 0x2 + field public static final byte SUBCLASS1_COMBO = -64; // 0xffffffc0 + field public static final byte SUBCLASS1_KEYBOARD = 64; // 0x40 + field public static final byte SUBCLASS1_MOUSE = -128; // 0xffffff80 + field public static final byte SUBCLASS1_NONE = 0; // 0x0 + field public static final byte SUBCLASS2_CARD_READER = 6; // 0x6 + field public static final byte SUBCLASS2_DIGITIZER_TABLET = 5; // 0x5 + field public static final byte SUBCLASS2_GAMEPAD = 2; // 0x2 + field public static final byte SUBCLASS2_JOYSTICK = 1; // 0x1 + field public static final byte SUBCLASS2_REMOTE_CONTROL = 3; // 0x3 + field public static final byte SUBCLASS2_SENSING_DEVICE = 4; // 0x4 + field public static final byte SUBCLASS2_UNCATEGORIZED = 0; // 0x0 + } + + public final class BluetoothHidDeviceAppQosSettings implements android.os.Parcelable { + ctor public BluetoothHidDeviceAppQosSettings(int, int, int, int, int, int); + method public int describeContents(); + method public int[] toArray(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppQosSettings> CREATOR; + field public static final int MAX = -1; // 0xffffffff + field public static final int SERVICE_BEST_EFFORT = 1; // 0x1 + field public static final int SERVICE_GUARANTEED = 2; // 0x2 + field public static final int SERVICE_NO_TRAFFIC = 0; // 0x0 + field public final int delayVariation; + field public final int latency; + field public final int peakBandwidth; + field public final int serviceType; + field public final int tokenBucketSize; + field public final int tokenRate; + } + + public static class BluetoothHidDeviceAppQosSettings.Builder { + ctor public BluetoothHidDeviceAppQosSettings.Builder(); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings build(); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder delayVariation(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder latency(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder peakBandwidth(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder serviceType(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenBucketSize(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenRate(int); + } + + public final class BluetoothHidDeviceAppSdpSettings implements android.os.Parcelable { + ctor public BluetoothHidDeviceAppSdpSettings(java.lang.String, java.lang.String, java.lang.String, byte, byte[]); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR; + field public final java.lang.String description; + field public final byte[] descriptors; + field public final java.lang.String name; + field public final java.lang.String provider; + field public final byte subclass; + } + + public abstract class BluetoothHidDeviceCallback { + ctor public BluetoothHidDeviceCallback(); + method public void onAppStatusChanged(android.bluetooth.BluetoothDevice, boolean); + method public void onConnectionStateChanged(android.bluetooth.BluetoothDevice, int); + method public void onGetReport(android.bluetooth.BluetoothDevice, byte, byte, int); + method public void onIntrData(android.bluetooth.BluetoothDevice, byte, byte[]); + method public void onSetProtocol(android.bluetooth.BluetoothDevice, byte); + method public void onSetReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]); + method public void onVirtualCableUnplug(android.bluetooth.BluetoothDevice); + } + public final class BluetoothManager { method public android.bluetooth.BluetoothAdapter getAdapter(); method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int); @@ -8140,6 +8244,7 @@ package android.bluetooth { field public static final int GATT_SERVER = 8; // 0x8 field public static final int HEADSET = 1; // 0x1 field public static final int HEALTH = 3; // 0x3 + field public static final int HID_DEVICE = 19; // 0x13 field public static final int SAP = 10; // 0xa field public static final int STATE_CONNECTED = 2; // 0x2 field public static final int STATE_CONNECTING = 1; // 0x1 diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 9400a1c435eb..1eabf11526df 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -294,6 +294,10 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( } 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) { @@ -327,6 +331,12 @@ void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { // 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); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 62e5d52b2b11..51af83d3a482 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" @@ -117,6 +118,7 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents); FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); + FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); }; } // namespace statsd diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 146a19d8cbfb..6f117d3d76d3 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -238,6 +238,79 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { 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/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ef446c2d72c3..da57efa94599 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -377,7 +377,7 @@ public final class ActivityThread extends ClientTransactionHandler { ActivityInfo activityInfo; CompatibilityInfo compatInfo; - public LoadedApk packageInfo; + public LoadedApk loadedApk; List<ResultInfo> pendingResults; List<ReferrerIntent> pendingIntents; @@ -420,7 +420,7 @@ public final class ActivityThread extends ClientTransactionHandler { this.isForward = isForward; this.profilerInfo = profilerInfo; this.overrideConfig = overrideConfig; - this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo, + this.loadedApk = client.getLoadedApkNoCheck(activityInfo.applicationInfo, compatInfo); init(); } @@ -602,7 +602,7 @@ public final class ActivityThread extends ClientTransactionHandler { } static final class AppBindData { - LoadedApk info; + LoadedApk loadedApk; String processName; ApplicationInfo appInfo; List<ProviderInfo> providers; @@ -1774,13 +1774,13 @@ public final class ActivityThread extends ClientTransactionHandler { return mH; } - public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - int flags) { - return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); + public final LoadedApk getLoadedApkForPackageName(String packageName, + CompatibilityInfo compatInfo, int flags) { + return getLoadedApkForPackageName(packageName, compatInfo, flags, UserHandle.myUserId()); } - public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - int flags, int userId) { + public final LoadedApk getLoadedApkForPackageName(String packageName, + CompatibilityInfo compatInfo, int flags, int userId) { final boolean differentUser = (UserHandle.myUserId() != userId); synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; @@ -1793,13 +1793,13 @@ public final class ActivityThread extends ClientTransactionHandler { ref = mResourcePackages.get(packageName); } - LoadedApk packageInfo = ref != null ? ref.get() : null; - //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo); - //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir - // + ": " + packageInfo.mResources.getAssets().isUpToDate()); - if (packageInfo != null && (packageInfo.mResources == null - || packageInfo.mResources.getAssets().isUpToDate())) { - if (packageInfo.isSecurityViolation() + LoadedApk loadedApk = ref != null ? ref.get() : null; + //Slog.i(TAG, "getLoadedApkForPackageName " + packageName + ": " + loadedApk); + //if (loadedApk != null) Slog.i(TAG, "isUptoDate " + loadedApk.mResDir + // + ": " + loadedApk.mResources.getAssets().isUpToDate()); + if (loadedApk != null && (loadedApk.mResources == null + || loadedApk.mResources.getAssets().isUpToDate())) { + if (loadedApk.isSecurityViolation() && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) { throw new SecurityException( "Requesting code from " + packageName @@ -1807,7 +1807,7 @@ public final class ActivityThread extends ClientTransactionHandler { + mBoundApplication.processName + "/" + mBoundApplication.appInfo.uid); } - return packageInfo; + return loadedApk; } } @@ -1822,13 +1822,13 @@ public final class ActivityThread extends ClientTransactionHandler { } if (ai != null) { - return getPackageInfo(ai, compatInfo, flags); + return getLoadedApk(ai, compatInfo, flags); } return null; } - public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, + public final LoadedApk getLoadedApk(ApplicationInfo ai, CompatibilityInfo compatInfo, int flags) { boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; boolean securityViolation = includeCode && ai.uid != 0 @@ -1850,17 +1850,17 @@ public final class ActivityThread extends ClientTransactionHandler { throw new SecurityException(msg); } } - return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode, + return getLoadedApk(ai, compatInfo, null, securityViolation, includeCode, registerPackage); } @Override - public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, + public final LoadedApk getLoadedApkNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { - return getPackageInfo(ai, compatInfo, null, false, true, false); + return getLoadedApk(ai, compatInfo, null, false, true, false); } - public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) { + public final LoadedApk peekLoadedApk(String packageName, boolean includeCode) { synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; if (includeCode) { @@ -1872,7 +1872,7 @@ public final class ActivityThread extends ClientTransactionHandler { } } - private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, + private LoadedApk getLoadedApk(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode, boolean registerPackage) { final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); @@ -1887,35 +1887,35 @@ public final class ActivityThread extends ClientTransactionHandler { ref = mResourcePackages.get(aInfo.packageName); } - LoadedApk packageInfo = ref != null ? ref.get() : null; - if (packageInfo == null || (packageInfo.mResources != null - && !packageInfo.mResources.getAssets().isUpToDate())) { + LoadedApk loadedApk = ref != null ? ref.get() : null; + if (loadedApk == null || (loadedApk.mResources != null + && !loadedApk.mResources.getAssets().isUpToDate())) { if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package " : "Loading resource-only package ") + aInfo.packageName + " (in " + (mBoundApplication != null ? mBoundApplication.processName : null) + ")"); - packageInfo = + loadedApk = new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); if (mSystemThread && "android".equals(aInfo.packageName)) { - packageInfo.installSystemApplicationInfo(aInfo, - getSystemContext().mPackageInfo.getClassLoader()); + loadedApk.installSystemApplicationInfo(aInfo, + getSystemContext().mLoadedApk.getClassLoader()); } if (differentUser) { // Caching not supported across users } else if (includeCode) { mPackages.put(aInfo.packageName, - new WeakReference<LoadedApk>(packageInfo)); + new WeakReference<LoadedApk>(loadedApk)); } else { mResourcePackages.put(aInfo.packageName, - new WeakReference<LoadedApk>(packageInfo)); + new WeakReference<LoadedApk>(loadedApk)); } } - return packageInfo; + return loadedApk; } } @@ -2627,8 +2627,8 @@ public final class ActivityThread extends ClientTransactionHandler { /** Core implementation of activity launch. */ private Activity performLaunchActivity(ActivityClientRecord r) { ActivityInfo aInfo = r.activityInfo; - if (r.packageInfo == null) { - r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, + if (r.loadedApk == null) { + r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } @@ -2665,15 +2665,15 @@ public final class ActivityThread extends ClientTransactionHandler { } try { - Application app = r.packageInfo.makeApplication(false, mInstrumentation); + Application app = r.loadedApk.makeApplication(false, mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() - + ", pkg=" + r.packageInfo.getPackageName() + + ", pkg=" + r.loadedApk.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() - + ", dir=" + r.packageInfo.getAppDir()); + + ", dir=" + r.loadedApk.getAppDir()); if (activity != null) { CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); @@ -2813,7 +2813,7 @@ public final class ActivityThread extends ClientTransactionHandler { } ContextImpl appContext = ContextImpl.createActivityContext( - this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); + this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig); final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); // For debugging purposes, if the activity's package name contains the value of @@ -2821,7 +2821,7 @@ public final class ActivityThread extends ClientTransactionHandler { // its content on a secondary display if there is one. String pkgName = SystemProperties.get("debug.second-display.pkg"); if (pkgName != null && !pkgName.isEmpty() - && r.packageInfo.mPackageName.contains(pkgName)) { + && r.loadedApk.mPackageName.contains(pkgName)) { for (int id : dm.getDisplayIds()) { if (id != Display.DEFAULT_DISPLAY) { Display display = @@ -3141,7 +3141,7 @@ public final class ActivityThread extends ClientTransactionHandler { String component = data.intent.getComponent().getClassName(); - LoadedApk packageInfo = getPackageInfoNoCheck( + LoadedApk loadedApk = getLoadedApkNoCheck( data.info.applicationInfo, data.compatInfo); IActivityManager mgr = ActivityManager.getService(); @@ -3150,7 +3150,7 @@ public final class ActivityThread extends ClientTransactionHandler { BroadcastReceiver receiver; ContextImpl context; try { - app = packageInfo.makeApplication(false, mInstrumentation); + app = loadedApk.makeApplication(false, mInstrumentation); context = (ContextImpl) app.getBaseContext(); if (data.info.splitName != null) { context = (ContextImpl) context.createContextForSplit(data.info.splitName); @@ -3159,7 +3159,7 @@ public final class ActivityThread extends ClientTransactionHandler { data.intent.setExtrasClassLoader(cl); data.intent.prepareToEnterProcess(); data.setExtrasClassLoader(cl); - receiver = packageInfo.getAppFactory() + receiver = loadedApk.getAppFactory() .instantiateReceiver(cl, data.info.name, data.intent); } catch (Exception e) { if (DEBUG_BROADCAST) Slog.i(TAG, @@ -3175,9 +3175,9 @@ public final class ActivityThread extends ClientTransactionHandler { TAG, "Performing receive of " + data.intent + ": app=" + app + ", appName=" + app.getPackageName() - + ", pkg=" + packageInfo.getPackageName() + + ", pkg=" + loadedApk.getPackageName() + ", comp=" + data.intent.getComponent().toShortString() - + ", dir=" + packageInfo.getAppDir()); + + ", dir=" + loadedApk.getAppDir()); sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); @@ -3222,8 +3222,8 @@ public final class ActivityThread extends ClientTransactionHandler { unscheduleGcIdler(); // instantiate the BackupAgent class named in the manifest - LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo); - String packageName = packageInfo.mPackageName; + LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo); + String packageName = loadedApk.mPackageName; if (packageName == null) { Slog.d(TAG, "Asked to create backup agent for nonexistent package"); return; @@ -3249,11 +3249,11 @@ public final class ActivityThread extends ClientTransactionHandler { try { if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname); - java.lang.ClassLoader cl = packageInfo.getClassLoader(); + java.lang.ClassLoader cl = loadedApk.getClassLoader(); agent = (BackupAgent) cl.loadClass(classname).newInstance(); // set up the agent's context - ContextImpl context = ContextImpl.createAppContext(this, packageInfo); + ContextImpl context = ContextImpl.createAppContext(this, loadedApk); context.setOuterContext(agent); agent.attach(context); @@ -3289,8 +3289,8 @@ public final class ActivityThread extends ClientTransactionHandler { private void handleDestroyBackupAgent(CreateBackupAgentData data) { if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data); - LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo); - String packageName = packageInfo.mPackageName; + LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo); + String packageName = loadedApk.mPackageName; BackupAgent agent = mBackupAgents.get(packageName); if (agent != null) { try { @@ -3310,12 +3310,12 @@ public final class ActivityThread extends ClientTransactionHandler { // we are back active so skip it. unscheduleGcIdler(); - LoadedApk packageInfo = getPackageInfoNoCheck( + LoadedApk loadedApk = getLoadedApkNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { - java.lang.ClassLoader cl = packageInfo.getClassLoader(); - service = packageInfo.getAppFactory() + java.lang.ClassLoader cl = loadedApk.getClassLoader(); + service = loadedApk.getAppFactory() .instantiateService(cl, data.info.name, data.intent); } catch (Exception e) { if (!mInstrumentation.onException(service, e)) { @@ -3328,10 +3328,10 @@ public final class ActivityThread extends ClientTransactionHandler { try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); - ContextImpl context = ContextImpl.createAppContext(this, packageInfo); + ContextImpl context = ContextImpl.createAppContext(this, loadedApk); context.setOuterContext(service); - Application app = packageInfo.makeApplication(false, mInstrumentation); + Application app = loadedApk.makeApplication(false, mInstrumentation); service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService()); service.onCreate(); @@ -4195,11 +4195,11 @@ public final class ActivityThread extends ClientTransactionHandler { } private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) { - LoadedApk apk = peekPackageInfo(data.pkg, false); + LoadedApk apk = peekLoadedApk(data.pkg, false); if (apk != null) { apk.setCompatibilityInfo(data.info); } - apk = peekPackageInfo(data.pkg, true); + apk = peekLoadedApk(data.pkg, true); if (apk != null) { apk.setCompatibilityInfo(data.info); } @@ -4693,7 +4693,7 @@ public final class ActivityThread extends ClientTransactionHandler { if (a != null) { Configuration thisConfig = applyConfigCompatMainThread( mCurDefaultDisplayDpi, newConfig, - ar.packageInfo.getCompatibilityInfo()); + ar.loadedApk.getCompatibilityInfo()); if (!ar.activity.mFinished && (allActivities || !ar.paused)) { // If the activity is currently resumed, its configuration // needs to change right now. @@ -5179,7 +5179,7 @@ public final class ActivityThread extends ClientTransactionHandler { } final void handleDispatchPackageBroadcast(int cmd, String[] packages) { - boolean hasPkgInfo = false; + boolean hasLoadedApk = false; switch (cmd) { case ApplicationThreadConstants.PACKAGE_REMOVED: case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL: @@ -5190,14 +5190,14 @@ public final class ActivityThread extends ClientTransactionHandler { } synchronized (mResourcesManager) { for (int i = packages.length - 1; i >= 0; i--) { - if (!hasPkgInfo) { + if (!hasLoadedApk) { WeakReference<LoadedApk> ref = mPackages.get(packages[i]); if (ref != null && ref.get() != null) { - hasPkgInfo = true; + hasLoadedApk = true; } else { ref = mResourcePackages.get(packages[i]); if (ref != null && ref.get() != null) { - hasPkgInfo = true; + hasLoadedApk = true; } } } @@ -5217,21 +5217,21 @@ public final class ActivityThread extends ClientTransactionHandler { synchronized (mResourcesManager) { for (int i = packages.length - 1; i >= 0; i--) { WeakReference<LoadedApk> ref = mPackages.get(packages[i]); - LoadedApk pkgInfo = ref != null ? ref.get() : null; - if (pkgInfo != null) { - hasPkgInfo = true; + LoadedApk loadedApk = ref != null ? ref.get() : null; + if (loadedApk != null) { + hasLoadedApk = true; } else { ref = mResourcePackages.get(packages[i]); - pkgInfo = ref != null ? ref.get() : null; - if (pkgInfo != null) { - hasPkgInfo = true; + loadedApk = ref != null ? ref.get() : null; + if (loadedApk != null) { + hasLoadedApk = true; } } // If the package is being replaced, yet it still has a valid // LoadedApk object, the package was updated with _DONT_KILL. // Adjust it's internal references to the application info and // resources. - if (pkgInfo != null) { + if (loadedApk != null) { try { final String packageName = packages[i]; final ApplicationInfo aInfo = @@ -5245,13 +5245,13 @@ public final class ActivityThread extends ClientTransactionHandler { if (ar.activityInfo.applicationInfo.packageName .equals(packageName)) { ar.activityInfo.applicationInfo = aInfo; - ar.packageInfo = pkgInfo; + ar.loadedApk = loadedApk; } } } final List<String> oldPaths = sPackageManager.getPreviousCodePaths(packageName); - pkgInfo.updateApplicationInfo(aInfo, oldPaths); + loadedApk.updateApplicationInfo(aInfo, oldPaths); } catch (RemoteException e) { } } @@ -5260,7 +5260,7 @@ public final class ActivityThread extends ClientTransactionHandler { break; } } - ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo); + ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk); } final void handleLowMemory() { @@ -5464,7 +5464,7 @@ public final class ActivityThread extends ClientTransactionHandler { applyCompatConfiguration(mCurDefaultDisplayDpi); } - data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); + data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo); /** * Switch this process to density compatibility mode if needed. @@ -5508,7 +5508,7 @@ public final class ActivityThread extends ClientTransactionHandler { // XXX should have option to change the port. Debug.changeDebugPort(8100); if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) { - Slog.w(TAG, "Application " + data.info.getPackageName() + Slog.w(TAG, "Application " + data.loadedApk.getPackageName() + " is waiting for the debugger on port 8100..."); IActivityManager mgr = ActivityManager.getService(); @@ -5527,7 +5527,7 @@ public final class ActivityThread extends ClientTransactionHandler { } } else { - Slog.w(TAG, "Application " + data.info.getPackageName() + Slog.w(TAG, "Application " + data.loadedApk.getPackageName() + " can be debugged on port 8100..."); } } @@ -5575,14 +5575,14 @@ public final class ActivityThread extends ClientTransactionHandler { mInstrumentationAppDir = ii.sourceDir; mInstrumentationSplitAppDirs = ii.splitSourceDirs; mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii); - mInstrumentedAppDir = data.info.getAppDir(); - mInstrumentedSplitAppDirs = data.info.getSplitAppDirs(); - mInstrumentedLibDir = data.info.getLibDir(); + mInstrumentedAppDir = data.loadedApk.getAppDir(); + mInstrumentedSplitAppDirs = data.loadedApk.getSplitAppDirs(); + mInstrumentedLibDir = data.loadedApk.getLibDir(); } else { ii = null; } - final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); + final ContextImpl appContext = ContextImpl.createAppContext(this, data.loadedApk); updateLocaleListFromAppContext(appContext, mResourcesManager.getConfiguration().getLocales()); @@ -5626,9 +5626,9 @@ public final class ActivityThread extends ClientTransactionHandler { } ii.copyTo(instrApp); instrApp.initForUser(UserHandle.myUserId()); - final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, + final LoadedApk loadedApk = getLoadedApk(instrApp, data.compatInfo, appContext.getClassLoader(), false, true, false); - final ContextImpl instrContext = ContextImpl.createAppContext(this, pi); + final ContextImpl instrContext = ContextImpl.createAppContext(this, loadedApk); try { final ClassLoader cl = instrContext.getClassLoader(); @@ -5673,7 +5673,7 @@ public final class ActivityThread extends ClientTransactionHandler { try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. - app = data.info.makeApplication(data.restrictedBackupMode, null); + app = data.loadedApk.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; // don't bring up providers in restricted mode; they may depend on the @@ -5727,7 +5727,7 @@ public final class ActivityThread extends ClientTransactionHandler { final int preloadedFontsResource = info.metaData.getInt( ApplicationInfo.METADATA_PRELOADED_FONTS, 0); if (preloadedFontsResource != 0) { - data.info.getResources().preloadFonts(preloadedFontsResource); + data.loadedApk.getResources().preloadFonts(preloadedFontsResource); } } } catch (RemoteException e) { @@ -6185,12 +6185,12 @@ public final class ActivityThread extends ClientTransactionHandler { try { final java.lang.ClassLoader cl = c.getClassLoader(); - LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); - if (packageInfo == null) { + LoadedApk loadedApk = peekLoadedApk(ai.packageName, true); + if (loadedApk == null) { // System startup case. - packageInfo = getSystemContext().mPackageInfo; + loadedApk = getSystemContext().mLoadedApk; } - localProvider = packageInfo.getAppFactory() + localProvider = loadedApk.getAppFactory() .instantiateProvider(cl, info.name); provider = localProvider.getIContentProvider(); if (provider == null) { @@ -6339,8 +6339,8 @@ public final class ActivityThread extends ClientTransactionHandler { mInstrumentation = new Instrumentation(); mInstrumentation.basicInit(this); ContextImpl context = ContextImpl.createAppContext( - this, getSystemContext().mPackageInfo); - mInitialApplication = context.mPackageInfo.makeApplication(true, null); + this, getSystemContext().mLoadedApk); + mInitialApplication = context.mLoadedApk.makeApplication(true, null); mInitialApplication.onCreate(); } catch (Exception e) { throw new RuntimeException( diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 156df36a600c..5822f5c8f6c1 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -187,7 +187,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { */ /* package */ final void attach(Context context) { attachBaseContext(context); - mLoadedApk = ContextImpl.getImpl(context).mPackageInfo; + mLoadedApk = ContextImpl.getImpl(context).mLoadedApk; } /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 1dbdb59ebcce..8641a21a0b3c 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1381,7 +1381,7 @@ public class ApplicationPackageManager extends PackageManager { sameUid ? app.sourceDir : app.publicSourceDir, sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY, - mContext.mPackageInfo); + mContext.mLoadedApk); if (r != null) { return r; } diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index ef66af0c60f4..45c0e0cdfb25 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -110,7 +110,7 @@ public abstract class ClientTransactionHandler { PendingTransactionActions pendingActions); /** Get package info. */ - public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, + public abstract LoadedApk getLoadedApkNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo); /** Deliver app configuration change notification. */ diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index a2de0f44962a..165343058a2a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -159,7 +159,7 @@ class ContextImpl extends Context { private ArrayMap<String, File> mSharedPrefsPaths; final @NonNull ActivityThread mMainThread; - final @NonNull LoadedApk mPackageInfo; + final @NonNull LoadedApk mLoadedApk; private @Nullable ClassLoader mClassLoader; private final @Nullable IBinder mActivityToken; @@ -257,8 +257,8 @@ class ContextImpl extends Context { @Override public Context getApplicationContext() { - return (mPackageInfo != null) ? - mPackageInfo.getApplication() : mMainThread.getApplication(); + return (mLoadedApk != null) ? + mLoadedApk.getApplication() : mMainThread.getApplication(); } @Override @@ -302,15 +302,15 @@ class ContextImpl extends Context { @Override public ClassLoader getClassLoader() { - return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader()); + return mClassLoader != null ? mClassLoader : (mLoadedApk != null ? mLoadedApk.getClassLoader() : ClassLoader.getSystemClassLoader()); } @Override public String getPackageName() { - if (mPackageInfo != null) { - return mPackageInfo.getPackageName(); + if (mLoadedApk != null) { + return mLoadedApk.getPackageName(); } - // No mPackageInfo means this is a Context for the system itself, + // No mLoadedApk means this is a Context for the system itself, // and this here is its name. return "android"; } @@ -329,24 +329,24 @@ class ContextImpl extends Context { @Override public ApplicationInfo getApplicationInfo() { - if (mPackageInfo != null) { - return mPackageInfo.getApplicationInfo(); + if (mLoadedApk != null) { + return mLoadedApk.getApplicationInfo(); } throw new RuntimeException("Not supported in system context"); } @Override public String getPackageResourcePath() { - if (mPackageInfo != null) { - return mPackageInfo.getResDir(); + if (mLoadedApk != null) { + return mLoadedApk.getResDir(); } throw new RuntimeException("Not supported in system context"); } @Override public String getPackageCodePath() { - if (mPackageInfo != null) { - return mPackageInfo.getAppDir(); + if (mLoadedApk != null) { + return mLoadedApk.getAppDir(); } throw new RuntimeException("Not supported in system context"); } @@ -356,7 +356,7 @@ class ContextImpl extends Context { // At least one application in the world actually passes in a null // name. This happened to work because when we generated the file name // we would stringify it to "null.xml". Nice. - if (mPackageInfo.getApplicationInfo().targetSdkVersion < + if (mLoadedApk.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) { if (name == null) { name = "null"; @@ -1098,11 +1098,11 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); IIntentReceiver rd = null; if (resultReceiver != null) { - if (mPackageInfo != null) { + if (mLoadedApk != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( resultReceiver, getOuterContext(), scheduler, mMainThread.getInstrumentation(), false); } else { @@ -1202,11 +1202,11 @@ class ContextImpl extends Context { Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { IIntentReceiver rd = null; if (resultReceiver != null) { - if (mPackageInfo != null) { + if (mLoadedApk != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( resultReceiver, getOuterContext(), scheduler, mMainThread.getInstrumentation(), false); } else { @@ -1256,11 +1256,11 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); IIntentReceiver rd = null; if (resultReceiver != null) { - if (mPackageInfo != null) { + if (mLoadedApk != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( resultReceiver, getOuterContext(), scheduler, mMainThread.getInstrumentation(), false); } else { @@ -1338,11 +1338,11 @@ class ContextImpl extends Context { Bundle initialExtras) { IIntentReceiver rd = null; if (resultReceiver != null) { - if (mPackageInfo != null) { + if (mLoadedApk != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( resultReceiver, getOuterContext(), scheduler, mMainThread.getInstrumentation(), false); } else { @@ -1419,11 +1419,11 @@ class ContextImpl extends Context { Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; if (receiver != null) { - if (mPackageInfo != null && context != null) { + if (mLoadedApk != null && context != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = mPackageInfo.getReceiverDispatcher( + rd = mLoadedApk.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } else { @@ -1450,8 +1450,8 @@ class ContextImpl extends Context { @Override public void unregisterReceiver(BroadcastReceiver receiver) { - if (mPackageInfo != null) { - IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher( + if (mLoadedApk != null) { + IIntentReceiver rd = mLoadedApk.forgetReceiverDispatcher( getOuterContext(), receiver); try { ActivityManager.getService().unregisterReceiver(rd); @@ -1584,7 +1584,7 @@ class ContextImpl extends Context { @Override public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler, int flags) { - return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); + return mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags); } /** @hide */ @@ -1606,16 +1606,16 @@ class ContextImpl extends Context { if (conn == null) { throw new IllegalArgumentException("connection is null"); } - if (mPackageInfo != null) { - sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); + if (mLoadedApk != null) { + sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags); } else { throw new RuntimeException("Not supported in system context"); } validateServiceIntent(service); try { IBinder token = getActivityToken(); - if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null - && mPackageInfo.getApplicationInfo().targetSdkVersion + if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null + && mLoadedApk.getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } @@ -1639,8 +1639,8 @@ class ContextImpl extends Context { if (conn == null) { throw new IllegalArgumentException("connection is null"); } - if (mPackageInfo != null) { - IServiceConnection sd = mPackageInfo.forgetServiceDispatcher( + if (mLoadedApk != null) { + IServiceConnection sd = mLoadedApk.forgetServiceDispatcher( getOuterContext(), conn); try { ActivityManager.getService().unbindService(sd); @@ -1985,40 +1985,20 @@ class ContextImpl extends Context { } } - private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName, - int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) { - final String[] splitResDirs; - final ClassLoader classLoader; - try { - splitResDirs = pi.getSplitPaths(splitName); - classLoader = pi.getSplitClassLoader(splitName); - } catch (NameNotFoundException e) { - throw new RuntimeException(e); - } - return ResourcesManager.getInstance().getResources(activityToken, - pi.getResDir(), - splitResDirs, - pi.getOverlayDirs(), - pi.getApplicationInfo().sharedLibraryFiles, - displayId, - overrideConfig, - compatInfo, - classLoader); - } - @Override public Context createApplicationContext(ApplicationInfo application, int flags) throws NameNotFoundException { - LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(), + LoadedApk loadedApk = mMainThread.getLoadedApk(application, + mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE); - if (pi != null) { - ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, + if (loadedApk != null) { + ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, new UserHandle(UserHandle.getUserId(application.uid)), flags, null); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - c.setResources(createResources(mActivityToken, pi, null, displayId, null, + c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null, getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; @@ -2042,20 +2022,21 @@ class ContextImpl extends Context { if (packageName.equals("system") || packageName.equals("android")) { // The system resources are loaded in every application, so we can safely copy // the context without reloading Resources. - return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user, + return new ContextImpl(this, mMainThread, mLoadedApk, null, mActivityToken, user, flags, null); } - LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), + LoadedApk loadedApk = mMainThread.getLoadedApkForPackageName(packageName, + mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); - if (pi != null) { - ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user, + if (loadedApk != null) { + ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, user, flags, null); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - c.setResources(createResources(mActivityToken, pi, null, displayId, null, + c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null, getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; @@ -2069,30 +2050,21 @@ class ContextImpl extends Context { @Override public Context createContextForSplit(String splitName) throws NameNotFoundException { - if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) { + if (!mLoadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) { // All Splits are always loaded. return this; } - final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName); - final String[] paths = mPackageInfo.getSplitPaths(splitName); + final ClassLoader classLoader = mLoadedApk.getSplitClassLoader(splitName); - final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName, + final ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, splitName, mActivityToken, mUser, mFlags, classLoader); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - context.setResources(ResourcesManager.getInstance().getResources( - mActivityToken, - mPackageInfo.getResDir(), - paths, - mPackageInfo.getOverlayDirs(), - mPackageInfo.getApplicationInfo().sharedLibraryFiles, - displayId, - null, - mPackageInfo.getCompatibilityInfo(), - classLoader)); + context.setResources(mLoadedApk.getOrCreateResourcesForSplit(splitName, + mActivityToken, displayId)); return context; } @@ -2102,11 +2074,11 @@ class ContextImpl extends Context { throw new IllegalArgumentException("overrideConfiguration must not be null"); } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, + ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser, mFlags, mClassLoader); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, + context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId, overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo())); return context; } @@ -2117,11 +2089,11 @@ class ContextImpl extends Context { throw new IllegalArgumentException("display must not be null"); } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, + ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser, mFlags, mClassLoader); final int displayId = display.getDisplayId(); - context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, + context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId, null, getDisplayAdjustments(displayId).getCompatibilityInfo())); context.mDisplay = display; return context; @@ -2131,7 +2103,7 @@ class ContextImpl extends Context { public Context createDeviceProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE) | Context.CONTEXT_DEVICE_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser, + return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser, flags, mClassLoader); } @@ -2139,7 +2111,7 @@ class ContextImpl extends Context { public Context createCredentialProtectedStorageContext() { final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE) | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE; - return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser, + return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser, flags, mClassLoader); } @@ -2188,14 +2160,14 @@ class ContextImpl extends Context { @Override public File getDataDir() { - if (mPackageInfo != null) { + if (mLoadedApk != null) { File res = null; if (isCredentialProtectedStorage()) { - res = mPackageInfo.getCredentialProtectedDataDirFile(); + res = mLoadedApk.getCredentialProtectedDataDirFile(); } else if (isDeviceProtectedStorage()) { - res = mPackageInfo.getDeviceProtectedDataDirFile(); + res = mLoadedApk.getDeviceProtectedDataDirFile(); } else { - res = mPackageInfo.getDataDirFile(); + res = mLoadedApk.getDataDirFile(); } if (res != null) { @@ -2246,10 +2218,10 @@ class ContextImpl extends Context { } static ContextImpl createSystemContext(ActivityThread mainThread) { - LoadedApk packageInfo = new LoadedApk(mainThread); - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, + LoadedApk loadedApk = new LoadedApk(mainThread); + ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0, null); - context.setResources(packageInfo.getResources()); + context.setResources(loadedApk.getResources()); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetrics()); return context; @@ -2260,35 +2232,35 @@ class ContextImpl extends Context { * Make sure that the created system UI context shares the same LoadedApk as the system context. */ static ContextImpl createSystemUiContext(ContextImpl systemContext) { - final LoadedApk packageInfo = systemContext.mPackageInfo; - ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null, + final LoadedApk loadedApk = systemContext.mLoadedApk; + ContextImpl context = new ContextImpl(null, systemContext.mMainThread, loadedApk, null, null, null, 0, null); - context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null, - packageInfo.getCompatibilityInfo())); + context.setResources(loadedApk.createResources(null, null, Display.DEFAULT_DISPLAY, null, + loadedApk.getCompatibilityInfo())); return context; } - static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { - if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, + static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) { + if (loadedApk == null) throw new IllegalArgumentException("loadedApk"); + ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0, null); - context.setResources(packageInfo.getResources()); + context.setResources(loadedApk.getResources()); return context; } static ContextImpl createActivityContext(ActivityThread mainThread, - LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId, + LoadedApk loadedApk, ActivityInfo activityInfo, IBinder activityToken, int displayId, Configuration overrideConfiguration) { - if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); + if (loadedApk == null) throw new IllegalArgumentException("loadedApk"); - String[] splitDirs = packageInfo.getSplitResDirs(); - ClassLoader classLoader = packageInfo.getClassLoader(); + String[] splitDirs = loadedApk.getSplitResDirs(); + ClassLoader classLoader = loadedApk.getClassLoader(); - if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) { + if (loadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies"); try { - classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName); - splitDirs = packageInfo.getSplitPaths(activityInfo.splitName); + classLoader = loadedApk.getSplitClassLoader(activityInfo.splitName); + splitDirs = loadedApk.getSplitPaths(activityInfo.splitName); } catch (NameNotFoundException e) { // Nothing above us can handle a NameNotFoundException, better crash. throw new RuntimeException(e); @@ -2297,14 +2269,14 @@ class ContextImpl extends Context { } } - ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName, + ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName, activityToken, null, 0, classLoader); // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY) - ? packageInfo.getCompatibilityInfo() + ? loadedApk.getCompatibilityInfo() : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; final ResourcesManager resourcesManager = ResourcesManager.getInstance(); @@ -2312,10 +2284,10 @@ class ContextImpl extends Context { // Create the base resources for which all configuration contexts for this Activity // will be rebased upon. context.setResources(resourcesManager.createBaseActivityResources(activityToken, - packageInfo.getResDir(), + loadedApk.getResDir(), splitDirs, - packageInfo.getOverlayDirs(), - packageInfo.getApplicationInfo().sharedLibraryFiles, + loadedApk.getOverlayDirs(), + loadedApk.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, compatInfo, @@ -2326,7 +2298,7 @@ class ContextImpl extends Context { } private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread, - @NonNull LoadedApk packageInfo, @Nullable String splitName, + @NonNull LoadedApk loadedApk, @Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user, int flags, @Nullable ClassLoader classLoader) { mOuterContext = this; @@ -2335,10 +2307,10 @@ class ContextImpl extends Context { // location for application. if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE | Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) { - final File dataDir = packageInfo.getDataDirFile(); - if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) { + final File dataDir = loadedApk.getDataDirFile(); + if (Objects.equals(dataDir, loadedApk.getCredentialProtectedDataDirFile())) { flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE; - } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) { + } else if (Objects.equals(dataDir, loadedApk.getDeviceProtectedDataDirFile())) { flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE; } } @@ -2352,7 +2324,7 @@ class ContextImpl extends Context { } mUser = user; - mPackageInfo = packageInfo; + mLoadedApk = loadedApk; mSplitName = splitName; mClassLoader = classLoader; mResourcesManager = ResourcesManager.getInstance(); @@ -2363,8 +2335,8 @@ class ContextImpl extends Context { setResources(container.mResources); mDisplay = container.mDisplay; } else { - mBasePackageName = packageInfo.mPackageName; - ApplicationInfo ainfo = packageInfo.getApplicationInfo(); + mBasePackageName = loadedApk.mPackageName; + ApplicationInfo ainfo = loadedApk.getApplicationInfo(); if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) { // Special case: system components allow themselves to be loaded in to other // processes. For purposes of app ops, we must then consider the context as @@ -2387,7 +2359,7 @@ class ContextImpl extends Context { } void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { - mPackageInfo.installSystemApplicationInfo(info, classLoader); + mLoadedApk.installSystemApplicationInfo(info, classLoader); } final void scheduleFinalCleanup(String who, String what) { @@ -2396,7 +2368,7 @@ class ContextImpl extends Context { final void performFinalCleanup(String who, String what) { //Log.i(TAG, "Cleanup up context: " + this); - mPackageInfo.removeContextRegistrations(getOuterContext(), who, what); + mLoadedApk.removeContextRegistrations(getOuterContext(), who, what); } final Context getReceiverRestrictedContext() { diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 490b2bf39f53..41324d0813b8 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1209,10 +1209,10 @@ public class Instrumentation { } private AppComponentFactory getFactory(String pkg) { - LoadedApk apk = mThread.peekPackageInfo(pkg, true); + LoadedApk loadedApk = mThread.peekLoadedApk(pkg, true); // This is in the case of starting up "android". - if (apk == null) apk = mThread.getSystemContext().mPackageInfo; - return apk.getAppFactory(); + if (loadedApk == null) loadedApk = mThread.getSystemContext().mLoadedApk; + return loadedApk.getAppFactory(); } private void prePerformCreate(Activity activity) { diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 9ec7f4134a30..88e235660c09 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -166,7 +166,7 @@ public abstract class LauncherActivity extends ListActivity { 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 ab00a7ddde55..26f498087786 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -31,6 +31,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.split.SplitDependencyLoader; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; @@ -48,15 +49,13 @@ import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.Log; +import android.util.LogPrinter; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayAdjustments; - import com.android.internal.util.ArrayUtils; - import dalvik.system.VMRuntime; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -968,14 +967,78 @@ public final class LoadedApk { throw new AssertionError("null split not found"); } - mResources = ResourcesManager.getInstance().getResources(null, mResDir, - splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, - Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(), + mResources = ResourcesManager.getInstance().getResources( + null, + mResDir, + splitPaths, + mOverlayDirs, + mApplicationInfo.sharedLibraryFiles, + Display.DEFAULT_DISPLAY, + null, + getCompatibilityInfo(), getClassLoader()); } return mResources; } + public Resources getOrCreateResourcesForSplit(@NonNull String splitName, + @Nullable IBinder activityToken, int displayId) throws NameNotFoundException { + return ResourcesManager.getInstance().getResources( + activityToken, + mResDir, + getSplitPaths(splitName), + mOverlayDirs, + mApplicationInfo.sharedLibraryFiles, + displayId, + null, + getCompatibilityInfo(), + getSplitClassLoader(splitName)); + } + + /** + * Creates the top level resources for the given package. Will return an existing + * Resources if one has already been created. + */ + public Resources getOrCreateTopLevelResources(@NonNull ApplicationInfo appInfo) { + // Request for this app, short circuit + if (appInfo.uid == Process.myUid()) { + return getResources(); + } + + // Get resources for a different package + return ResourcesManager.getInstance().getResources( + null, + appInfo.publicSourceDir, + appInfo.splitPublicSourceDirs, + appInfo.resourceDirs, + appInfo.sharedLibraryFiles, + Display.DEFAULT_DISPLAY, + null, + getCompatibilityInfo(), + getClassLoader()); + } + + public Resources createResources(IBinder activityToken, String splitName, + int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) { + final String[] splitResDirs; + final ClassLoader classLoader; + try { + splitResDirs = getSplitPaths(splitName); + classLoader = getSplitClassLoader(splitName); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + return ResourcesManager.getInstance().getResources(activityToken, + mResDir, + splitResDirs, + mOverlayDirs, + mApplicationInfo.sharedLibraryFiles, + displayId, + overrideConfig, + compatInfo, + classLoader); + } + public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { if (mApplication != null) { diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java index 073d28cfa27f..8304c1c5a2d8 100644 --- a/core/java/android/app/servertransaction/PendingTransactionActions.java +++ b/core/java/android/app/servertransaction/PendingTransactionActions.java @@ -134,7 +134,7 @@ public class PendingTransactionActions { Bundle.dumpStats(pw, mPersistentState); if (ex instanceof TransactionTooLargeException - && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) { + && mActivity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) { Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex); return; } diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index f8e19c12b810..0c5f225d515e 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -16,24 +16,37 @@ package android.app.slice; +import android.annotation.NonNull; import android.annotation.SystemService; -import android.app.slice.ISliceListener.Stub; import android.content.Context; import android.net.Uri; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import android.util.ArrayMap; +import android.util.Pair; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executor; /** - * @hide + * Class to handle interactions with {@link Slice}s. + * <p> + * The SliceManager manages permissions and pinned state for slices. */ @SystemService(Context.SLICE_SERVICE) public class SliceManager { private final ISliceManager mService; private final Context mContext; + private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup = + new ArrayMap<>(); + /** + * @hide + */ public SliceManager(Context context, Handler handler) throws ServiceNotFoundException { mContext = context; mService = ISliceManager.Stub.asInterface( @@ -41,38 +54,142 @@ public class SliceManager { } /** + * Adds a callback to a specific slice uri. + * <p> + * This is a convenience that performs a few slice actions at once. It will put + * the slice in a pinned state since there is a callback attached. It will also + * listen for content changes, when a content change observes, the android system + * will bind the new slice and provide it to all registered {@link SliceCallback}s. + * + * @param uri The uri of the slice being listened to. + * @param callback The listener that should receive the callbacks. + * @param specs The list of supported {@link SliceSpec}s of the callback. + * @see SliceProvider#onSlicePinned(Uri) + */ + public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback, + @NonNull List<SliceSpec> specs) { + registerSliceCallback(uri, callback, specs, Handler.getMain()); + } + + /** + * Adds a callback to a specific slice uri. + * <p> + * This is a convenience that performs a few slice actions at once. It will put + * the slice in a pinned state since there is a callback attached. It will also + * listen for content changes, when a content change observes, the android system + * will bind the new slice and provide it to all registered {@link SliceCallback}s. + * + * @param uri The uri of the slice being listened to. + * @param callback The listener that should receive the callbacks. + * @param specs The list of supported {@link SliceSpec}s of the callback. + * @see SliceProvider#onSlicePinned(Uri) */ - public void addSliceListener(Uri uri, SliceListener listener, SliceSpec[] specs) { + public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback, + @NonNull List<SliceSpec> specs, Handler handler) { try { - mService.addSliceListener(uri, mContext.getPackageName(), listener.mStub, specs); + mService.addSliceListener(uri, mContext.getPackageName(), + getListener(uri, callback, new ISliceListener.Stub() { + @Override + public void onSliceUpdated(Slice s) throws RemoteException { + handler.post(() -> callback.onSliceUpdated(s)); + } + }), specs.toArray(new SliceSpec[specs.size()])); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Adds a callback to a specific slice uri. + * <p> + * This is a convenience that performs a few slice actions at once. It will put + * the slice in a pinned state since there is a callback attached. It will also + * listen for content changes, when a content change observes, the android system + * will bind the new slice and provide it to all registered {@link SliceCallback}s. + * + * @param uri The uri of the slice being listened to. + * @param callback The listener that should receive the callbacks. + * @param specs The list of supported {@link SliceSpec}s of the callback. + * @see SliceProvider#onSlicePinned(Uri) */ - public void removeSliceListener(Uri uri, SliceListener listener) { + public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback, + @NonNull List<SliceSpec> specs, Executor executor) { try { - mService.removeSliceListener(uri, mContext.getPackageName(), listener.mStub); + mService.addSliceListener(uri, mContext.getPackageName(), + getListener(uri, callback, new ISliceListener.Stub() { + @Override + public void onSliceUpdated(Slice s) throws RemoteException { + executor.execute(() -> callback.onSliceUpdated(s)); + } + }), specs.toArray(new SliceSpec[specs.size()])); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + private ISliceListener getListener(Uri uri, SliceCallback callback, + ISliceListener listener) { + Pair<Uri, SliceCallback> key = new Pair<>(uri, callback); + if (mListenerLookup.containsKey(key)) { + try { + mService.removeSliceListener(uri, mContext.getPackageName(), + mListenerLookup.get(key)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + mListenerLookup.put(key, listener); + return listener; + } + /** + * Removes a callback for a specific slice uri. + * <p> + * Removes the app from the pinned state (if there are no other apps/callbacks pinning it) + * in addition to removing the callback. + * + * @param uri The uri of the slice being listened to + * @param callback The listener that should no longer receive callbacks. + * @see #registerSliceCallback */ - public void pinSlice(Uri uri, SliceSpec[] specs) { + public void unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) { try { - mService.pinSlice(mContext.getPackageName(), uri, specs); + mService.removeSliceListener(uri, mContext.getPackageName(), + mListenerLookup.remove(new Pair<>(uri, callback))); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Ensures that a slice is in a pinned state. + * <p> + * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices + * they still care about after a reboot. + * + * @param uri The uri of the slice being pinned. + * @param specs The list of supported {@link SliceSpec}s of the callback. + * @see SliceProvider#onSlicePinned(Uri) */ - public void unpinSlice(Uri uri) { + public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) { + try { + mService.pinSlice(mContext.getPackageName(), uri, + specs.toArray(new SliceSpec[specs.size()])); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove a pin for a slice. + * <p> + * If the slice has no other pins/callbacks then the slice will be unpinned. + * + * @param uri The uri of the slice being unpinned. + * @see #pinSlice + * @see SliceProvider#onSliceUnpinned(Uri) + */ + public void unpinSlice(@NonNull Uri uri) { try { mService.unpinSlice(mContext.getPackageName(), uri); } catch (RemoteException e) { @@ -81,6 +198,7 @@ public class SliceManager { } /** + * @hide */ public boolean hasSliceAccess() { try { @@ -91,41 +209,31 @@ public class SliceManager { } /** + * Get the current set of specs for a pinned slice. + * <p> + * This is the set of specs supported for a specific pinned slice. It will take + * into account all clients and returns only specs supported by all. + * @see SliceSpec */ - public SliceSpec[] getPinnedSpecs(Uri uri) { + public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) { try { - return mService.getPinnedSpecs(uri, mContext.getPackageName()); + return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Class that listens to changes in {@link Slice}s. */ - public abstract static class SliceListener { - private final Handler mHandler; - - /** - */ - public SliceListener() { - this(Handler.getMain()); - } + public interface SliceCallback { /** + * Called when slice is updated. + * + * @param s The updated slice. + * @see #registerSliceCallback */ - public SliceListener(Handler h) { - mHandler = h; - } - - /** - */ - public abstract void onSliceUpdated(Slice s); - - private final ISliceListener.Stub mStub = new Stub() { - @Override - public void onSliceUpdated(Slice s) throws RemoteException { - mHandler.post(() -> SliceListener.this.onSliceUpdated(s)); - } - }; + void onSliceUpdated(Slice s); } } diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index 7dcd2fead73f..8483931ceaec 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -151,13 +151,33 @@ public abstract class SliceProvider extends ContentProvider { } /** - * @hide + * Called to inform an app that a slice has been pinned. + * <p> + * Pinning is a way that slice hosts use to notify apps of which slices + * they care about updates for. When a slice is pinned the content is + * expected to be relatively fresh and kept up to date. + * <p> + * Being pinned does not provide any escalated privileges for the slice + * provider. So apps should do things such as turn on syncing or schedule + * a job in response to a onSlicePinned. + * <p> + * Pinned state is not persisted through a reboot, and apps can expect a + * new call to onSlicePinned for any slices that should remain pinned + * after a reboot occurs. + * + * @param sliceUri The uri of the slice being unpinned. + * @see #onSliceUnpinned(Uri) */ public void onSlicePinned(Uri sliceUri) { } /** - * @hide + * Called to inform an app that a slices is no longer pinned. + * <p> + * This means that no other apps on the device care about updates to this + * slice anymore and therefore it is not important to be updated. Any syncs + * or jobs related to this slice should be cancelled. + * @see #onSlicePinned(Uri) */ public void onSliceUnpinned(Uri sliceUri) { } diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index 4a0048673c28..f38e462eab4f 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -35,8 +35,6 @@ import java.util.List; * * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. - * - * <p>{@hide} */ public final class BluetoothHidDevice implements BluetoothProfile { diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 4609d52df0c0..c05df2d23e45 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -26,8 +26,6 @@ import android.os.Parcelable; * registration. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @@ -46,10 +44,8 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS - * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see <a - * href="https://www.bluetooth.com/specifications/profiles-overview"> - * https://www.bluetooth.com/specifications/profiles-overview </a> Bluetooth HID Specfication - * v1.1.1 Section 5.2 and Appendix D } + * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. + * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters. * * @param serviceType L2CAP service type * @param tokenRate L2CAP token rate diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 2da64e5a5023..562c559eddc4 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -28,8 +28,6 @@ import java.util.Arrays; * Android device can be discovered as a Bluetooth HID Device. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java index 6ed19654b4c5..bd19955b4e62 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -24,8 +24,6 @@ import android.util.Log; * registration. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public abstract class BluetoothHidDeviceCallback { diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index ebbc710922c2..df2028a55351 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -153,8 +153,6 @@ public interface BluetoothProfile { /** * HID Device - * - * @hide */ public static final int HID_DEVICE = 19; diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index ec5b1c668a27..c94da9a12cad 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -4244,19 +4244,39 @@ public final class ContactsContract { /** * 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/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index 6be0e76c552b..6a3c55efda8a 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -297,7 +297,7 @@ public class VoicemailContract { * {@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/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java index 2707f1467bcf..b94ccf9e8223 100644 --- a/core/java/android/service/carrier/CarrierService.java +++ b/core/java/android/service/carrier/CarrierService.java @@ -33,8 +33,8 @@ import com.android.internal.telephony.ITelephonyRegistry; * 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 54b48b619101..b7d33599a396 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -41,7 +41,7 @@ public class FeatureFlagUtils { 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/proto/android/graphics/point.proto b/core/proto/android/graphics/point.proto new file mode 100644 index 000000000000..5ae17cb5df5d --- /dev/null +++ b/core/proto/android/graphics/point.proto @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.graphics; + +option java_multiple_files = true; + +message PointProto { + optional int32 x = 1; + optional int32 y = 2; +} + diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 915fe9d5ce41..ef7b6b0019f3 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -170,6 +170,8 @@ message WindowStateProto { optional WindowStateAnimatorProto animator = 13; optional bool animating_exit = 14; repeated WindowStateProto child_windows = 15; + optional .android.graphics.RectProto surface_position = 16; + optional .android.graphics.RectProto shown_position = 17; } message IdentifierProto { diff --git a/core/res/res/layout/activity_list_item_2.xml b/core/res/res/layout/activity_list_item_2.xml index 608e9861dfbe..af1963c7c39a 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/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java index abcccbdbc9fb..c6b6c668f03a 100644 --- a/graphics/java/android/graphics/Point.java +++ b/graphics/java/android/graphics/Point.java @@ -18,6 +18,7 @@ package android.graphics; import android.os.Parcel; import android.os.Parcelable; +import android.util.proto.ProtoOutputStream; import java.io.PrintWriter; @@ -121,6 +122,21 @@ public class Point implements Parcelable { out.writeInt(y); } + /** + * Write to a protocol buffer output stream. + * Protocol buffer message definition at {@link android.graphics.PointProto} + * + * @param protoOutputStream Stream to write the Rect object to. + * @param fieldId Field Id of the Rect as defined in the parent message + * @hide + */ + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + final long token = protoOutputStream.start(fieldId); + protoOutputStream.write(PointProto.X, x); + protoOutputStream.write(PointProto.Y, y); + protoOutputStream.end(token); + } + public static final Parcelable.Creator<Point> CREATOR = new Parcelable.Creator<Point>() { /** * Return a new point from the data in the specified parcel. diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 4adcb99f5628..94b06b67ff87 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -25,7 +25,6 @@ import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKU import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR; -import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_INIT; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR; @@ -120,6 +119,7 @@ import com.android.server.backup.params.RestoreParams; import com.android.server.backup.restore.ActiveRestoreSession; import com.android.server.backup.restore.PerformUnifiedRestoreTask; import com.android.server.backup.transport.TransportClient; +import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.BackupManagerMonitorUtils; import com.android.server.backup.utils.BackupObserverUtils; @@ -1083,56 +1083,35 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter return mBackupPasswordManager.backupPasswordMatches(currentPw); } - // Maintain persistent state around whether need to do an initialize operation. - // Must be called with the queue lock held. - public void recordInitPendingLocked(boolean isPending, String transportName) { + /** + * Maintain persistent state around whether need to do an initialize operation. + * Must be called with the queue lock held. + */ + @GuardedBy("mQueueLock") + public void recordInitPendingLocked( + boolean isPending, String transportName, String transportDirName) { if (MORE_DEBUG) { Slog.i(TAG, "recordInitPendingLocked: " + isPending + " on transport " + transportName); } - mBackupHandler.removeMessages(MSG_RETRY_INIT); - try { - IBackupTransport transport = mTransportManager.getTransportBinder(transportName); - if (transport != null) { - String transportDirName = transport.transportDirName(); - File stateDir = new File(mBaseStateDir, transportDirName); - File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); - - if (isPending) { - // We need an init before we can proceed with sending backup data. - // Record that with an entry in our set of pending inits, as well as - // journaling it via creation of a sentinel file. - mPendingInits.add(transportName); - try { - (new FileOutputStream(initPendingFile)).close(); - } catch (IOException ioe) { - // Something is badly wrong with our permissions; just try to move on - } - } else { - // No more initialization needed; wipe the journal and reset our state. - initPendingFile.delete(); - mPendingInits.remove(transportName); - } - return; // done; don't fall through to the error case - } - } catch (Exception e) { - // transport threw when asked its name; fall through to the lookup-failed case - Slog.e(TAG, "Transport " + transportName + " failed to report name: " - + e.getMessage()); - } + File stateDir = new File(mBaseStateDir, transportDirName); + File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); - // The named transport doesn't exist or threw. This operation is - // important, so we record the need for a an init and post a message - // to retry the init later. if (isPending) { + // We need an init before we can proceed with sending backup data. + // Record that with an entry in our set of pending inits, as well as + // journaling it via creation of a sentinel file. mPendingInits.add(transportName); - mBackupHandler.sendMessageDelayed( - mBackupHandler.obtainMessage(MSG_RETRY_INIT, - (isPending ? 1 : 0), - 0, - transportName), - TRANSPORT_RETRY_INTERVAL); + try { + (new FileOutputStream(initPendingFile)).close(); + } catch (IOException ioe) { + // Something is badly wrong with our permissions; just try to move on + } + } else { + // No more initialization needed; wipe the journal and reset our state. + initPendingFile.delete(); + mPendingInits.remove(transportName); } } @@ -1614,27 +1593,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter return BackupManager.ERROR_BACKUP_NOT_ALLOWED; } - // We're using pieces of the new binding on-demand infra-structure and the old always-bound - // infra-structure below this comment. The TransportManager.getCurrentTransportClient() line - // is using the new one and TransportManager.getCurrentTransportBinder() is using the old. - // This is weird but there is a reason. - // This is the natural place to put TransportManager.getCurrentTransportClient() because of - // the null handling below that should be the same for TransportClient. - // TransportClient.connect() would return a IBackupTransport for us (instead of using the - // old infra), but it may block and we don't want this in this thread. - // The only usage of transport in this method is for transport.transportDirName(). When the - // push-from-transport part of binding on-demand is in place we will replace the calls for - // IBackupTransport.transportDirName() with calls for - // TransportManager.transportDirName(transportName) or similar. So we'll leave the old piece - // here until we implement that. - // TODO(brufino): Remove always-bound code mTransportManager.getCurrentTransportBinder() TransportClient transportClient = mTransportManager.getCurrentTransportClient("BMS.requestBackup()"); - IBackupTransport transport = mTransportManager.getCurrentTransportBinder(); - if (transportClient == null || transport == null) { - if (transportClient != null) { - mTransportManager.disposeOfTransportClient(transportClient, "BMS.requestBackup()"); - } + if (transportClient == null) { BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED); monitor = BackupManagerMonitorUtils.monitorEvent(monitor, BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL, @@ -1679,15 +1640,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter + " k/v backups"); } - String dirName; - try { - dirName = transport.transportDirName(); - } catch (Exception e) { - Slog.e(TAG, "Transport unavailable while attempting backup: " + e.getMessage()); - BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED); - return BackupManager.ERROR_TRANSPORT_ABORTED; - } - + String dirName = transportClient.getTransportDirName(); boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0; Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP); @@ -1998,16 +1951,17 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter writeFullBackupScheduleAsync(); } - private boolean fullBackupAllowable(IBackupTransport transport) { - if (transport == null) { - Slog.w(TAG, "Transport not present; full data backup not performed"); + private boolean fullBackupAllowable(String transportName) { + if (!mTransportManager.isTransportRegistered(transportName)) { + Slog.w(TAG, "Transport not registered; full data backup not performed"); return false; } // Don't proceed unless we have already established package metadata // for the current dataset via a key/value backup pass. try { - File stateDir = new File(mBaseStateDir, transport.transportDirName()); + String transportDirName = mTransportManager.getTransportDirName(transportName); + File stateDir = new File(mBaseStateDir, transportDirName); File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL); if (pmState.length() <= 0) { if (DEBUG) { @@ -2097,7 +2051,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter headBusy = false; - if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) { + String transportName = mTransportManager.getCurrentTransportName(); + if (!fullBackupAllowable(transportName)) { if (MORE_DEBUG) { Slog.i(TAG, "Preconditions not met; not running full backup"); } @@ -2545,7 +2500,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter throw new IllegalStateException("Restore supported only for the device owner"); } - if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) { + String transportName = mTransportManager.getCurrentTransportName(); + if (!fullBackupAllowable(transportName)) { Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?"); } else { if (DEBUG) { @@ -2826,10 +2782,30 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter if (wasEnabled && mProvisioned) { // NOTE: we currently flush every registered transport, not just // the currently-active one. - String[] allTransports = mTransportManager.getBoundTransportNames(); + List<String> transportNames = new ArrayList<>(); + List<String> transportDirNames = new ArrayList<>(); + mTransportManager.forEachRegisteredTransport( + name -> { + final String dirName; + try { + dirName = + mTransportManager + .getTransportDirName(name); + } catch (TransportNotRegisteredException e) { + // Should never happen + Slog.e(TAG, "Unexpected unregistered transport", e); + return; + } + transportNames.add(name); + transportDirNames.add(dirName); + }); + // build the set of transports for which we are posting an init - for (String transport : allTransports) { - recordInitPendingLocked(true, transport); + for (int i = 0; i < transportNames.size(); i++) { + recordInitPendingLocked( + true, + transportNames.get(i), + transportDirNames.get(i)); } mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), mRunInitIntent); @@ -2993,7 +2969,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter final long oldId = Binder.clearCallingIdentity(); try { - mTransportManager.describeTransport( + mTransportManager.updateTransportAttributes( transportComponent, name, configurationIntent, @@ -3093,23 +3069,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter public Intent getConfigurationIntent(String transportName) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getConfigurationIntent"); - - final IBackupTransport transport = mTransportManager.getTransportBinder(transportName); - if (transport != null) { - try { - final Intent intent = transport.configurationIntent(); - if (MORE_DEBUG) { - Slog.d(TAG, "getConfigurationIntent() returning config intent " - + intent); - } - return intent; - } catch (Exception e) { - /* fall through to return null */ - Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage()); + try { + Intent intent = mTransportManager.getTransportConfigurationIntent(transportName); + if (MORE_DEBUG) { + Slog.d(TAG, "getConfigurationIntent() returning intent " + intent); } + return intent; + } catch (TransportNotRegisteredException e) { + Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage()); + return null; } - - return null; } // Supply the configuration summary string for the given transport. If the name is @@ -3143,22 +3112,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getDataManagementIntent"); - final IBackupTransport transport = mTransportManager.getTransportBinder(transportName); - if (transport != null) { - try { - final Intent intent = transport.dataManagementIntent(); - if (MORE_DEBUG) { - Slog.d(TAG, "getDataManagementIntent() returning intent " - + intent); - } - return intent; - } catch (Exception e) { - /* fall through to return null */ - Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage()); + try { + Intent intent = mTransportManager.getTransportDataManagementIntent(transportName); + if (MORE_DEBUG) { + Slog.d(TAG, "getDataManagementIntent() returning intent " + intent); } + return intent; + } catch (TransportNotRegisteredException e) { + Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage()); + return null; } - - return null; } // Supply the menu label for affordances that fire the manage-data intent @@ -3168,19 +3131,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getDataManagementLabel"); - final IBackupTransport transport = mTransportManager.getTransportBinder(transportName); - if (transport != null) { - try { - final String text = transport.dataManagementLabel(); - if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text); - return text; - } catch (Exception e) { - /* fall through to return null */ - Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage()); + try { + String label = mTransportManager.getTransportDataManagementLabel(transportName); + if (MORE_DEBUG) { + Slog.d(TAG, "getDataManagementLabel() returning " + label); } + return label; + } catch (TransportNotRegisteredException e) { + Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage()); + return null; } - - return null; } // Callback: a requested backup agent has been instantiated. This should only @@ -3497,14 +3457,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter pw.println("Available transports:"); final String[] transports = listAllTransports(); if (transports != null) { - for (String t : listAllTransports()) { + for (String t : transports) { pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * " : " ") + t); try { IBackupTransport transport = mTransportManager.getTransportBinder(t); - File dir = new File(mBaseStateDir, transport.transportDirName()); + File dir = new File(mBaseStateDir, + mTransportManager.getTransportDirName(t)); pw.println(" destination: " + transport.currentDestinationString()); - pw.println(" intent: " + transport.configurationIntent()); + pw.println(" intent: " + + mTransportManager.getTransportConfigurationIntent(t)); for (File f : dir.listFiles()) { pw.println( " " + f.getName() + " - " + f.length() + " state bytes"); diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java index 1f3ebf936ceb..f1854433dcd0 100644 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ b/services/backup/java/com/android/server/backup/TransportManager.java @@ -49,12 +49,14 @@ import com.android.server.EventLogTags; import com.android.server.backup.transport.TransportClient; import com.android.server.backup.transport.TransportClientManager; import com.android.server.backup.transport.TransportConnectionListener; +import com.android.server.backup.transport.TransportNotRegisteredException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -236,6 +238,72 @@ public class TransportManager { return getTransportBinder(mCurrentTransportName); } + /** + * Retrieve the configuration intent of {@code transportName}. + * @throws TransportNotRegisteredException if the transport is not registered. + */ + @Nullable + public Intent getTransportConfigurationIntent(String transportName) + throws TransportNotRegisteredException { + synchronized (mTransportLock) { + return getRegisteredTransportDescriptionOrThrowLocked(transportName) + .configurationIntent; + } + } + + /** + * Retrieve the data management intent of {@code transportName}. + * @throws TransportNotRegisteredException if the transport is not registered. + */ + @Nullable + public Intent getTransportDataManagementIntent(String transportName) + throws TransportNotRegisteredException { + synchronized (mTransportLock) { + return getRegisteredTransportDescriptionOrThrowLocked(transportName) + .dataManagementIntent; + } + } + + /** + * Retrieve the data management label of {@code transportName}. + * @throws TransportNotRegisteredException if the transport is not registered. + */ + @Nullable + public String getTransportDataManagementLabel(String transportName) + throws TransportNotRegisteredException { + synchronized (mTransportLock) { + return getRegisteredTransportDescriptionOrThrowLocked(transportName) + .dataManagementLabel; + } + } + + /** + * Retrieve the transport dir name of {@code transportName}. + * @throws TransportNotRegisteredException if the transport is not registered. + */ + public String getTransportDirName(String transportName) + throws TransportNotRegisteredException { + synchronized (mTransportLock) { + return getRegisteredTransportDescriptionOrThrowLocked(transportName) + .transportDirName; + } + } + + /** + * Execute {@code transportConsumer} for each registered transport passing the transport name. + * This is called with an internal lock held, ensuring that the transport will remain registered + * while {@code transportConsumer} is being executed. Don't do heavy operations in + * {@code transportConsumer}. + */ + public void forEachRegisteredTransport(Consumer<String> transportConsumer) { + synchronized (mTransportLock) { + for (TransportDescription transportDescription + : mRegisteredTransportsDescriptionMap.values()) { + transportConsumer.accept(transportDescription.name); + } + } + } + public String getTransportName(IBackupTransport binder) { synchronized (mTransportLock) { for (TransportConnection conn : mValidTransports.values()) { @@ -281,6 +349,17 @@ public class TransportManager { } @GuardedBy("mTransportLock") + private TransportDescription getRegisteredTransportDescriptionOrThrowLocked( + String transportName) throws TransportNotRegisteredException { + TransportDescription description = getRegisteredTransportDescriptionLocked(transportName); + if (description == null) { + throw new TransportNotRegisteredException(transportName); + } + return description; + } + + + @GuardedBy("mTransportLock") @Nullable private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked( String transportName) { @@ -385,13 +464,13 @@ public class TransportManager { * Updates given values for the transport already registered and identified with * {@param transportComponent}. If the transport is not registered it will log and return. */ - public void describeTransport( + public void updateTransportAttributes( ComponentName transportComponent, String name, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, - String dataManagementLabel) { + @Nullable String dataManagementLabel) { synchronized (mTransportLock) { TransportDescription description = mRegisteredTransportsDescriptionMap.get(transportComponent); @@ -766,7 +845,7 @@ public class TransportManager { @Nullable private Intent configurationIntent; private String currentDestinationString; @Nullable private Intent dataManagementIntent; - private String dataManagementLabel; + @Nullable private String dataManagementLabel; private TransportDescription( String name, @@ -774,7 +853,7 @@ public class TransportManager { @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, - String dataManagementLabel) { + @Nullable String dataManagementLabel) { this.name = name; this.transportDirName = transportDirName; this.configurationIntent = configurationIntent; diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java index 4c7834825054..f29a9c2ecb6d 100644 --- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java +++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java @@ -293,16 +293,6 @@ public class BackupHandler extends Handler { break; } - case MSG_RETRY_INIT: { - synchronized (backupManagerService.getQueueLock()) { - backupManagerService.recordInitPendingLocked(msg.arg1 != 0, (String) msg.obj); - backupManagerService.getAlarmManager().set(AlarmManager.RTC_WAKEUP, - System.currentTimeMillis(), - backupManagerService.getRunInitIntent()); - } - break; - } - case MSG_RUN_GET_RESTORE_SETS: { // Like other async operations, this is entered with the wakelock held RestoreSet[] sets = null; diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java index 690922fd9aa9..b21b0724acc2 100644 --- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java +++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java @@ -98,7 +98,8 @@ public class PerformInitializeTask implements Runnable { transportDirName)); EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); synchronized (backupManagerService.getQueueLock()) { - backupManagerService.recordInitPendingLocked(false, transportName); + backupManagerService.recordInitPendingLocked( + false, transportName, transportDirName); } notifyResult(transportName, BackupTransport.TRANSPORT_OK); } else { @@ -107,7 +108,8 @@ public class PerformInitializeTask implements Runnable { Slog.e(TAG, "Transport error in initializeDevice()"); EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); synchronized (backupManagerService.getQueueLock()) { - backupManagerService.recordInitPendingLocked(true, transportName); + backupManagerService.recordInitPendingLocked( + true, transportName, transportDirName); } notifyResult(transportName, status); result = status; diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java new file mode 100644 index 000000000000..26bf92cb10eb --- /dev/null +++ b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.transport; + +import android.util.AndroidException; + +import com.android.server.backup.TransportManager; + +/** + * Exception thrown when the transport is not registered. + * + * @see TransportManager#getTransportDirName(String) + * @see TransportManager#getTransportConfigurationIntent(String) + * @see TransportManager#getTransportDataManagementIntent(String) + * @see TransportManager#getTransportDataManagementLabel(String) + */ +public class TransportNotRegisteredException extends AndroidException { + public TransportNotRegisteredException(String transportName) { + super("Transport " + transportName + " not registered"); + } +} diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java index 24d8d1e8f10f..9877717943df 100644 --- a/services/core/java/com/android/server/EntropyMixer.java +++ b/services/core/java/com/android/server/EntropyMixer.java @@ -70,7 +70,10 @@ public class EntropyMixer extends Binder { /** * 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 @@ public class EntropyMixer extends Binder { 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/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 009f10e74336..bcb57efffc19 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -279,8 +279,8 @@ public final class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L; - private static final int DEFAULT_STANDBY_WORKING_BEATS = 5; // ~ 1 hour, with 11-min beats - private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 31; // ~ 6 hours + private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats + private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours /** diff --git a/services/core/java/com/android/server/location/NanoAppStateManager.java b/services/core/java/com/android/server/location/NanoAppStateManager.java index 81c47849c3ad..98696265c209 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 android.util.Log; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; /** @@ -153,11 +154,12 @@ import java.util.List; 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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 00ab5bee8f3d..696d89575e2d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2901,7 +2901,7 @@ public class PackageManagerService extends IPackageManager.Stub final List<String> changedAbiCodePath = adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/); if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { - for (int i = changedAbiCodePath.size() - 1; i <= 0; --i) { + for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { final String codePathString = changedAbiCodePath.get(i); try { mInstaller.rmdex(codePathString, diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 95b139ad4da2..2bda80d99328 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -234,16 +234,7 @@ final class AccessibilityController { private static void populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix) { - sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx; - sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx; - sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy; - sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy; - sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x; - sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y; - sTempFloats[Matrix.MPERSP_0] = 0; - sTempFloats[Matrix.MPERSP_1] = 0; - sTempFloats[Matrix.MPERSP_2] = 1; - outMatrix.setValues(sTempFloats); + windowState.getTransformationMatrix(sTempFloats, outMatrix); } /** diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6bebcf407c75..ddc1eace1ea2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -118,7 +118,9 @@ import static com.android.server.wm.proto.WindowStateProto.GIVEN_CONTENT_INSETS; import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER; import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME; import static com.android.server.wm.proto.WindowStateProto.STACK_ID; +import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION; import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS; +import static com.android.server.wm.proto.WindowStateProto.SURFACE_POSITION; import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER; import android.annotation.CallSuper; @@ -3112,6 +3114,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mContentFrame.writeToProto(proto, CONTENT_FRAME); mContentInsets.writeToProto(proto, CONTENT_INSETS); mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS); + mSurfacePosition.writeToProto(proto, SURFACE_POSITION); + mShownPosition.writeToProto(proto, SHOWN_POSITION); mWinAnimator.writeToProto(proto, ANIMATOR); proto.write(ANIMATING_EXIT, mAnimatingExit); for (int i = 0; i < mChildren.size(); i++) { @@ -4314,6 +4318,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWinAnimator.onAnimationFinished(); } + /** + * Retrieves the current transformation matrix of the window, relative to the display. + * + * @param float9 A temporary array of 9 floats. + * @param outMatrix Matrix to fill in the transformation. + */ + void getTransformationMatrix(float[] float9, Matrix outMatrix) { + float9[Matrix.MSCALE_X] = mWinAnimator.mDsDx; + float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx; + float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy; + float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy; + float9[Matrix.MTRANS_X] = mSurfacePosition.x + mShownPosition.x; + float9[Matrix.MTRANS_Y] = mSurfacePosition.y + mShownPosition.y; + float9[Matrix.MPERSP_0] = 0; + float9[Matrix.MPERSP_1] = 0; + float9[Matrix.MPERSP_2] = 1; + outMatrix.setValues(float9); + } + // TODO: Hack to work around the number of states AppWindowToken needs to access without having // access to its windows children. Need to investigate re-writing // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed. diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java index 2cb4a69cdcc1..13623e50d9c7 100644 --- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java +++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java @@ -18,9 +18,13 @@ package com.android.server.backup; import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.fail; + import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.robolectric.shadow.api.Shadow.extract; +import android.annotation.Nullable; import android.app.backup.BackupManager; import android.content.ComponentName; import android.content.Intent; @@ -29,15 +33,18 @@ import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.IBinder; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; -import com.android.server.backup.testing.BackupTransportStub; +import com.android.internal.backup.IBackupTransport; +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.server.backup.testing.ShadowBackupTransportStub; import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.backup.testing.TransportBoundListenerStub; import com.android.server.backup.testing.TransportReadyCallbackStub; import com.android.server.backup.transport.TransportClient; +import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; @@ -95,15 +102,29 @@ public class TransportManagerTest { (ShadowPackageManagerForBackup) extract(RuntimeEnvironment.application.getPackageManager()); - mTransport1 = new TransportInfo(PACKAGE_NAME, "transport1.name"); - mTransport2 = new TransportInfo(PACKAGE_NAME, "transport2.name"); + mTransport1 = new TransportInfo( + PACKAGE_NAME, + "transport1.name", + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel"); + mTransport2 = new TransportInfo( + PACKAGE_NAME, + "transport2.name", + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel"); ShadowContextImplForBackup.sComponentBinderMap.put(mTransport1.componentName, mTransport1.binder); ShadowContextImplForBackup.sComponentBinderMap.put(mTransport2.componentName, mTransport2.binder); - ShadowBackupTransportStub.sBinderTransportMap.put(mTransport1.binder, mTransport1.stub); - ShadowBackupTransportStub.sBinderTransportMap.put(mTransport2.binder, mTransport2.stub); + ShadowBackupTransportStub.sBinderTransportMap.put( + mTransport1.binder, mTransport1.binderInterface); + ShadowBackupTransportStub.sBinderTransportMap.put( + mTransport2.binder, mTransport2.binderInterface); } @After @@ -129,8 +150,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isTrue(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -153,8 +176,10 @@ public class TransportManagerTest { Collections.singleton(mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Collections.singleton(mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -193,8 +218,10 @@ public class TransportManagerTest { Collections.singleton(mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Collections.singleton(mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -250,8 +277,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -265,8 +294,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isFalse(); } @Test @@ -280,8 +311,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isFalse(); } @Test @@ -295,8 +328,10 @@ public class TransportManagerTest { Arrays.asList(mTransport1.componentName, mTransport2.componentName)); assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) + .isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) + .isTrue(); } @Test @@ -305,9 +340,9 @@ public class TransportManagerTest { Arrays.asList(mTransport1, mTransport2), mTransport1.name); assertThat(transportManager.getTransportBinder(mTransport1.name)).isEqualTo( - mTransport1.stub); + mTransport1.binderInterface); assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo( - mTransport2.stub); + mTransport2.binderInterface); } @Test @@ -326,7 +361,7 @@ public class TransportManagerTest { assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull(); assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo( - mTransport2.stub); + mTransport2.binderInterface); } @Test @@ -356,7 +391,8 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - assertThat(transportManager.getCurrentTransportBinder()).isEqualTo(mTransport1.stub); + assertThat(transportManager.getCurrentTransportBinder()) + .isEqualTo(mTransport1.binderInterface); } @Test @@ -375,8 +411,10 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - assertThat(transportManager.getTransportName(mTransport1.stub)).isEqualTo(mTransport1.name); - assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name); + assertThat(transportManager.getTransportName(mTransport1.binderInterface)) + .isEqualTo(mTransport1.name); + assertThat(transportManager.getTransportName(mTransport2.binderInterface)) + .isEqualTo(mTransport2.name); } @Test @@ -385,8 +423,9 @@ public class TransportManagerTest { createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), Collections.singletonList(mTransport1), mTransport1.name); - assertThat(transportManager.getTransportName(mTransport1.stub)).isNull(); - assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name); + assertThat(transportManager.getTransportName(mTransport1.binderInterface)).isNull(); + assertThat(transportManager.getTransportName(mTransport2.binderInterface)) + .isEqualTo(mTransport2.name); } @Test @@ -499,7 +538,7 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - transportManager.describeTransport( + transportManager.updateTransportAttributes( mTransport1.componentName, "newName", null, "destinationString", null, null); TransportClient transportClient = @@ -514,7 +553,7 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - transportManager.describeTransport( + transportManager.updateTransportAttributes( mTransport1.componentName, "newName", null, "destinationString", null, null); TransportClient transportClient = @@ -529,7 +568,7 @@ public class TransportManagerTest { TransportManager transportManager = createTransportManagerAndSetUpTransports( Arrays.asList(mTransport1, mTransport2), mTransport1.name); - transportManager.describeTransport( + transportManager.updateTransportAttributes( mTransport1.componentName, "newName", null, "destinationString", null, null); String transportName = transportManager.getTransportName(mTransport1.componentName); @@ -549,6 +588,48 @@ public class TransportManagerTest { assertThat(transportManager.isTransportRegistered(mTransport2.name)).isFalse(); } + @Test + public void getTransportAttributes_forRegisteredTransport_returnsCorrectValues() + throws Exception { + TransportManager transportManager = + createTransportManagerAndSetUpTransports( + Collections.singletonList(mTransport1), + mTransport1.name); + + assertThat(transportManager.getTransportConfigurationIntent(mTransport1.name)) + .isEqualTo(mTransport1.binderInterface.configurationIntent()); + assertThat(transportManager.getTransportDataManagementIntent(mTransport1.name)) + .isEqualTo(mTransport1.binderInterface.dataManagementIntent()); + assertThat(transportManager.getTransportDataManagementLabel(mTransport1.name)) + .isEqualTo(mTransport1.binderInterface.dataManagementLabel()); + assertThat(transportManager.getTransportDirName(mTransport1.name)) + .isEqualTo(mTransport1.binderInterface.transportDirName()); + } + + @Test + public void getTransportAttributes_forUnregisteredTransport_throws() + throws Exception { + TransportManager transportManager = + createTransportManagerAndSetUpTransports( + Collections.singletonList(mTransport1), + Collections.singletonList(mTransport2), + mTransport1.name); + + expectThrows( + TransportNotRegisteredException.class, + () -> transportManager.getTransportConfigurationIntent(mTransport2.name)); + expectThrows( + TransportNotRegisteredException.class, + () -> transportManager.getTransportDataManagementIntent( + mTransport2.name)); + expectThrows( + TransportNotRegisteredException.class, + () -> transportManager.getTransportDataManagementLabel(mTransport2.name)); + expectThrows( + TransportNotRegisteredException.class, + () -> transportManager.getTransportDirName(mTransport2.name)); + } + private void setUpPackageWithTransports(String packageName, List<TransportInfo> transports, int flags) throws Exception { PackageInfo packageInfo = new PackageInfo(); @@ -616,10 +697,12 @@ public class TransportManagerTest { assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( availableTransportsNames); for (TransportInfo transport : availableTransports) { - assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isTrue(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface)) + .isTrue(); } for (TransportInfo transport : unavailableTransports) { - assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isFalse(); + assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface)) + .isFalse(); } mTransportBoundListenerStub.resetState(); @@ -627,19 +710,46 @@ public class TransportManagerTest { return transportManager; } + private static <T extends Throwable> void expectThrows( + Class<T> throwableClass, ThrowingRunnable runnable) { + try { + runnable.runOrThrow(); + fail("Expected to throw " + throwableClass.getSimpleName()); + } catch (Throwable t) { + assertThat(t).isInstanceOf(throwableClass); + } + } + private static class TransportInfo { public final String packageName; public final String name; public final ComponentName componentName; - public final BackupTransportStub stub; + public final IBackupTransport binderInterface; public final IBinder binder; - TransportInfo(String packageName, String name) { + TransportInfo( + String packageName, + String name, + @Nullable Intent configurationIntent, + String currentDestinationString, + @Nullable Intent dataManagementIntent, + String dataManagementLabel) { this.packageName = packageName; this.name = name; this.componentName = new ComponentName(packageName, name); - this.stub = new BackupTransportStub(name); this.binder = mock(IBinder.class); + IBackupTransport transport = mock(IBackupTransport.class); + try { + when(transport.name()).thenReturn(name); + when(transport.configurationIntent()).thenReturn(configurationIntent); + when(transport.currentDestinationString()).thenReturn(currentDestinationString); + when(transport.dataManagementIntent()).thenReturn(dataManagementIntent); + when(transport.dataManagementLabel()).thenReturn(dataManagementLabel); + } catch (RemoteException e) { + // Only here to mock methods that throw RemoteException + } + this.binderInterface = transport; } } + } diff --git a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java b/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java deleted file mode 100644 index ec09f908c90d..000000000000 --- a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.testing; - -import android.app.backup.RestoreDescription; -import android.app.backup.RestoreSet; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; - -import com.android.internal.backup.IBackupTransport; - -/** - * Stub backup transport, doing nothing and returning default values. - */ -public class BackupTransportStub implements IBackupTransport { - - private final String mName; - - public BackupTransportStub(String name) { - mName = name; - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public String name() throws RemoteException { - return mName; - } - - @Override - public Intent configurationIntent() throws RemoteException { - return null; - } - - @Override - public String currentDestinationString() throws RemoteException { - return null; - } - - @Override - public Intent dataManagementIntent() throws RemoteException { - return null; - } - - @Override - public String dataManagementLabel() throws RemoteException { - return null; - } - - @Override - public String transportDirName() throws RemoteException { - return null; - } - - @Override - public long requestBackupTime() throws RemoteException { - return 0; - } - - @Override - public int initializeDevice() throws RemoteException { - return 0; - } - - @Override - public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags) - throws RemoteException { - return 0; - } - - @Override - public int clearBackupData(PackageInfo packageInfo) throws RemoteException { - return 0; - } - - @Override - public int finishBackup() throws RemoteException { - return 0; - } - - @Override - public RestoreSet[] getAvailableRestoreSets() throws RemoteException { - return new RestoreSet[0]; - } - - @Override - public long getCurrentRestoreSet() throws RemoteException { - return 0; - } - - @Override - public int startRestore(long token, PackageInfo[] packages) throws RemoteException { - return 0; - } - - @Override - public RestoreDescription nextRestorePackage() throws RemoteException { - return null; - } - - @Override - public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException { - return 0; - } - - @Override - public void finishRestore() throws RemoteException { - - } - - @Override - public long requestFullBackupTime() throws RemoteException { - return 0; - } - - @Override - public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket, - int flags) - throws RemoteException { - return 0; - } - - @Override - public int checkFullBackupSize(long size) throws RemoteException { - return 0; - } - - @Override - public int sendBackupData(int numBytes) throws RemoteException { - return 0; - } - - @Override - public void cancelFullBackup() throws RemoteException { - - } - - @Override - public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup) - throws RemoteException { - return false; - } - - @Override - public long getBackupQuota(String packageName, boolean isFullBackup) - throws RemoteException { - return 0; - } - - @Override - public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException { - return 0; - } - - @Override - public int abortFullRestore() throws RemoteException { - return 0; - } -} diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java index 362856c707c7..f4c54420853c 100644 --- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -117,7 +117,7 @@ public class BackupManagerServiceTest { "dataManagementLabel"); verify(mTransportManager) - .describeTransport( + .updateTransportAttributes( eq(TRANSPORT_COMPONENT), eq(TRANSPORT_NAME), eq(configurationIntent), @@ -247,7 +247,7 @@ public class BackupManagerServiceTest { null); verify(mTransportManager) - .describeTransport( + .updateTransportAttributes( eq(TRANSPORT_COMPONENT), eq(TRANSPORT_NAME), eq(configurationIntent), @@ -274,7 +274,7 @@ public class BackupManagerServiceTest { "dataManagementLabel"); verify(mTransportManager) - .describeTransport( + .updateTransportAttributes( eq(TRANSPORT_COMPONENT), eq(TRANSPORT_NAME), eq(configurationIntent), diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ead8849c363e..dedce1702dfe 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -300,10 +300,10 @@ public class CarrierConfigManager { * 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 @@ public class CarrierConfigManager { * {@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 000000000000..5d16dd5b30ee --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal; + +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; + +import com.android.ims.ImsCallProfile; +import com.android.ims.ImsConferenceState; +import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsStreamMediaProfile; +import com.android.ims.ImsSuppServiceNotification; +import com.android.ims.internal.ImsCallSession; + +/** + * Proxy class for interfacing with the framework's Call session for an ongoing IMS call. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsCallSessionListener maintained by other ImsServices. + * + * @hide + */ +public class ImsCallSessionListener { + + private final IImsCallSessionListener mListener; + + public ImsCallSessionListener(IImsCallSessionListener l) { + mListener = l; + } + + /** + * Called when a request is sent out to initiate a new session + * and 1xx response is received from the network. + */ + public void callSessionProgressing(ImsStreamMediaProfile profile) + throws RemoteException { + mListener.callSessionProgressing(profile); + } + + /** + * Called when the session is initiated. + * + * @param profile the associated {@link ImsCallSession}. + */ + public void callSessionInitiated(ImsCallProfile profile) throws RemoteException { + mListener.callSessionInitiated(profile); + } + + /** + * Called when the session establishment has failed. + * + * @param reasonInfo detailed reason of the session establishment failure + */ + public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionInitiatedFailed(reasonInfo); + } + + /** + * Called when the session is terminated. + * + * @param reasonInfo detailed reason of the session termination + */ + public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionTerminated(reasonInfo); + } + + /** + * Called when the session is on hold. + */ + public void callSessionHeld(ImsCallProfile profile) throws RemoteException { + mListener.callSessionHeld(profile); + } + + /** + * Called when the session hold has failed. + * + * @param reasonInfo detailed reason of the session hold failure + */ + public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHoldFailed(reasonInfo); + } + + /** + * Called when the session hold is received from the remote user. + */ + public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionHoldReceived(profile); + } + + /** + * Called when the session resume is done. + */ + public void callSessionResumed(ImsCallProfile profile) throws RemoteException { + mListener.callSessionResumed(profile); + } + + /** + * Called when the session resume has failed. + * + * @param reasonInfo detailed reason of the session resume failure + */ + public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionResumeFailed(reasonInfo); + } + + /** + * Called when the session resume is received from the remote user. + */ + public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionResumeReceived(profile); + } + + /** + * Called when the session merge has been started. At this point, the {@code newSession} + * represents the session which has been initiated to the IMS conference server for the + * new merged conference. + * + * @param newSession the session object that is merged with an active & hold session + */ + public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile) + throws RemoteException { + mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null, + profile); + } + + /** + * Called when the session merge is successful and the merged session is active. + * + * @param newSession the new session object that is used for the conference + */ + public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException { + mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null); + } + + /** + * Called when the session merge has failed. + * + * @param reasonInfo detailed reason of the call merge failure + */ + public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionMergeFailed(reasonInfo); + } + + /** + * Called when the session is updated (except for hold/unhold). + */ + public void callSessionUpdated(ImsCallProfile profile) throws RemoteException { + mListener.callSessionUpdated(profile); + } + + /** + * Called when the session update has failed. + * + * @param reasonInfo detailed reason of the session update failure + */ + public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionUpdateFailed(reasonInfo); + } + + /** + * Called when the session update is received from the remote user. + */ + public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionUpdateReceived(profile); + } + + /** + * Called when the session has been extended to a conference session. + * + * @param newSession the session object that is extended to the conference + * from the active session + */ + public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile) + throws RemoteException { + mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null, + profile); + } + + /** + * Called when the conference extension has failed. + * + * @param reasonInfo detailed reason of the conference extension failure + */ + public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionConferenceExtendFailed(reasonInfo); + } + + /** + * Called when the conference extension is received from the remote user. + */ + public void callSessionConferenceExtendReceived(ImsCallSession newSession, + ImsCallProfile profile) throws RemoteException { + mListener.callSessionConferenceExtendReceived(newSession != null + ? newSession.getSession() : null, profile); + } + + /** + * Called when the invitation request of the participants is delivered to the conference + * server. + */ + public void callSessionInviteParticipantsRequestDelivered() throws RemoteException { + mListener.callSessionInviteParticipantsRequestDelivered(); + } + + /** + * Called when the invitation request of the participants has failed. + * + * @param reasonInfo detailed reason of the conference invitation failure + */ + public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) + throws RemoteException { + mListener.callSessionInviteParticipantsRequestFailed(reasonInfo); + } + + /** + * Called when the removal request of the participants is delivered to the conference + * server. + */ + public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException { + mListener.callSessionRemoveParticipantsRequestDelivered(); + } + + /** + * Called when the removal request of the participants has failed. + * + * @param reasonInfo detailed reason of the conference removal failure + */ + public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) + throws RemoteException { + mListener.callSessionInviteParticipantsRequestFailed(reasonInfo); + } + + /** + * Notifies the framework of the updated Call session conference state. + * + * @param state the new {@link ImsConferenceState} associated with the conference. + */ + public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException { + mListener.callSessionConferenceStateUpdated(state); + } + + /** + * Notifies the incoming USSD message. + */ + public void callSessionUssdMessageReceived(int mode, String ussdMessage) + throws RemoteException { + mListener.callSessionUssdMessageReceived(mode, ussdMessage); + } + + /** + * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially + * handover from one radio technology to another. + * + * @param srcAccessTech The source radio access technology; one of the access technology + * constants defined in {@link android.telephony.ServiceState}. For + * example + * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}. + * @param targetAccessTech The target radio access technology; one of the access technology + * constants defined in {@link android.telephony.ServiceState}. For + * example + * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}. + */ + public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) + throws RemoteException { + mListener.callSessionMayHandover(srcAccessTech, targetAccessTech); + } + + /** + * Called when session access technology changes. + * + * @param srcAccessTech original access technology + * @param targetAccessTech new access technology + * @param reasonInfo + */ + public void callSessionHandover(int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo); + } + + /** + * Called when session access technology change fails. + * + * @param srcAccessTech original access technology + * @param targetAccessTech new access technology + * @param reasonInfo handover failure reason + */ + public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo); + } + + /** + * Called when the TTY mode is changed by the remote party. + * + * @param mode one of the following: - + * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} + */ + public void callSessionTtyModeReceived(int mode) throws RemoteException { + mListener.callSessionTtyModeReceived(mode); + } + + /** + * Called when the multiparty state is changed for this {@code ImsCallSession}. + * + * @param isMultiParty {@code true} if the session became multiparty, + * {@code false} otherwise. + */ + + public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException { + mListener.callSessionMultipartyStateChanged(isMultiParty); + } + + /** + * Called when the supplementary service information is received for the current session. + */ + public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification) + throws RemoteException { + mListener.callSessionSuppServiceReceived(suppSrvNotification); + } + + /** + * Received RTT modify request from the remote party. + * + * @param callProfile ImsCallProfile with updated attributes + */ + public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) + throws RemoteException { + mListener.callSessionRttModifyRequestReceived(callProfile); + } + + /** + * @param status the received response for RTT modify request. + */ + public void callSessionRttModifyResponseReceived(int status) throws RemoteException { + mListener.callSessionRttModifyResponseReceived(status); + } + + /** + * Device received RTT message from Remote UE. + * + * @param rttMessage RTT message received + */ + public void callSessionRttMessageReceived(String rttMessage) throws RemoteException { + mListener.callSessionRttMessageReceived(rttMessage); + } +} + diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java new file mode 100644 index 000000000000..b7c8ca0f9799 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/ImsService.java @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.CarrierConfigManager; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsRcsFeature; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsServiceController; +import android.telephony.ims.internal.aidl.IImsServiceControllerListener; +import android.telephony.ims.internal.feature.ImsFeature; +import android.telephony.ims.internal.feature.MmTelFeature; +import android.telephony.ims.internal.feature.RcsFeature; +import android.telephony.ims.internal.stub.ImsConfigImplBase; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.util.Log; +import android.util.SparseArray; + +import com.android.ims.internal.IImsFeatureStatusCallback; +import com.android.internal.annotations.VisibleForTesting; + +/** + * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend + * ImsService must register the service in their AndroidManifest to be detected by the framework. + * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE" + * permission. Then, the ImsService definition in the manifest must follow the following format: + * + * ... + * <service android:name=".EgImsService" + * android:permission="android.permission.BIND_IMS_SERVICE" > + * <!-- Apps must declare which features they support as metadata. The different categories are + * defined below. In this example, the RCS_FEATURE feature is supported. --> + * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" /> + * <intent-filter> + * <action android:name="android.telephony.ims.ImsService" /> + * </intent-filter> + * </service> + * ... + * + * The telephony framework will then bind to the ImsService you have defined in your manifest + * if you are either: + * 1) Defined as the default ImsService for the device in the device overlay using + * "config_ims_package". + * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using + * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}. + * + * The features that are currently supported in an ImsService are: + * - RCS_FEATURE: This ImsService implements the RcsFeature class. + * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class. + * @hide + */ +public class ImsService extends Service { + + private static final String LOG_TAG = "ImsService"; + + /** + * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. + * @hide + */ + public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService"; + + // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that + // slot. + // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and + // call ImsFeature#onFeatureRemoved. + private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>(); + + private IImsServiceControllerListener mListener; + + + /** + * Listener that notifies the framework of ImsService changes. + */ + public static class Listener extends IImsServiceControllerListener.Stub { + /** + * The IMS features that this ImsService supports has changed. + * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s + * that this ImsService supports. This may trigger the addition/removal of feature + * in this service. + */ + public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) { + } + } + + /** + * @hide + */ + protected final IBinder mImsServiceController = new IImsServiceController.Stub() { + @Override + public void setListener(IImsServiceControllerListener l) { + mListener = l; + } + + @Override + public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) { + return createMmTelFeatureInternal(slotId, c); + } + + @Override + public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) { + return createRcsFeatureInternal(slotId, c); + } + + @Override + public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) + throws RemoteException { + ImsService.this.removeImsFeature(slotId, featureType, c); + } + + @Override + public ImsFeatureConfiguration querySupportedImsFeatures() { + return ImsService.this.querySupportedImsFeatures(); + } + + @Override + public void notifyImsServiceReadyForFeatureCreation() { + ImsService.this.readyForFeatureCreation(); + } + + @Override + public void notifyImsFeatureReady(int slotId, int featureType) + throws RemoteException { + ImsService.this.notifyImsFeatureReady(slotId, featureType); + } + + @Override + public IImsConfig getConfig(int slotId) throws RemoteException { + ImsConfigImplBase c = ImsService.this.getConfig(slotId); + return c != null ? c.getBinder() : null; + } + + @Override + public IImsRegistration getRegistration(int slotId) throws RemoteException { + ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId); + return r != null ? r.getBinder() : null; + } + }; + + /** + * @hide + */ + @Override + public IBinder onBind(Intent intent) { + if(SERVICE_INTERFACE.equals(intent.getAction())) { + Log.i(LOG_TAG, "ImsService Bound."); + return mImsServiceController; + } + return null; + } + + /** + * @hide + */ + @VisibleForTesting + public SparseArray<ImsFeature> getFeatures(int slotId) { + return mFeaturesBySlot.get(slotId); + } + + private IImsMmTelFeature createMmTelFeatureInternal(int slotId, + IImsFeatureStatusCallback c) { + MmTelFeature f = createMmTelFeature(slotId); + if (f != null) { + setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c); + return f.getBinder(); + } else { + Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); + return null; + } + } + + private IImsRcsFeature createRcsFeatureInternal(int slotId, + IImsFeatureStatusCallback c) { + RcsFeature f = createRcsFeature(slotId); + if (f != null) { + setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c); + return f.getBinder(); + } else { + Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned."); + return null; + } + } + + private void setupFeature(ImsFeature f, int slotId, int featureType, + IImsFeatureStatusCallback c) { + f.addImsFeatureStatusCallback(c); + f.initialize(this, slotId); + addImsFeature(slotId, featureType, f); + } + + private void addImsFeature(int slotId, int featureType, ImsFeature f) { + synchronized (mFeaturesBySlot) { + // Get SparseArray for Features, by querying slot Id + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + // Populate new SparseArray of features if it doesn't exist for this slot yet. + features = new SparseArray<>(); + mFeaturesBySlot.put(slotId, features); + } + features.put(featureType, f); + } + } + + private void removeImsFeature(int slotId, int featureType, + IImsFeatureStatusCallback c) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot " + + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f == null) { + Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type " + + featureType + " exists on slot " + slotId); + return; + } + f.removeImsFeatureStatusCallback(c); + f.onFeatureRemoved(); + features.remove(featureType); + } + } + + private void notifyImsFeatureReady(int slotId, int featureType) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " + + "slot " + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f == null) { + Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type " + + featureType + " exists on slot " + slotId); + return; + } + f.onFeatureReady(); + } + } + + /** + * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently + * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond + * to the {@link ImsFeature.FeatureType}s configured here. + * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports, + * defined in {@link ImsFeature.FeatureType}. + */ + public ImsFeatureConfiguration querySupportedImsFeatures() { + // Return empty for base implementation + return new ImsFeatureConfiguration(); + } + + /** + * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated + * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may + * trigger the framework to add/remove new ImsFeatures, depending on the configuration. + */ + public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) + throws RemoteException { + if (mListener == null) { + throw new IllegalStateException("Framework is not ready"); + } + mListener.onUpdateSupportedImsFeatures(c); + } + + /** + * The ImsService has been bound and is ready for ImsFeature creation based on the Features that + * the ImsService has registered for with the framework, either in the manifest or via + * The ImsService should use this signal instead of onCreate/onBind or similar to perform + * feature initialization because the framework may bind to this service multiple times to + * query the ImsService's {@link ImsFeatureConfiguration} via + * {@link #querySupportedImsFeatures()}before creating features. + */ + public void readyForFeatureCreation() { + } + + /** + * When called, the framework is requesting that a new MmTelFeature is created for the specified + * slot. + * + * @param slotId The slot ID that the MMTel Feature is being created for. + * @return The newly created MmTelFeature associated with the slot or null if the feature is not + * supported. + */ + public MmTelFeature createMmTelFeature(int slotId) { + return null; + } + + /** + * When called, the framework is requesting that a new RcsFeature is created for the specified + * slot + * + * @param slotId The slot ID that the RCS Feature is being created for. + * @return The newly created RcsFeature associated with the slot or null if the feature is not + * supported. + */ + public RcsFeature createRcsFeature(int slotId) { + return null; + } + + /** + * @param slotId The slot that the IMS configuration is associated with. + * @return ImsConfig implementation that is associated with the specified slot. + */ + public ImsConfigImplBase getConfig(int slotId) { + return new ImsConfigImplBase(); + } + + /** + * @param slotId The slot that is associated with the IMS Registration. + * @return the ImsRegistration implementation associated with the slot. + */ + public ImsRegistrationImplBase getRegistration(int slotId) { + return new ImsRegistrationImplBase(); + } +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl new file mode 100644 index 000000000000..2fb67442fa34 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import com.android.ims.ImsStreamMediaProfile; +import com.android.ims.ImsCallProfile; +import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsConferenceState; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.ImsSuppServiceNotification; + +/** + * A listener type for receiving notification on IMS call session events. + * When an event is generated for an {@link IImsCallSession}, the application is notified + * by having one of the methods called on the {@link IImsCallSessionListener}. + * {@hide} + */ +oneway interface IImsCallSessionListener { + /** + * Notifies the result of the basic session operation (setup / terminate). + */ + void callSessionProgressing(in ImsStreamMediaProfile profile); + void callSessionInitiated(in ImsCallProfile profile); + void callSessionInitiatedFailed(in ImsReasonInfo reasonInfo); + void callSessionTerminated(in ImsReasonInfo reasonInfo); + + /** + * Notifies the result of the call hold/resume operation. + */ + void callSessionHeld(in ImsCallProfile profile); + void callSessionHoldFailed(in ImsReasonInfo reasonInfo); + void callSessionHoldReceived(in ImsCallProfile profile); + void callSessionResumed(in ImsCallProfile profile); + void callSessionResumeFailed(in ImsReasonInfo reasonInfo); + void callSessionResumeReceived(in ImsCallProfile profile); + + /** + * Notifies the result of call merge operation. + */ + void callSessionMergeStarted(IImsCallSession newSession, in ImsCallProfile profile); + void callSessionMergeComplete(IImsCallSession session); + void callSessionMergeFailed(in ImsReasonInfo reasonInfo); + + /** + * Notifies the result of call upgrade / downgrade or any other call updates. + */ + void callSessionUpdated(in ImsCallProfile profile); + void callSessionUpdateFailed(in ImsReasonInfo reasonInfo); + void callSessionUpdateReceived(in ImsCallProfile profile); + + /** + * Notifies the result of conference extension. + */ + void callSessionConferenceExtended(IImsCallSession newSession, in ImsCallProfile profile); + void callSessionConferenceExtendFailed(in ImsReasonInfo reasonInfo); + void callSessionConferenceExtendReceived(IImsCallSession newSession, + in ImsCallProfile profile); + + /** + * Notifies the result of the participant invitation / removal to/from the conference session. + */ + void callSessionInviteParticipantsRequestDelivered(); + void callSessionInviteParticipantsRequestFailed(in ImsReasonInfo reasonInfo); + void callSessionRemoveParticipantsRequestDelivered(); + void callSessionRemoveParticipantsRequestFailed(in ImsReasonInfo reasonInfo); + + /** + * Notifies the changes of the conference info. in the conference session. + */ + void callSessionConferenceStateUpdated(in ImsConferenceState state); + + /** + * Notifies the incoming USSD message. + */ + void callSessionUssdMessageReceived(int mode, String ussdMessage); + + /** + * Notifies of handover information for this call + */ + void callSessionHandover(int srcAccessTech, int targetAccessTech, + in ImsReasonInfo reasonInfo); + void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech, + in ImsReasonInfo reasonInfo); + void callSessionMayHandover(int srcAccessTech, int targetAccessTech); + + /** + * Notifies the TTY mode change by remote party. + * @param mode one of the following: + * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} + * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} + */ + void callSessionTtyModeReceived(int mode); + + /** + * Notifies of a change to the multiparty state for this {@code ImsCallSession}. + * + * @param session The call session. + * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise. + */ + void callSessionMultipartyStateChanged(boolean isMultiParty); + + /** + * Notifies the supplementary service information for the current session. + */ + void callSessionSuppServiceReceived(in ImsSuppServiceNotification suppSrvNotification); + + /** + * Device received RTT modify request from Remote UE + * @param session ImsCallProfile with updated attribute + */ + void callSessionRttModifyRequestReceived(in ImsCallProfile callProfile); + + /* Device issued RTT modify request and inturn received response + * from Remote UE + * @param status Will be one of the following values from: + * - {@link Connection.RttModifyStatus} + */ + void callSessionRttModifyResponseReceived(int status); + + /* + * While in call, device received RTT message from Remote UE + * @param rttMessage Received RTT message + */ + void callSessionRttMessageReceived(in String rttMessage); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl new file mode 100644 index 000000000000..fd2eb24610ec --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +/** + * See ImsFeature#CapabilityCallback for more information. + * {@hide} + */ +oneway interface IImsCapabilityCallback { + void onQueryCapabilityConfiguration(int capability, int radioTech, boolean enabled); + void onChangeCapabilityConfigurationError(int capability, int radioTech, int reason); + void onCapabilitiesStatusChanged(int config); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl new file mode 100644 index 000000000000..3d424a33012d --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsConfigCallback; + +import com.android.ims.ImsConfigListener; + +/** + * Provides APIs to get/set the IMS service feature/capability/parameters. + * The config items include items provisioned by the operator. + * + * {@hide} + */ +interface IImsConfig { + + void addImsConfigCallback(IImsConfigCallback c); + void removeImsConfigCallback(IImsConfigCallback c); + int getConfigInt(int item); + String getConfigString(int item); + // Return result code defined in ImsConfig#OperationStatusConstants + int setConfigInt(int item, int value); + // Return result code defined in ImsConfig#OperationStatusConstants + int setConfigString(int item, String value); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl new file mode 100644 index 000000000000..52efd2322c17 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +/** + * Provides callback interface for ImsConfig when a value has changed. + * + * {@hide} + */ +oneway interface IImsConfigCallback { + void onIntConfigChanged(int item, int value); + void onStringConfigChanged(int item, String value); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl new file mode 100644 index 000000000000..712578117e44 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import android.os.Message; +import android.telephony.ims.internal.aidl.IImsMmTelListener; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; +import android.telephony.ims.internal.feature.CapabilityChangeRequest; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsUt; + +/** + * See MmTelFeature for more information. + * {@hide} + */ +interface IImsMmTelFeature { + void setListener(IImsMmTelListener l); + int getFeatureState(); + ImsCallProfile createCallProfile(int callSessionType, int callType); + IImsCallSession createCallSession(in ImsCallProfile profile, IImsCallSessionListener listener); + IImsUt getUtInterface(); + IImsEcbm getEcbmInterface(); + void setUiTtyMode(int uiTtyMode, in Message onCompleteMessage); + IImsMultiEndpoint getMultiEndpointInterface(); + int queryCapabilityStatus(); + oneway void addCapabilityCallback(IImsCapabilityCallback c); + oneway void removeCapabilityCallback(IImsCapabilityCallback c); + oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest request, + IImsCapabilityCallback c); + oneway void queryCapabilityConfiguration(int capability, int radioTech, + IImsCapabilityCallback c); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl new file mode 100644 index 000000000000..8332bc024e37 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import com.android.ims.internal.IImsCallSession; + +/** + * See MmTelFeature#Listener for more information. + * {@hide} + */ +oneway interface IImsMmTelListener { + void onIncomingCall(IImsCallSession c); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl new file mode 100644 index 000000000000..f6005b66bd3c --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +/** + * See RcsFeature for more information. + * {@hide} + */ +interface IImsRcsFeature { + //Empty Default Implementation +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl new file mode 100644 index 000000000000..687b7ca408d5 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsRegistrationCallback; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +/** + * See ImsRegistration for more information. + * + * {@hide} + */ +interface IImsRegistration { + int getRegistrationTechnology(); + oneway void addRegistrationCallback(IImsRegistrationCallback c); + oneway void removeRegistrationCallback(IImsRegistrationCallback c); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl new file mode 100644 index 000000000000..a50575b96865 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +import com.android.ims.ImsReasonInfo; + +/** + * See ImsRegistrationImplBase.Callback for more information. + * + * {@hide} + */ +oneway interface IImsRegistrationCallback { + void onRegistered(int imsRadioTech); + void onRegistering(int imsRadioTech); + void onDeregistered(in ImsReasonInfo info); + void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl new file mode 100644 index 000000000000..8afb95588b01 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsRcsFeature; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsServiceControllerListener; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +import com.android.ims.internal.IImsFeatureStatusCallback; + +/** + * See ImsService and MmTelFeature for more information. + * {@hide} + */ +interface IImsServiceController { + void setListener(IImsServiceControllerListener l); + IImsMmTelFeature createMmTelFeature(int slotId, in IImsFeatureStatusCallback c); + IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c); + ImsFeatureConfiguration querySupportedImsFeatures(); + // Synchronous call to ensure the ImsService is ready before continuing with feature creation. + void notifyImsServiceReadyForFeatureCreation(); + // Synchronous call to ensure the new ImsFeature is ready before using the Feature. + void notifyImsFeatureReady(int slotId, int featureType); + void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c); + IImsConfig getConfig(int slotId); + IImsRegistration getRegistration(int slotId); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl new file mode 100644 index 000000000000..01cca2db0978 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +/** + * See ImsService#Listener for more information. + * {@hide} + */ +oneway interface IImsServiceControllerListener { + void onUpdateSupportedImsFeatures(in ImsFeatureConfiguration c); +} diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl new file mode 100644 index 000000000000..f4ec0eb38f34 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +parcelable CapabilityChangeRequest; diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java new file mode 100644 index 000000000000..4d188734c10e --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.util.ArraySet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Request to send to IMS provider, which will try to enable/disable capabilities that are added to + * the request. + * {@hide} + */ +public class CapabilityChangeRequest implements Parcelable { + + public static class CapabilityPair { + private final int mCapability; + private final int radioTech; + + public CapabilityPair(int capability, int radioTech) { + this.mCapability = capability; + this.radioTech = radioTech; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CapabilityPair)) return false; + + CapabilityPair that = (CapabilityPair) o; + + if (getCapability() != that.getCapability()) return false; + return getRadioTech() == that.getRadioTech(); + } + + @Override + public int hashCode() { + int result = getCapability(); + result = 31 * result + getRadioTech(); + return result; + } + + public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() { + return mCapability; + } + + public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() { + return radioTech; + } + } + + // Pair contains <radio tech, mCapability> + private final Set<CapabilityPair> mCapabilitiesToEnable; + // Pair contains <radio tech, mCapability> + private final Set<CapabilityPair> mCapabilitiesToDisable; + + public CapabilityChangeRequest() { + mCapabilitiesToEnable = new ArraySet<>(); + mCapabilitiesToDisable = new ArraySet<>(); + } + + /** + * Add one or many capabilities to the request to be enabled. + * + * @param capabilities A bitfield of capabilities to enable, valid values are defined in + * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * @param radioTech the radio tech that these capabilities should be enabled for, valid + * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. + */ + public void addCapabilitiesToEnableForTech( + @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech); + } + + /** + * Add one or many capabilities to the request to be disabled. + * @param capabilities A bitfield of capabilities to diable, valid values are defined in + * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * @param radioTech the radio tech that these capabilities should be disabled for, valid + * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. + */ + public void addCapabilitiesToDisableForTech( + @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech); + } + + /** + * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled. + */ + public List<CapabilityPair> getCapabilitiesToEnable() { + return new ArrayList<>(mCapabilitiesToEnable); + } + + /** + * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled. + */ + public List<CapabilityPair> getCapabilitiesToDisable() { + return new ArrayList<>(mCapabilitiesToDisable); + } + + // Iterate through capabilities bitfield and add each one as a pair associated with the radio + // technology + private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) { + long highestCapability = Long.highestOneBit(capabilities); + for (int i = 1; i <= highestCapability; i *= 2) { + if ((i & capabilities) > 0) { + set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech)); + } + } + } + + protected CapabilityChangeRequest(Parcel in) { + int enableSize = in.readInt(); + mCapabilitiesToEnable = new ArraySet<>(enableSize); + for (int i = 0; i < enableSize; i++) { + mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(), + /*radioTech*/ in.readInt())); + } + int disableSize = in.readInt(); + mCapabilitiesToDisable = new ArraySet<>(disableSize); + for (int i = 0; i < disableSize; i++) { + mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(), + /*radioTech*/ in.readInt())); + } + } + + public static final Creator<CapabilityChangeRequest> CREATOR = + new Creator<CapabilityChangeRequest>() { + @Override + public CapabilityChangeRequest createFromParcel(Parcel in) { + return new CapabilityChangeRequest(in); + } + + @Override + public CapabilityChangeRequest[] newArray(int size) { + return new CapabilityChangeRequest[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mCapabilitiesToEnable.size()); + for (CapabilityPair pair : mCapabilitiesToEnable) { + dest.writeInt(pair.getCapability()); + dest.writeInt(pair.getRadioTech()); + } + dest.writeInt(mCapabilitiesToDisable.size()); + for (CapabilityPair pair : mCapabilitiesToDisable) { + dest.writeInt(pair.getCapability()); + dest.writeInt(pair.getRadioTech()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CapabilityChangeRequest)) return false; + + CapabilityChangeRequest that = (CapabilityChangeRequest) o; + + if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false; + return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable); + } + + @Override + public int hashCode() { + int result = mCapabilitiesToEnable.hashCode(); + result = 31 * result + mCapabilitiesToDisable.hashCode(); + return result; + } +} diff --git a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java new file mode 100644 index 000000000000..9f82ad241eaf --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; +import android.os.IInterface; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.SubscriptionManager; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.util.Log; + +import com.android.ims.internal.IImsFeatureStatusCallback; +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * Base class for all IMS features that are supported by the framework. + * + * @hide + */ +public abstract class ImsFeature { + + private static final String LOG_TAG = "ImsFeature"; + + /** + * Action to broadcast when ImsService is up. + * Internal use only. + * Only defined here separately for compatibility purposes with the old ImsService. + * + * @hide + */ + public static final String ACTION_IMS_SERVICE_UP = + "com.android.ims.IMS_SERVICE_UP"; + + /** + * Action to broadcast when ImsService is down. + * Internal use only. + * Only defined here separately for compatibility purposes with the old ImsService. + * + * @hide + */ + public static final String ACTION_IMS_SERVICE_DOWN = + "com.android.ims.IMS_SERVICE_DOWN"; + + /** + * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents. + * A long value; the phone ID corresponding to the IMS service coming up or down. + * Only defined here separately for compatibility purposes with the old ImsService. + * + * @hide + */ + public static final String EXTRA_PHONE_ID = "android:phone_id"; + + // Invalid feature value + public static final int FEATURE_INVALID = -1; + // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously + // defined values in ImsServiceClass for compatibility purposes. + public static final int FEATURE_EMERGENCY_MMTEL = 0; + public static final int FEATURE_MMTEL = 1; + public static final int FEATURE_RCS = 2; + // Total number of features defined + public static final int FEATURE_MAX = 3; + + // Integer values defining IMS features that are supported in ImsFeature. + @IntDef(flag = true, + value = { + FEATURE_EMERGENCY_MMTEL, + FEATURE_MMTEL, + FEATURE_RCS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FeatureType {} + + // Integer values defining the state of the ImsFeature at any time. + @IntDef(flag = true, + value = { + STATE_UNAVAILABLE, + STATE_INITIALIZING, + STATE_READY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsState {} + + public static final int STATE_UNAVAILABLE = 0; + public static final int STATE_INITIALIZING = 1; + public static final int STATE_READY = 2; + + // Integer values defining the result codes that should be returned from + // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability. + @IntDef(flag = true, + value = { + CAPABILITY_ERROR_GENERIC, + CAPABILITY_SUCCESS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsCapabilityError {} + + public static final int CAPABILITY_ERROR_GENERIC = -1; + public static final int CAPABILITY_SUCCESS = 0; + + + /** + * The framework implements this callback in order to register for Feature Capability status + * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability + * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error + * callbacks when the ImsService can not change the capability as requested, via + * {@link #onChangeCapabilityConfigurationError}. + */ + public static class CapabilityCallback extends IImsCapabilityCallback.Stub { + + @Override + public final void onCapabilitiesStatusChanged(int config) throws RemoteException { + onCapabilitiesStatusChanged(new Capabilities(config)); + } + + /** + * Returns the result of a query for the capability configuration of a requested capability. + * + * @param capability The capability that was requested. + * @param radioTech The IMS radio technology associated with the capability. + * @param isEnabled true if the capability is enabled, false otherwise. + */ + @Override + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + + } + + /** + * Called when a change to the capability configuration has returned an error. + * + * @param capability The capability that was requested to be changed. + * @param radioTech The IMS radio technology associated with the capability. + * @param reason error associated with the failure to change configuration. + */ + @Override + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + int reason) { + } + + /** + * The status of the feature's capabilities has changed to either available or unavailable. + * If unavailable, the feature is not able to support the unavailable capability at this + * time. + * + * @param config The new availability of the capabilities. + */ + public void onCapabilitiesStatusChanged(Capabilities config) { + } + } + + /** + * Used by the ImsFeature to call back to the CapabilityCallback that the framework has + * provided. + */ + protected static class CapabilityCallbackProxy { + private final IImsCapabilityCallback mCallback; + + public CapabilityCallbackProxy(IImsCapabilityCallback c) { + mCallback = c; + } + + /** + * This method notifies the provided framework callback that the request to change the + * indicated capability has failed and has not changed. + * + * @param capability The Capability that will be notified to the framework. + * @param radioTech The radio tech that this capability failed for. + * @param reason The reason this capability was unable to be changed. + */ + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + @ImsCapabilityError int reason) { + try { + mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason); + } catch (RemoteException e) { + Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder."); + } + } + + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + try { + mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled); + } catch (RemoteException e) { + Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder."); + } + } + } + + /** + * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask. + */ + public static class Capabilities { + protected int mCapabilities = 0; + + public Capabilities() { + } + + protected Capabilities(int capabilities) { + mCapabilities = capabilities; + } + + /** + * @param capabilities Capabilities to be added to the configuration in the form of a + * bit mask. + */ + public void addCapabilities(int capabilities) { + mCapabilities |= capabilities; + } + + /** + * @param capabilities Capabilities to be removed to the configuration in the form of a + * bit mask. + */ + public void removeCapabilities(int capabilities) { + mCapabilities &= ~capabilities; + } + + /** + * @return true if all of the capabilities specified are capable. + */ + public boolean isCapable(int capabilities) { + return (mCapabilities & capabilities) == capabilities; + } + + public Capabilities copy() { + return new Capabilities(mCapabilities); + } + + /** + * @return a bitmask containing the capability flags directly. + */ + public int getMask() { + return mCapabilities; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Capabilities)) return false; + + Capabilities that = (Capabilities) o; + + return mCapabilities == that.mCapabilities; + } + + @Override + public int hashCode() { + return mCapabilities; + } + } + + private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap( + new WeakHashMap<IImsFeatureStatusCallback, Boolean>()); + private @ImsState int mState = STATE_UNAVAILABLE; + private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; + private Context mContext; + private final Object mLock = new Object(); + private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks + = new RemoteCallbackList<>(); + private Capabilities mCapabilityStatus = new Capabilities(); + + public final void initialize(Context context, int slotId) { + mContext = context; + mSlotId = slotId; + } + + public final int getFeatureState() { + synchronized (mLock) { + return mState; + } + } + + protected final void setFeatureState(@ImsState int state) { + synchronized (mLock) { + if (mState != state) { + mState = state; + notifyFeatureState(state); + } + } + } + + // Not final for testing, but shouldn't be extended! + @VisibleForTesting + public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { + try { + // If we have just connected, send queued status. + c.notifyImsFeatureStatus(getFeatureState()); + // Add the callback if the callback completes successfully without a RemoteException. + synchronized (mLock) { + mStatusCallbacks.add(c); + } + } catch (RemoteException e) { + Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); + } + } + + @VisibleForTesting + // Not final for testing, but should not be extended! + public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { + synchronized (mLock) { + mStatusCallbacks.remove(c); + } + } + + /** + * Internal method called by ImsFeature when setFeatureState has changed. + */ + private void notifyFeatureState(@ImsState int state) { + synchronized (mLock) { + for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator(); + iter.hasNext(); ) { + IImsFeatureStatusCallback callback = iter.next(); + try { + Log.i(LOG_TAG, "notifying ImsFeatureState=" + state); + callback.notifyImsFeatureStatus(state); + } catch (RemoteException e) { + // remove if the callback is no longer alive. + iter.remove(); + Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); + } + } + } + sendImsServiceIntent(state); + } + + /** + * Provide backwards compatibility using deprecated service UP/DOWN intents. + */ + private void sendImsServiceIntent(@ImsState int state) { + if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + return; + } + Intent intent; + switch (state) { + case ImsFeature.STATE_UNAVAILABLE: + case ImsFeature.STATE_INITIALIZING: + intent = new Intent(ACTION_IMS_SERVICE_DOWN); + break; + case ImsFeature.STATE_READY: + intent = new Intent(ACTION_IMS_SERVICE_UP); + break; + default: + intent = new Intent(ACTION_IMS_SERVICE_DOWN); + } + intent.putExtra(EXTRA_PHONE_ID, mSlotId); + mContext.sendBroadcast(intent); + } + + public final void addCapabilityCallback(IImsCapabilityCallback c) { + mCapabilityCallbacks.register(c); + } + + public final void removeCapabilityCallback(IImsCapabilityCallback c) { + mCapabilityCallbacks.unregister(c); + } + + /** + * @return the cached capabilities status for this feature. + */ + @VisibleForTesting + public Capabilities queryCapabilityStatus() { + synchronized (mLock) { + return mCapabilityStatus.copy(); + } + } + + // Called internally to request the change of enabled capabilities. + @VisibleForTesting + public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request, + IImsCapabilityCallback c) throws RemoteException { + if (request == null) { + throw new IllegalArgumentException( + "ImsFeature#requestChangeEnabledCapabilities called with invalid params."); + } + changeEnabledCapabilities(request, new CapabilityCallbackProxy(c)); + } + + /** + * Called by the ImsFeature when the capabilities status has changed. + * + * @param c A {@link Capabilities} containing the new Capabilities status. + */ + protected final void notifyCapabilitiesStatusChanged(Capabilities c) { + synchronized (mLock) { + mCapabilityStatus = c.copy(); + } + int count = mCapabilityCallbacks.beginBroadcast(); + try { + for (int i = 0; i < count; i++) { + try { + mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged( + c.mCapabilities); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " + + "callback."); + } + } + } finally { + mCapabilityCallbacks.finishBroadcast(); + } + } + + /** + * Features should override this method to receive Capability preference change requests from + * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities + * in the {@link CapabilityChangeRequest} are not able to be completed due to an error, + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for + * each failed capability. + * + * @param request A {@link CapabilityChangeRequest} containing requested capabilities to + * enable/disable. + * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework + * setting a subset of these capabilities fail, using + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}. + */ + public abstract void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c); + + /** + * Called when the framework is removing this feature and it needs to be cleaned up. + */ + public abstract void onFeatureRemoved(); + + /** + * Called when the feature has been initialized and communication with the framework is set up. + * Any attempt by this feature to access the framework before this method is called will return + * with an {@link IllegalStateException}. + * The IMS provider should use this method to trigger registration for this feature on the IMS + * network, if needed. + */ + public abstract void onFeatureReady(); + + /** + * @return Binder instance that the framework will use to communicate with this feature. + */ + protected abstract IInterface getBinder(); +} diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java new file mode 100644 index 000000000000..f183a57e77fc --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +import android.annotation.IntDef; +import android.os.Message; +import android.os.RemoteException; +import android.telecom.TelecomManager; +import android.telephony.ims.internal.ImsCallSessionListener; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsMmTelListener; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.ImsEcbmImplBase; +import android.telephony.ims.stub.ImsMultiEndpointImplBase; +import android.telephony.ims.stub.ImsUtImplBase; +import android.util.Log; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsUt; +import com.android.ims.internal.ImsCallSession; +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support. + * + * Any class wishing to use MmTelFeature should extend this class and implement all methods that the + * service supports. + * @hide + */ + +public class MmTelFeature extends ImsFeature { + + private static final String LOG_TAG = "MmTelFeature"; + + private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() { + + @Override + public void setListener(IImsMmTelListener l) throws RemoteException { + synchronized (mLock) { + MmTelFeature.this.setListener(l); + } + } + + @Override + public int getFeatureState() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getFeatureState(); + } + } + + + @Override + public ImsCallProfile createCallProfile(int callSessionType, int callType) + throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.createCallProfile(callSessionType, callType); + } + } + + @Override + public IImsCallSession createCallSession(ImsCallProfile profile, + IImsCallSessionListener listener) throws RemoteException { + synchronized (mLock) { + ImsCallSession s = MmTelFeature.this.createCallSession(profile, + new ImsCallSessionListener(listener)); + return s != null ? s.getSession() : null; + } + } + + @Override + public IImsUt getUtInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getUt(); + } + } + + @Override + public IImsEcbm getEcbmInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getEcbm(); + } + } + + @Override + public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException { + synchronized (mLock) { + MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage); + } + } + + @Override + public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getMultiEndpoint(); + } + } + + @Override + public int queryCapabilityStatus() throws RemoteException { + return MmTelFeature.this.queryCapabilityStatus().mCapabilities; + } + + @Override + public void addCapabilityCallback(IImsCapabilityCallback c) { + MmTelFeature.this.addCapabilityCallback(c); + } + + @Override + public void removeCapabilityCallback(IImsCapabilityCallback c) { + MmTelFeature.this.removeCapabilityCallback(c); + } + + @Override + public void changeCapabilitiesConfiguration(CapabilityChangeRequest request, + IImsCapabilityCallback c) throws RemoteException { + MmTelFeature.this.requestChangeEnabledCapabilities(request, c); + } + + @Override + public void queryCapabilityConfiguration(int capability, int radioTech, + IImsCapabilityCallback c) { + queryCapabilityConfigurationInternal(capability, radioTech, c); + } + }; + + /** + * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask. + * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}. + * + * The capabilities of this MmTelFeature will be set by the framework and can be queried with + * {@link #queryCapabilityStatus()}. + * + * This MmTelFeature can then return the status of each of these capabilities (enabled or not) + * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current + * status can also be queried using {@link #queryCapabilityStatus()}. + */ + public static class MmTelCapabilities extends Capabilities { + + @VisibleForTesting + public MmTelCapabilities() { + super(); + } + + public MmTelCapabilities(Capabilities c) { + mCapabilities = c.mCapabilities; + } + + @IntDef(flag = true, + value = { + CAPABILITY_TYPE_VOICE, + CAPABILITY_TYPE_VIDEO, + CAPABILITY_TYPE_UT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MmTelCapability {} + + /** + * This MmTelFeature supports Voice calling (IR.92) + */ + public static final int CAPABILITY_TYPE_VOICE = 1 << 0; + + /** + * This MmTelFeature supports Video (IR.94) + */ + public static final int CAPABILITY_TYPE_VIDEO = 1 << 1; + + /** + * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92) + */ + public static final int CAPABILITY_TYPE_UT = 1 << 2; + + @Override + public final void addCapabilities(@MmTelCapability int capabilities) { + super.addCapabilities(capabilities); + } + + @Override + public final void removeCapabilities(@MmTelCapability int capability) { + super.removeCapabilities(capability); + } + + @Override + public final boolean isCapable(@MmTelCapability int capabilities) { + return super.isCapable(capabilities); + } + } + + /** + * Listener that the framework implements for communication from the MmTelFeature. + */ + public static class Listener extends IImsMmTelListener.Stub { + + @Override + public final void onIncomingCall(IImsCallSession c) { + onIncomingCall(new ImsCallSession(c)); + } + + /** + * Called when the IMS provider receives an incoming call. + * @param c The {@link ImsCallSession} associated with the new call. + */ + public void onIncomingCall(ImsCallSession c) { + } + } + + // Lock for feature synchronization + private final Object mLock = new Object(); + private IImsMmTelListener mListener; + + /** + * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and + * notifies the framework. + */ + private void setListener(IImsMmTelListener listener) { + synchronized (mLock) { + mListener = listener; + } + } + + private void queryCapabilityConfigurationInternal(int capability, int radioTech, + IImsCapabilityCallback c) { + boolean enabled = queryCapabilityConfiguration(capability, radioTech); + try { + if (c != null) { + c.onQueryCapabilityConfiguration(capability, radioTech, enabled); + } + } catch (RemoteException e) { + Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!"); + } + } + + /** + * The current capability status that this MmTelFeature has defined is available. This + * configuration will be used by the platform to figure out which capabilities are CURRENTLY + * available to be used. + * + * Should be a subset of the capabilities that are enabled by the framework in + * {@link #changeEnabledCapabilities}. + * @return A copy of the current MmTelFeature capability status. + */ + @Override + public final MmTelCapabilities queryCapabilityStatus() { + return new MmTelCapabilities(super.queryCapabilityStatus()); + } + + /** + * Notify the framework that the status of the Capabilities has changed. Even though the + * MmTelFeature capability may be enabled by the framework, the status may be disabled due to + * the feature being unavailable from the network. + * @param c The current capability status of the MmTelFeature. If a capability is disabled, then + * the status of that capability is disabled. This can happen if the network does not currently + * support the capability that is enabled. A capability that is disabled by the framework (via + * {@link #changeEnabledCapabilities}) should also show the status as disabled. + */ + protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) { + super.notifyCapabilitiesStatusChanged(c); + } + + /** + * Notify the framework of an incoming call. + * @param c The {@link ImsCallSession} of the new incoming call. + * + * @throws RemoteException if the connection to the framework is not available. If this happens, + * the call should be no longer considered active and should be cleaned up. + * */ + protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException { + synchronized (mLock) { + if (mListener == null) { + throw new IllegalStateException("Session is not available."); + } + mListener.onIncomingCall(c.getSession()); + } + } + + /** + * Provides the MmTelFeature with the ability to return the framework Capability Configuration + * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and + * includes a capability A to enable or disable, this method should return the correct enabled + * status for capability A. + * @param capability The capability that we are querying the configuration for. + * @return true if the capability is enabled, false otherwise. + */ + public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + // Base implementation - Override to provide functionality + return false; + } + + /** + * The MmTelFeature should override this method to handle the enabling/disabling of + * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes + * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities + * could not be set to their new values, + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called + * individually for each capability whose processing resulted in an error. + * + * Enabling/Disabling a capability here indicates that the capability should be registered or + * deregistered (depending on the capability change) and become available or unavailable to + * the framework. + */ + @Override + public void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c) { + // Base implementation, no-op + } + + /** + * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. + * + * @param callSessionType a service type that is specified in {@link ImsCallProfile} + * {@link ImsCallProfile#SERVICE_TYPE_NONE} + * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} + * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} + * @param callType a call type that is specified in {@link ImsCallProfile} + * {@link ImsCallProfile#CALL_TYPE_VOICE} + * {@link ImsCallProfile#CALL_TYPE_VT} + * {@link ImsCallProfile#CALL_TYPE_VT_TX} + * {@link ImsCallProfile#CALL_TYPE_VT_RX} + * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} + * {@link ImsCallProfile#CALL_TYPE_VS} + * {@link ImsCallProfile#CALL_TYPE_VS_TX} + * {@link ImsCallProfile#CALL_TYPE_VS_RX} + * @return a {@link ImsCallProfile} object + */ + public ImsCallProfile createCallProfile(int callSessionType, int callType) { + // Base Implementation - Should be overridden + return null; + } + + /** + * Creates an {@link ImsCallSession} with the specified call profile. + * Use other methods, if applicable, instead of interacting with + * {@link ImsCallSession} directly. + * + * @param profile a call profile to make the call + * @param listener An implementation of IImsCallSessionListener. + */ + public ImsCallSession createCallSession(ImsCallProfile profile, + ImsCallSessionListener listener) { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Ut interface for the supplementary service configuration. + */ + public ImsUtImplBase getUt() { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Emergency call-back mode interface for emergency VoLTE calls that support it. + */ + public ImsEcbmImplBase getEcbm() { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Emergency call-back mode interface for emergency VoLTE calls that support it. + */ + public ImsMultiEndpointImplBase getMultiEndpoint() { + // Base Implementation - Should be overridden + return null; + } + + /** + * Sets the current UI TTY mode for the MmTelFeature. + * @param mode An integer containing the new UI TTY Mode, can consist of + * {@link TelecomManager#TTY_MODE_OFF}, + * {@link TelecomManager#TTY_MODE_FULL}, + * {@link TelecomManager#TTY_MODE_HCO}, + * {@link TelecomManager#TTY_MODE_VCO} + * @param onCompleteMessage A {@link Message} to be used when the mode has been set. + */ + void setUiTtyMode(int mode, Message onCompleteMessage) { + // Base Implementation - Should be overridden + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureRemoved() { + // Base Implementation - Should be overridden + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureReady() { + // Base Implementation - Should be overridden + } + + /** + * @hide + */ + @Override + public final IImsMmTelFeature getBinder() { + return mImsMMTelBinder; + } +} diff --git a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java new file mode 100644 index 000000000000..8d1bd9d27f7c --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.feature; + +import android.telephony.ims.internal.aidl.IImsRcsFeature; + +/** + * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend + * this class and provide implementations of the RcsFeature methods that they support. + * @hide + */ + +public class RcsFeature extends ImsFeature { + + private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() { + // Empty Default Implementation. + }; + + + public RcsFeature() { + super(); + } + + @Override + public void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c) { + // Do nothing for base implementation. + } + + @Override + public void onFeatureRemoved() { + + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureReady() { + + } + + @Override + public final IImsRcsFeature getBinder() { + return mImsRcsBinder; + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java new file mode 100644 index 000000000000..33aec5dfb8af --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.stub; + +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsConfigCallback; + +import com.android.ims.ImsConfig; + +/** + * Controls the modification of IMS specific configurations. For more information on the supported + * IMS configuration constants, see {@link ImsConfig}. + * + * @hide + */ + +public class ImsConfigImplBase { + + //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config + // work first. + private final IImsConfig mBinder = new IImsConfig.Stub() { + + @Override + public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { + ImsConfigImplBase.this.addImsConfigCallback(c); + } + + @Override + public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException { + ImsConfigImplBase.this.removeImsConfigCallback(c); + } + + @Override + public int getConfigInt(int item) throws RemoteException { + return Integer.MIN_VALUE; + } + + @Override + public String getConfigString(int item) throws RemoteException { + return null; + } + + @Override + public int setConfigInt(int item, int value) throws RemoteException { + return Integer.MIN_VALUE; + } + + @Override + public int setConfigString(int item, String value) throws RemoteException { + return Integer.MIN_VALUE; + } + }; + + public class Callback extends IImsConfigCallback.Stub { + + @Override + public final void onIntConfigChanged(int item, int value) throws RemoteException { + onConfigChanged(item, value); + } + + @Override + public final void onStringConfigChanged(int item, String value) throws RemoteException { + onConfigChanged(item, value); + } + + /** + * Called when the IMS configuration has changed. + * @param item the IMS configuration key constant, as defined in ImsConfig. + * @param value the new integer value of the IMS configuration constant. + */ + public void onConfigChanged(int item, int value) { + // Base Implementation + } + + /** + * Called when the IMS configuration has changed. + * @param item the IMS configuration key constant, as defined in ImsConfig. + * @param value the new String value of the IMS configuration constant. + */ + public void onConfigChanged(int item, String value) { + // Base Implementation + } + } + + private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>(); + + /** + * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration + * changes. + * @param c callback to add. + */ + private void addImsConfigCallback(IImsConfigCallback c) { + mCallbacks.register(c); + } + /** + * Removes a {@link Callback} to the list of callbacks notified when a value in the + * configuration changes. + * + * @param c callback to remove. + */ + private void removeImsConfigCallback(IImsConfigCallback c) { + mCallbacks.unregister(c); + } + + public final IImsConfig getBinder() { + return mBinder; + } + + /** + * Sets the value for IMS service/capabilities parameters by the operator device + * management entity. It sets the config item value in the provisioned storage + * from which the master value is derived. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in Integer format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + public int setConfig(int item, int value) { + // Base Implementation - To be overridden. + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Sets the value for IMS service/capabilities parameters by the operator device + * management entity. It sets the config item value in the provisioned storage + * from which the master value is derived. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in String format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + public int setConfig(int item, String value) { + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Gets the value for ims service/capabilities parameters from the provisioned + * value storage. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in Integer format. + */ + public int getConfigInt(int item) { + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Gets the value for ims service/capabilities parameters from the provisioned + * value storage. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in String format. + */ + public String getConfigString(int item) { + return null; + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl new file mode 100644 index 000000000000..e890cf8756f3 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.stub; + +parcelable ImsFeatureConfiguration; diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java new file mode 100644 index 000000000000..244c9578f6b4 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.stub; + +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.internal.feature.ImsFeature; +import android.util.ArraySet; + +import java.util.Arrays; +import java.util.Set; + +/** + * Container class for IMS Feature configuration. This class contains the features that the + * ImsService supports, which are defined in {@link ImsFeature.FeatureType}. + * @hide + */ +public class ImsFeatureConfiguration implements Parcelable { + /** + * Features that this ImsService supports. + */ + private final Set<Integer> mFeatures; + + /** + * Creates an ImsFeatureConfiguration with the features + */ + public static class Builder { + ImsFeatureConfiguration mConfig; + public Builder() { + mConfig = new ImsFeatureConfiguration(); + } + + /** + * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service + * supports. + * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration. + */ + public Builder addFeature(@ImsFeature.FeatureType int feature) { + mConfig.addFeature(feature); + return this; + } + + public ImsFeatureConfiguration build() { + return mConfig; + } + } + + /** + * Creates with all registration features empty. + * + * Consider using the provided {@link Builder} to create this configuration instead. + */ + public ImsFeatureConfiguration() { + mFeatures = new ArraySet<>(); + } + + /** + * Configuration of the ImsService, which describes which features the ImsService supports + * (for registration). + * @param features an array of feature integers defined in {@link ImsFeature} that describe + * which features this ImsService supports. + */ + public ImsFeatureConfiguration(int[] features) { + mFeatures = new ArraySet<>(); + + if (features != null) { + for (int i : features) { + mFeatures.add(i); + } + } + } + + /** + * @return an int[] containing the features that this ImsService supports. + */ + public int[] getServiceFeatures() { + return mFeatures.stream().mapToInt(i->i).toArray(); + } + + void addFeature(int feature) { + mFeatures.add(feature); + } + + protected ImsFeatureConfiguration(Parcel in) { + int[] features = in.createIntArray(); + if (features != null) { + mFeatures = new ArraySet<>(features.length); + for(Integer i : features) { + mFeatures.add(i); + } + } else { + mFeatures = new ArraySet<>(); + } + } + + public static final Creator<ImsFeatureConfiguration> CREATOR + = new Creator<ImsFeatureConfiguration>() { + @Override + public ImsFeatureConfiguration createFromParcel(Parcel in) { + return new ImsFeatureConfiguration(in); + } + + @Override + public ImsFeatureConfiguration[] newArray(int size) { + return new ImsFeatureConfiguration[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ImsFeatureConfiguration)) return false; + + ImsFeatureConfiguration that = (ImsFeatureConfiguration) o; + + return mFeatures.equals(that.mFeatures); + } + + @Override + public int hashCode() { + return mFeatures.hashCode(); + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java new file mode 100644 index 000000000000..558b009ab4c2 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.internal.stub; + +import android.annotation.IntDef; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsRegistrationCallback; +import android.util.Log; + +import com.android.ims.ImsReasonInfo; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Controls IMS registration for this ImsService and notifies the framework when the IMS + * registration for this ImsService has changed status. + * @hide + */ + +public class ImsRegistrationImplBase { + + private static final String LOG_TAG = "ImsRegistrationImplBase"; + + // Defines the underlying radio technology type that we have registered for IMS over. + @IntDef(flag = true, + value = { + REGISTRATION_TECH_NONE, + REGISTRATION_TECH_LTE, + REGISTRATION_TECH_IWLAN + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsRegistrationTech {} + /** + * No registration technology specified, used when we are not registered. + */ + public static final int REGISTRATION_TECH_NONE = -1; + /** + * IMS is registered to IMS via LTE. + */ + public static final int REGISTRATION_TECH_LTE = 0; + /** + * IMS is registered to IMS via IWLAN. + */ + public static final int REGISTRATION_TECH_IWLAN = 1; + + // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current + // state. + private static final int REGISTRATION_STATE_NOT_REGISTERED = 0; + private static final int REGISTRATION_STATE_REGISTERING = 1; + private static final int REGISTRATION_STATE_REGISTERED = 2; + + + /** + * Callback class for receiving Registration callback events. + */ + public static class Callback extends IImsRegistrationCallback.Stub { + + /** + * Notifies the framework when the IMS Provider is connected to the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + @Override + public void onRegistered(@ImsRegistrationTech int imsRadioTech) { + } + + /** + * Notifies the framework when the IMS Provider is trying to connect the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + @Override + public void onRegistering(@ImsRegistrationTech int imsRadioTech) { + } + + /** + * Notifies the framework when the IMS Provider is disconnected from the IMS network. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + */ + @Override + public void onDeregistered(ImsReasonInfo info) { + } + + /** + * A failure has occurred when trying to handover registration to another technology type, + * defined in {@link ImsRegistrationTech} + * + * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed + * @param info A {@link ImsReasonInfo} that identifies the reason for failure. + */ + @Override + public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, + ImsReasonInfo info) { + } + } + + private final IImsRegistration mBinder = new IImsRegistration.Stub() { + + @Override + public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { + return getConnectionType(); + } + + @Override + public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + ImsRegistrationImplBase.this.addRegistrationCallback(c); + } + + @Override + public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + ImsRegistrationImplBase.this.removeRegistrationCallback(c); + } + }; + + private final RemoteCallbackList<IImsRegistrationCallback> mCallbacks + = new RemoteCallbackList<>(); + private final Object mLock = new Object(); + // Locked on mLock + private @ImsRegistrationTech + int mConnectionType = REGISTRATION_TECH_NONE; + // Locked on mLock + private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED; + // Locked on mLock + private ImsReasonInfo mLastDisconnectCause; + + public final IImsRegistration getBinder() { + return mBinder; + } + + private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + mCallbacks.register(c); + updateNewCallbackWithState(c); + } + + private void removeRegistrationCallback(IImsRegistrationCallback c) { + mCallbacks.unregister(c); + } + + /** + * Notify the framework that the device is connected to the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { + updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED); + mCallbacks.broadcast((c) -> { + try { + c.onRegistered(imsRadioTech); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " + + "callback."); + } + }); + } + + /** + * Notify the framework that the device is trying to connect the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { + updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING); + mCallbacks.broadcast((c) -> { + try { + c.onRegistering(imsRadioTech); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " + + "callback."); + } + }); + } + + /** + * Notify the framework that the device is disconnected from the IMS network. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + */ + public final void onDeregistered(ImsReasonInfo info) { + updateToDisconnectedState(info); + mCallbacks.broadcast((c) -> { + try { + c.onDeregistered(info); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " + + "callback."); + } + }); + } + + public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, + ImsReasonInfo info) { + mCallbacks.broadcast((c) -> { + try { + c.onTechnologyChangeFailed(imsRadioTech, info); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " + + "callback."); + } + }); + } + + private void updateToState(@ImsRegistrationTech int connType, int newState) { + synchronized (mLock) { + mConnectionType = connType; + mRegistrationState = newState; + mLastDisconnectCause = null; + } + } + + private void updateToDisconnectedState(ImsReasonInfo info) { + synchronized (mLock) { + updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED); + if (info != null) { + mLastDisconnectCause = info; + } else { + Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); + mLastDisconnectCause = new ImsReasonInfo(); + } + } + } + + private @ImsRegistrationTech int getConnectionType() { + synchronized (mLock) { + return mConnectionType; + } + } + + /** + * @param c the newly registered callback that will be updated with the current registration + * state. + */ + private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException { + int state; + ImsReasonInfo disconnectInfo; + synchronized (mLock) { + state = mRegistrationState; + disconnectInfo = mLastDisconnectCause; + } + switch (state) { + case REGISTRATION_STATE_NOT_REGISTERED: { + c.onDeregistered(disconnectInfo); + break; + } + case REGISTRATION_STATE_REGISTERING: { + c.onRegistering(getConnectionType()); + break; + } + case REGISTRATION_STATE_REGISTERED: { + c.onRegistered(getConnectionType()); + break; + } + } + } +} diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java index d267ad204f57..0dbc186ef2cf 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 @@ public class SmsCbHeader { @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 a3fd3452aaef..30c9af1de1b0 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -25,6 +25,7 @@ java_library { srcs: ["src/**/*.java"], no_framework_libs: true, + hostdex: true, libs: [ "framework", ], |