summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp11
-rw-r--r--api/current.txt105
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp10
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h2
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp73
-rw-r--r--core/java/android/app/ActivityThread.java178
-rw-r--r--core/java/android/app/Application.java2
-rw-r--r--core/java/android/app/ApplicationPackageManager.java2
-rw-r--r--core/java/android/app/ClientTransactionHandler.java2
-rw-r--r--core/java/android/app/ContextImpl.java210
-rw-r--r--core/java/android/app/Instrumentation.java6
-rw-r--r--core/java/android/app/LauncherActivity.java2
-rw-r--r--core/java/android/app/LoadedApk.java75
-rw-r--r--core/java/android/app/servertransaction/PendingTransactionActions.java2
-rw-r--r--core/java/android/app/slice/SliceManager.java174
-rw-r--r--core/java/android/app/slice/SliceProvider.java24
-rw-r--r--core/java/android/bluetooth/BluetoothHidDevice.java2
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java8
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java2
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceCallback.java2
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java2
-rw-r--r--core/java/android/provider/ContactsContract.java26
-rw-r--r--core/java/android/provider/VoicemailContract.java2
-rw-r--r--core/java/android/service/carrier/CarrierService.java4
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/proto/android/graphics/point.proto26
-rw-r--r--core/proto/android/server/windowmanagerservice.proto2
-rw-r--r--core/res/res/layout/activity_list_item_2.xml1
-rw-r--r--graphics/java/android/graphics/Point.java16
-rw-r--r--services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java210
-rw-r--r--services/backup/java/com/android/server/backup/TransportManager.java87
-rw-r--r--services/backup/java/com/android/server/backup/internal/BackupHandler.java10
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java6
-rw-r--r--services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java35
-rw-r--r--services/core/java/com/android/server/EntropyMixer.java12
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java4
-rw-r--r--services/core/java/com/android/server/location/NanoAppStateManager.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java23
-rw-r--r--services/robotests/src/com/android/server/backup/TransportManagerTest.java180
-rw-r--r--services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java179
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java6
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java10
-rw-r--r--telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java364
-rw-r--r--telephony/java/android/telephony/ims/internal/ImsService.java339
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl141
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl27
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl40
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl28
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl52
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl27
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl25
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl32
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl34
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl44
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl27
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl19
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java197
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/ImsFeature.java462
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java422
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/RcsFeature.java59
-rw-r--r--telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java173
-rw-r--r--telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl19
-rw-r--r--telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java147
-rw-r--r--telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java276
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java11
-rw-r--r--test-base/Android.bp1
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",
],