Merge "Workaround for leaked dim layer."
diff --git a/api/current.txt b/api/current.txt
index 927414bc..ddaf8c1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -83,6 +83,7 @@
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+ field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -21928,6 +21929,8 @@
method public void setLocation(float, float);
method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
+ method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
+ method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
method public void setOrientationHint(int);
@@ -21946,7 +21949,9 @@
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
+ field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322
field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321
+ field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323
field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1
}
@@ -37179,6 +37184,7 @@
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
+ method public void onShowIncomingCallUi();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
@@ -37190,6 +37196,7 @@
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
+ method public final void setAudioRoute(int);
method public final void setCallerDisplayName(java.lang.String, int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -37238,6 +37245,7 @@
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+ field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -37305,7 +37313,9 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -37418,6 +37428,7 @@
field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
+ field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -37599,6 +37610,8 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+ method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
diff --git a/api/system-current.txt b/api/system-current.txt
index 95b85d9..f3a0a34 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -138,6 +138,7 @@
field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+ field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -23485,6 +23486,8 @@
method public void setLocation(float, float);
method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
+ method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
+ method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
method public void setOrientationHint(int);
@@ -23503,7 +23506,9 @@
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
+ field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322
field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321
+ field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323
field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1
}
@@ -40165,6 +40170,7 @@
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
+ method public void onShowIncomingCallUi();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
@@ -40176,6 +40182,7 @@
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
+ method public final void setAudioRoute(int);
method public final void setCallerDisplayName(java.lang.String, int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -40224,6 +40231,7 @@
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+ field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -40291,7 +40299,9 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -40528,6 +40538,7 @@
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
+ field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -40766,6 +40777,8 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+ method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isRinging();
method public boolean isTtySupported();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 5fe855d..f29820f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -83,6 +83,7 @@
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+ field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -22018,6 +22019,8 @@
method public void setLocation(float, float);
method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
+ method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
+ method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
method public void setOrientationHint(int);
@@ -22036,7 +22039,9 @@
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
+ field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322
field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321
+ field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323
field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1
}
@@ -37300,6 +37305,7 @@
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
+ method public void onShowIncomingCallUi();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
@@ -37311,6 +37317,7 @@
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
+ method public final void setAudioRoute(int);
method public final void setCallerDisplayName(java.lang.String, int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -37359,6 +37366,7 @@
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+ field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -37426,7 +37434,9 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -37539,6 +37549,7 @@
field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
+ field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -37720,6 +37731,8 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+ method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0b3ae3a..74614cc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -245,7 +245,7 @@
boolean mSomeActivitiesChanged = false;
boolean mUpdatingSystemConfig = false;
- // These can be accessed by multiple threads; mPackages is the lock.
+ // These can be accessed by multiple threads; mResourcesManager is the lock.
// XXX For now we keep around information about all packages we have
// seen, not removing entries from this map.
// NOTE: The activity and window managers need to call in to
@@ -254,12 +254,13 @@
// holds their own lock. Thus you MUST NEVER call back into the activity manager
// or window manager or anything that depends on them while holding this lock.
// These LoadedApk are only valid for the userId that we're running as.
- final ArrayMap<String, WeakReference<LoadedApk>> mPackages
- = new ArrayMap<String, WeakReference<LoadedApk>>();
- final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages
- = new ArrayMap<String, WeakReference<LoadedApk>>();
- final ArrayList<ActivityClientRecord> mRelaunchingActivities
- = new ArrayList<ActivityClientRecord>();
+ @GuardedBy("mResourcesManager")
+ final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
+ @GuardedBy("mResourcesManager")
+ final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
+ @GuardedBy("mResourcesManager")
+ final ArrayList<ActivityClientRecord> mRelaunchingActivities = new ArrayList<>();
+ @GuardedBy("mResourcesManager")
Configuration mPendingConfiguration = null;
// Because we merge activity relaunch operations we can't depend on the ordering provided by
// the handler messages. We need to introduce secondary ordering mechanism, which will allow
@@ -904,6 +905,10 @@
sendMessage(H.CONFIGURATION_CHANGED, config);
}
+ public void scheduleApplicationInfoChanged(ApplicationInfo ai) {
+ sendMessage(H.APPLICATION_INFO_CHANGED, ai);
+ }
+
public void updateTimeZone() {
TimeZone.setDefault(null);
}
@@ -1448,6 +1453,7 @@
public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
public static final int ATTACH_AGENT = 155;
+ public static final int APPLICATION_INFO_CHANGED = 156;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
@@ -1505,6 +1511,7 @@
case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
case ATTACH_AGENT: return "ATTACH_AGENT";
+ case APPLICATION_INFO_CHANGED: return "APPLICATION_INFO_CHANGED";
}
}
return Integer.toString(code);
@@ -1636,8 +1643,11 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
mUpdatingSystemConfig = true;
- handleConfigurationChanged((Configuration)msg.obj, null);
- mUpdatingSystemConfig = false;
+ try {
+ handleConfigurationChanged((Configuration) msg.obj, null);
+ } finally {
+ mUpdatingSystemConfig = false;
+ }
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CLEAN_UP_CONTEXT:
@@ -1763,6 +1773,14 @@
case ATTACH_AGENT:
handleAttachAgent((String) msg.obj);
break;
+ case APPLICATION_INFO_CHANGED:
+ mUpdatingSystemConfig = true;
+ try {
+ handleApplicationInfoChanged((ApplicationInfo) msg.obj);
+ } finally {
+ mUpdatingSystemConfig = false;
+ }
+ break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
@@ -3500,15 +3518,17 @@
}
r.activity.performResume();
- // If there is a pending local relaunch that was requested when the activity was
- // paused, it will put the activity into paused state when it finally happens.
- // Since the activity resumed before being relaunched, we don't want that to happen,
- // so we need to clear the request to relaunch paused.
- for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
- final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
- if (relaunching.token == r.token
- && relaunching.onlyLocalRequest && relaunching.startsNotResumed) {
- relaunching.startsNotResumed = false;
+ synchronized (mResourcesManager) {
+ // If there is a pending local relaunch that was requested when the activity was
+ // paused, it will put the activity into paused state when it finally happens.
+ // Since the activity resumed before being relaunched, we don't want that to
+ // happen, so we need to clear the request to relaunch paused.
+ for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
+ final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
+ if (relaunching.token == r.token
+ && relaunching.onlyLocalRequest && relaunching.startsNotResumed) {
+ relaunching.startsNotResumed = false;
+ }
}
}
@@ -4898,6 +4918,44 @@
}
}
+ void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
+ synchronized (mResourcesManager) {
+ // Update all affected loaded packages with new package information
+ WeakReference<LoadedApk> ref = mPackages.get(ai.packageName);
+ LoadedApk apk = ref != null ? ref.get() : null;
+ if (apk != null) {
+ apk.updateApplicationInfo(ai, null);
+ }
+
+ ref = mResourcePackages.get(ai.packageName);
+ apk = ref != null ? ref.get() : null;
+ if (apk != null) {
+ apk.updateApplicationInfo(ai, null);
+ }
+
+ // Update all affected Resources objects to use new ResourcesImpl
+ mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs);
+ }
+
+ ApplicationPackageManager.configurationChanged();
+
+ // Trigger a regular Configuration change event, only with a different assetsSeq number
+ // so that we actually call through to all components.
+ Configuration newConfig = new Configuration();
+ newConfig.unset();
+ newConfig.assetsSeq = mConfiguration.assetsSeq + 1;
+ handleConfigurationChanged(newConfig, null);
+
+ // Schedule all activities to reload
+ for (final Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
+ final Activity activity = entry.getValue().activity;
+ if (!activity.mFinished) {
+ requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
+ false);
+ }
+ }
+ }
+
static void freeTextLayoutCachesIfNeeded(int configDiff) {
if (configDiff != 0) {
// Ask text layout engine to free its caches if there is a locale change
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 5824c32..e143255 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -601,6 +601,8 @@
*/
ActivityManager.TaskSnapshot getTaskSnapshot(int taskId);
+ void scheduleApplicationInfoChanged(in List<String> packageNames, int userId);
+
// WARNING: when these transactions are updated, check if they are any callers on the native
// side. If so, make sure they are using the correct transaction ids and arguments.
// If a transaction which will also be used on the native side is being inserted, add it
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 7f168c9..41d1255 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -152,4 +152,5 @@
IVoiceInteractor voiceInteractor);
void handleTrustStorageUpdate();
void attachAgent(String path);
+ void scheduleApplicationInfoChanged(in ApplicationInfo ai);
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 45831a3..94a8990 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -916,44 +916,85 @@
}
}
- // Bail early if there is no work to do.
- if (updatedResourceKeys.isEmpty()) {
- return;
+ redirectResourcesToNewImplLocked(updatedResourceKeys);
+ }
+ }
+
+ // TODO(adamlesinski): Make this accept more than just overlay directories.
+ final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
+ @NonNull final String[] newResourceDirs) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyNewResourceDirsLocked");
+
+ final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+ final int implCount = mResourceImpls.size();
+ for (int i = 0; i < implCount; i++) {
+ final ResourcesKey key = mResourceImpls.keyAt(i);
+ final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ if (impl != null && key.mResDir != null && key.mResDir.equals(baseCodePath)) {
+ updatedResourceKeys.put(impl, new ResourcesKey(
+ key.mResDir,
+ key.mSplitResDirs,
+ newResourceDirs,
+ key.mLibDirs,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo));
+ }
}
- // Update any references to ResourcesImpl that require reloading.
- final int resourcesCount = mResourceReferences.size();
- for (int i = 0; i < resourcesCount; i++) {
- final Resources r = mResourceReferences.get(i).get();
+ invalidatePath("/");
+
+ redirectResourcesToNewImplLocked(updatedResourceKeys);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
+ }
+
+ private void redirectResourcesToNewImplLocked(
+ @NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) {
+ // Bail early if there is no work to do.
+ if (updatedResourceKeys.isEmpty()) {
+ return;
+ }
+
+ // Update any references to ResourcesImpl that require reloading.
+ final int resourcesCount = mResourceReferences.size();
+ for (int i = 0; i < resourcesCount; i++) {
+ final WeakReference<Resources> ref = mResourceReferences.get(i);
+ final Resources r = ref != null ? ref.get() : null;
+ if (r != null) {
+ final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+ if (key != null) {
+ final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+ if (impl == null) {
+ throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
+ }
+ r.setImpl(impl);
+ }
+ }
+ }
+
+ // Update any references to ResourcesImpl that require reloading for each Activity.
+ for (ActivityResources activityResources : mActivityResourceReferences.values()) {
+ final int resCount = activityResources.activityResources.size();
+ for (int i = 0; i < resCount; i++) {
+ final WeakReference<Resources> ref = activityResources.activityResources.get(i);
+ final Resources r = ref != null ? ref.get() : null;
if (r != null) {
final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
if (key != null) {
final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
if (impl == null) {
- throw new Resources.NotFoundException("failed to load " + libAsset);
+ throw new Resources.NotFoundException(
+ "failed to redirect ResourcesImpl");
}
r.setImpl(impl);
}
}
}
-
- // Update any references to ResourcesImpl that require reloading for each Activity.
- for (ActivityResources activityResources : mActivityResourceReferences.values()) {
- final int resCount = activityResources.activityResources.size();
- for (int i = 0; i < resCount; i++) {
- final Resources r = activityResources.activityResources.get(i).get();
- if (r != null) {
- final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
- if (key != null) {
- final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
- if (impl == null) {
- throw new Resources.NotFoundException("failed to load " + libAsset);
- }
- r.setImpl(impl);
- }
- }
- }
- }
}
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 44dff00..298dc4e 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -671,6 +671,14 @@
public static final int CONFIG_LAYOUT_DIRECTION = 0x2000;
/**
* Bit in {@link #configChanges} that indicates that the activity
+ * can itself handle asset path changes. Set from the {@link android.R.attr#configChanges}
+ * attribute. This is not a core resource configuration, but a higher-level value, so its
+ * constant starts at the high bits.
+ * @hide We do not want apps handling this yet, but we do need some kind of bit for diffs.
+ */
+ public static final int CONFIG_ASSETS_PATHS = 0x80000000;
+ /**
+ * Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the font scaling factor. Set from the
* {@link android.R.attr#configChanges} attribute. This is
* not a core resource configuration, but a higher-level value, so its
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 48860f7..65f4957 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -346,6 +346,9 @@
if ((diff & ActivityInfo.CONFIG_FONT_SCALE) != 0) {
list.add("CONFIG_FONT_SCALE");
}
+ if ((diff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
+ list.add("CONFIG_ASSETS_PATHS");
+ }
StringBuilder builder = new StringBuilder("{");
for (int i = 0, n = list.size(); i < n; i++) {
builder.append(list.get(i));
@@ -671,6 +674,21 @@
public int compatSmallestScreenWidthDp;
/**
+ * An undefined assetsSeq. This will not override an existing assetsSeq.
+ * @hide
+ */
+ public static final int ASSETS_SEQ_UNDEFINED = 0;
+
+ /**
+ * Internal counter that allows us to piggyback off the configuration change mechanism to
+ * signal to apps that the the assets for an Application have changed. A difference in these
+ * between two Configurations will yield a diff flag of
+ * {@link ActivityInfo#CONFIG_ASSETS_PATHS}.
+ * @hide
+ */
+ public int assetsSeq;
+
+ /**
* @hide Internal book-keeping.
*/
public int seq;
@@ -795,6 +813,7 @@
compatScreenWidthDp = o.compatScreenWidthDp;
compatScreenHeightDp = o.compatScreenHeightDp;
compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
+ assetsSeq = o.assetsSeq;
seq = o.seq;
}
@@ -930,9 +949,11 @@
case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
default: sb.append("/"); sb.append(navigationHidden); break;
}
+ if (assetsSeq != 0) {
+ sb.append(" as.").append(assetsSeq);
+ }
if (seq != 0) {
- sb.append(" s.");
- sb.append(seq);
+ sb.append(" s.").append(seq);
}
sb.append('}');
return sb.toString();
@@ -960,6 +981,7 @@
screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
densityDpi = DENSITY_DPI_UNDEFINED;
+ assetsSeq = ASSETS_SEQ_UNDEFINED;
seq = 0;
}
@@ -1130,6 +1152,10 @@
if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
}
+ if (delta.assetsSeq != ASSETS_SEQ_UNDEFINED) {
+ changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
+ assetsSeq = delta.assetsSeq;
+ }
if (delta.seq != 0) {
seq = delta.seq;
}
@@ -1254,6 +1280,10 @@
&& densityDpi != delta.densityDpi) {
changed |= ActivityInfo.CONFIG_DENSITY;
}
+ if ((compareUndefined || delta.assetsSeq != ASSETS_SEQ_UNDEFINED)
+ && assetsSeq != delta.assetsSeq) {
+ changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
+ }
return changed;
}
@@ -1272,7 +1302,11 @@
*/
public static boolean needNewResources(@Config int configChanges,
@Config int interestingChanges) {
- return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
+ // CONFIG_ASSETS_PATHS and CONFIG_FONT_SCALE are higher level configuration changes that
+ // all resources are subject to change with.
+ interestingChanges = interestingChanges | ActivityInfo.CONFIG_ASSETS_PATHS
+ | ActivityInfo.CONFIG_FONT_SCALE;
+ return (configChanges & interestingChanges) != 0;
}
/**
@@ -1345,6 +1379,7 @@
dest.writeInt(compatScreenWidthDp);
dest.writeInt(compatScreenHeightDp);
dest.writeInt(compatSmallestScreenWidthDp);
+ dest.writeInt(assetsSeq);
dest.writeInt(seq);
}
@@ -1378,6 +1413,7 @@
compatScreenWidthDp = source.readInt();
compatScreenHeightDp = source.readInt();
compatSmallestScreenWidthDp = source.readInt();
+ assetsSeq = source.readInt();
seq = source.readInt();
}
@@ -1461,6 +1497,8 @@
n = this.smallestScreenWidthDp - that.smallestScreenWidthDp;
if (n != 0) return n;
n = this.densityDpi - that.densityDpi;
+ if (n != 0) return n;
+ n = this.assetsSeq - that.assetsSeq;
//if (n != 0) return n;
return n;
}
@@ -1498,6 +1536,7 @@
result = 31 * result + screenHeightDp;
result = 31 * result + smallestScreenWidthDp;
result = 31 * result + densityDpi;
+ result = 31 * result + assetsSeq;
return result;
}
@@ -1979,6 +2018,10 @@
if (base.densityDpi != change.densityDpi) {
delta.densityDpi = change.densityDpi;
}
+
+ if (base.assetsSeq != change.assetsSeq) {
+ delta.assetsSeq = change.assetsSeq;
+ }
return delta;
}
@@ -2046,6 +2089,8 @@
SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
DENSITY_DPI_UNDEFINED);
+
+ // For persistence, we don't care about assetsSeq, so do not read it out.
}
@@ -2111,5 +2156,7 @@
if (config.densityDpi != DENSITY_DPI_UNDEFINED) {
XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
}
+
+ // For persistence, we do not care about assetsSeq, so do not write it out.
}
}
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index 23d5af5..9e4dd87 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -226,16 +226,6 @@
return false;
}
- /** Determine whether the application with the given UID is the enabled scorer. */
- @Deprecated // Use NetworkScoreManager.isCallerActiveScorer()
- public boolean isCallerActiveScorer(int callingUid) {
- NetworkScorerAppData defaultApp = getActiveScorer();
- if (defaultApp == null) {
- return false;
- }
- return callingUid == defaultApp.packageUid;
- }
-
private boolean isNetworkRecommendationsDisabled() {
final ContentResolver cr = mContext.getContentResolver();
// A value of 1 indicates enabled.
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index d9ab47e..096fcb8 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -343,53 +343,42 @@
class LogisticRegressionAppRanker {
private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
private static final String BIAS_PREF_KEY = "bias";
- private static final float LEARNING_RATE = 0.02f;
- private static final float REGULARIZER_PARAM = 0.1f;
+ private static final String VERSION_PREF_KEY = "version";
+
+ // parameters for a pre-trained model, to initialize the app ranker. When updating the
+ // pre-trained model, please update these params, as well as initModel().
+ private static final int CURRENT_VERSION = 1;
+ private static final float LEARNING_RATE = 0.0001f;
+ private static final float REGULARIZER_PARAM = 0.0001f;
+
private SharedPreferences mParamSharedPref;
private ArrayMap<String, Float> mFeatureWeights;
private float mBias;
public LogisticRegressionAppRanker(Context context) {
mParamSharedPref = getParamSharedPref(context);
+ initModel();
}
public float predict(ArrayMap<String, Float> target) {
- if (target == null || mParamSharedPref == null) {
+ if (target == null) {
return 0.0f;
}
final int featureSize = target.size();
- if (featureSize == 0) {
- return 0.0f;
- }
float sum = 0.0f;
- if (mFeatureWeights == null) {
- mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
- mFeatureWeights = new ArrayMap<>(featureSize);
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float weight = mParamSharedPref.getFloat(featureName, 0.0f);
- sum += weight * target.valueAt(i);
- mFeatureWeights.put(featureName, weight);
- }
- } else {
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- sum += weight * target.valueAt(i);
- }
+ for (int i = 0; i < featureSize; i++) {
+ String featureName = target.keyAt(i);
+ float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+ sum += weight * target.valueAt(i);
}
return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
}
public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
- if (target == null || target.size() == 0) {
+ if (target == null) {
return;
}
final int featureSize = target.size();
- if (mFeatureWeights == null) {
- mBias = 0.0f;
- mFeatureWeights = new ArrayMap<>(featureSize);
- }
float error = isSelected ? 1.0f - predict : -predict;
for (int i = 0; i < featureSize; i++) {
String featureName = target.keyAt(i);
@@ -405,15 +394,13 @@
}
public void commitUpdate() {
- if (mFeatureWeights == null || mFeatureWeights.size() == 0) {
- return;
- }
SharedPreferences.Editor editor = mParamSharedPref.edit();
editor.putFloat(BIAS_PREF_KEY, mBias);
final int size = mFeatureWeights.size();
for (int i = 0; i < size; i++) {
editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
}
+ editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
editor.apply();
}
@@ -431,5 +418,27 @@
PARAM_SHARED_PREF_NAME + ".xml");
return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
}
+
+ private void initModel() {
+ mFeatureWeights = new ArrayMap<>(4);
+ if (mParamSharedPref == null ||
+ mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
+ // Initializing the app ranker to a pre-trained model. When updating the pre-trained
+ // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
+ // REGULARIZER_PARAM.
+ mBias = -1.6568f;
+ mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
+ mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
+ mFeatureWeights.put(RECENCY_SCORE, 0.269f);
+ mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
+ } else {
+ mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
+ mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
+ mFeatureWeights.put(
+ TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
+ mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
+ mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
+ }
+ }
}
}
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 4dd3360..6d13743 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -128,11 +128,14 @@
/**
* Applies the offset to the {@param stackBounds} to adjust it to a minimized state.
*/
- public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize) {
+ public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize,
+ Rect stableInsets) {
if (stackBounds.left <= movementBounds.centerX()) {
- stackBounds.offsetTo(-stackBounds.width() + mMinimizedVisibleSize, stackBounds.top);
+ stackBounds.offsetTo(stableInsets.left + mMinimizedVisibleSize - stackBounds.width(),
+ stackBounds.top);
} else {
- stackBounds.offsetTo(displaySize.x - mMinimizedVisibleSize, stackBounds.top);
+ stackBounds.offsetTo(displaySize.x - stableInsets.right - mMinimizedVisibleSize,
+ stackBounds.top);
}
}
diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto
new file mode 100644
index 0000000..4d86526
--- /dev/null
+++ b/core/proto/android/service/diskstats.proto
@@ -0,0 +1,94 @@
+/*
+ * 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 = "proto3";
+
+package android.service.diskstats;
+
+option java_multiple_files = true;
+option java_outer_classname = "DiskStatsServiceProto";
+
+message DiskStatsServiceDumpProto {
+ enum EncryptionType {
+ // Unknown encryption type
+ ENCRYPTION_UNKNOWN = 0;
+ // No encryption
+ ENCRYPTION_NONE = 1;
+ // Full disk encryption
+ ENCRYPTION_FULL_DISK = 2;
+ // File-based encryption
+ ENCRYPTION_FILE_BASED = 3;
+ }
+ // Whether the latency test resulted in an error
+ bool has_test_error = 1;
+ // If the test errored, error message is contained here
+ string error_message = 2;
+ // 512B write latency in milliseconds, if the test was successful
+ int32 write_512b_latency_millis = 3;
+ // Free Space in the major partitions
+ repeated DiskStatsFreeSpaceProto partitions_free_space = 4;
+ // Is the device using file-based encryption, full disk encryption or other
+ EncryptionType encryption = 5;
+ // Cached values of folder sizes, etc.
+ DiskStatsCachedValuesProto cached_folder_sizes = 6;
+}
+
+message DiskStatsCachedValuesProto {
+ // Total app data size, in kilobytes
+ int64 agg_apps_size = 1;
+ // Total app cache size, in kilobytes
+ int64 agg_apps_cache_size = 2;
+ // Size of image files, in kilobytes
+ int64 photos_size = 3;
+ // Size of video files, in kilobytes
+ int64 videos_size = 4;
+ // Size of audio files, in kilobytes
+ int64 audio_size = 5;
+ // Size of downloads, in kilobytes
+ int64 downloads_size = 6;
+ // Size of system directory, in kilobytes
+ int64 system_size = 7;
+ // Size of other files, in kilobytes
+ int64 other_size = 8;
+ // Sizes of individual packages
+ repeated DiskStatsAppSizesProto app_sizes = 9;
+}
+
+message DiskStatsAppSizesProto {
+ // Name of the package
+ string package_name = 1;
+ // App's data size in kilobytes
+ int64 app_size = 2;
+ // App's cache size in kilobytes
+ int64 cache_size = 3;
+}
+
+message DiskStatsFreeSpaceProto {
+ enum Folder {
+ // Data folder
+ FOLDER_DATA = 0;
+ // Cache folder
+ FOLDER_CACHE = 1;
+ // System folder
+ FOLDER_SYSTEM = 2;
+ }
+ // Which folder?
+ Folder folder = 1;
+ // Available space, in kilobytes
+ int64 available_space = 2;
+ // Total space, in kilobytes
+ int64 total_space = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 84b03d2..7f25cf3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -801,6 +801,16 @@
android:description="@string/permdesc_callPhone"
android:protectionLevel="dangerous" />
+ <!-- Allows an application to manage its own calls, but rely on the system to route focus to the
+ currently active call.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.MANAGE_OWN_CALLS"
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_manageOwnCalls"
+ android:description="@string/permdesc_manageOwnCalls"
+ android:protectionLevel="dangerous" />
+
<!-- Allows an application to access the IMS call service: making and
modifying a call
<p>Protection level: signature|privileged
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0204e93..d252f23 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1019,6 +1019,14 @@
phone number and device IDs, whether a call is active, and the remote number
connected by a call.</string>
+ <!-- Title of an application permission. When granted the user is giving access to a third
+ party app to route its calls through the system. -->
+ <string name="permlab_manageOwnCalls">route calls through the system</string>
+ <!-- Description of an application permission. When granted the user is giving access to a
+ third party app to route its calls through the system. -->
+ <string name="permdesc_manageOwnCalls">Allows the app to route its calls through the system in
+ order to improve the calling experience.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readPhoneNumber">read phone number</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 29020ba..5bfff26 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -169,25 +169,6 @@
assertNull(activeScorer);
}
- public void testIsCallerActiveScorer_providerNotAvailable() throws Exception {
- ContentResolver cr = mTargetContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
-
- assertFalse(mNetworkScorerAppManager.isCallerActiveScorer(924));
- }
-
- public void testIsCallerActiveScorer_providerAvailable() throws Exception {
- setNetworkRecommendationPackageNames("package1");
- mockScoreNetworksGranted("package1");
- mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
-
- ContentResolver cr = mTargetContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
-
- assertTrue(mNetworkScorerAppManager.isCallerActiveScorer(924));
- assertFalse(mNetworkScorerAppManager.isCallerActiveScorer(925));
- }
-
private void setNetworkRecommendationPackageNames(String... names) {
if (names == null) {
names = new String[0];
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 64576ec..e62dfaa 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -801,6 +801,28 @@
}
/**
+ * Sets the next output file descriptor to be used when the maximum filesize is reached
+ * on the prior output {@link #setOutputFile} or {@link #setNextOutputFile}). File descriptor
+ * must be seekable and in read-write mode. After setting the next output file, application
+ * should not use the file referenced by this file descriptor until {@link #stop}. Application
+ * must call this after receiving on the {@link android.media.MediaRecorder.OnInfoListener} a
+ * "what" code of {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING} and before receiving
+ * a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}. The file is not used
+ * until switching to that output. Application will receive
+ * {@link #MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED} when the next output file is used.
+ * Application will not be able to set a new output file if the previous one has not been used.
+ * Application is responsible for cleaning up unused files after {@link #stop} is called.
+ *
+ * @param fd an open file descriptor to be written into.
+ * @throws IllegalStateException if it is called before prepare().
+ * @throws IOException if setNextOutputFile fails otherwise.
+ */
+ public void setNextOutputFile(FileDescriptor fd) throws IllegalStateException, IOException
+ {
+ _setNextOutputFile(fd);
+ }
+
+ /**
* Sets the path of the output file to be produced. Call this after
* setOutputFormat() but before prepare().
*
@@ -814,9 +836,38 @@
mPath = path;
}
+ /**
+ * Sets the next output file path to be used when the maximum filesize is reached
+ * on the prior output {@link #setOutputFile} or {@link #setNextOutputFile}). File should
+ * be seekable. After setting the next output file, application should not use the file
+ * referenced by this file descriptor until {@link #stop}. Application must call this
+ * after receiving on the {@link android.media.MediaRecorder.OnInfoListener} a "what" code of
+ * {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING} and before receiving a "what" code of
+ * {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}. The file is not used until switching to
+ * that output. Application will receive {@link #MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED}
+ * when the next output file is used. Application will not be able to set a new output file if
+ * the previous one has not been used. Application is responsible for cleaning up unused files
+ * after {@link #stop} is called.
+ *
+ * @param path The pathname to use.
+ * @throws IllegalStateException if it is called before prepare().
+ * @throws IOException if setNextOutputFile fails otherwise.
+ */
+ public void setNextOutputFile(String path) throws IllegalStateException, IOException
+ {
+ if (path != null) {
+ RandomAccessFile file = new RandomAccessFile(path, "rws");
+ try {
+ _setNextOutputFile(file.getFD());
+ } finally {
+ file.close();
+ }
+ }
+ }
+
// native implementation
- private native void _setOutputFile(FileDescriptor fd, long offset, long length)
- throws IllegalStateException, IOException;
+ private native void _setOutputFile(FileDescriptor fd) throws IllegalStateException, IOException;
+ private native void _setNextOutputFile(FileDescriptor fd) throws IllegalStateException, IOException;
private native void _prepare() throws IllegalStateException, IOException;
/**
@@ -833,12 +884,12 @@
if (mPath != null) {
RandomAccessFile file = new RandomAccessFile(mPath, "rws");
try {
- _setOutputFile(file.getFD(), 0, 0);
+ _setOutputFile(file.getFD());
} finally {
file.close();
}
} else if (mFd != null) {
- _setOutputFile(mFd, 0, 0);
+ _setOutputFile(mFd);
} else {
throw new IOException("No valid output file");
}
@@ -980,9 +1031,26 @@
*/
public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800;
/** A maximum filesize had been setup and has now been reached.
+ * Note: This event will not be sent if application already set
+ * next output file through {@link #setNextOutputFile}.
* @see android.media.MediaRecorder.OnInfoListener
*/
public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801;
+ /** A maximum filesize had been setup and current recorded file size
+ * has reached 90% of the limit. This is sent once per file upon
+ * reaching/passing the 90% limit. To continue the recording, applicaiton
+ * should use {@link #setNextOutputFile} to set the next output file.
+ * Otherwise, recording will stop when reaching maximum file size.
+ * @see android.media.MediaRecorder.OnInfoListener
+ */
+ public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802;
+ /** A maximum filesize had been reached and MediaRecorder has switched
+ * output to a new file set by application {@link #setNextOutputFile}.
+ * For best practice, application should use this event to keep track
+ * of whether the file previously set has been used or not.
+ * @see android.media.MediaRecorder.OnInfoListener
+ */
+ public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803;
/** informational events for individual tracks, for testing purpose.
* The track informational event usually contains two parts in the ext1
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 6c79ab7..7c509d2 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -290,7 +290,7 @@
}
static void
-android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
+android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor)
{
ALOGV("setOutputFile");
if (fileDescriptor == NULL) {
@@ -303,7 +303,25 @@
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
- status_t opStatus = mr->setOutputFile(fd, offset, length);
+ status_t opStatus = mr->setOutputFile(fd);
+ process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
+}
+
+static void
+android_media_MediaRecorder_setNextOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor)
+{
+ ALOGV("setNextOutputFile");
+ if (fileDescriptor == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+ if (mr == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+ status_t opStatus = mr->setNextOutputFile(fd);
process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
}
@@ -617,7 +635,8 @@
{"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder},
{"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder},
{"setParameter", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameter},
- {"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD},
+ {"_setOutputFile", "(Ljava/io/FileDescriptor;)V", (void *)android_media_MediaRecorder_setOutputFileFD},
+ {"_setNextOutputFile", "(Ljava/io/FileDescriptor;)V", (void *)android_media_MediaRecorder_setNextOutputFileFD},
{"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize},
{"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate},
{"setMaxDuration", "(I)V", (void *)android_media_MediaRecorder_setMaxDuration},
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 18ae3cf..1476110 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -87,6 +87,7 @@
// Allow the PIP to be "docked" slightly offscreen
private boolean mEnableMinimizing = true;
+ private final Rect mStableInsets = new Rect();
private final Rect mPinnedStackBounds = new Rect();
private final Rect mBoundedPinnedStackBounds = new Rect();
private ValueAnimator mPinnedStackBoundsAnimator = null;
@@ -421,7 +422,8 @@
mContext.getDisplay().getRealSize(displaySize);
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
mPinnedStackBounds);
- mSnapAlgorithm.applyMinimizedOffset(toBounds, mBoundedPinnedStackBounds, displaySize);
+ mSnapAlgorithm.applyMinimizedOffset(toBounds, mBoundedPinnedStackBounds, displaySize,
+ mStableInsets);
mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
toBounds, MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN,
mUpdatePinnedStackBoundsListener);
@@ -528,6 +530,7 @@
if (updatePinnedStackBounds) {
mPinnedStackBounds.set(info.bounds);
}
+ mWindowManager.getStableInsets(info.displayId, mStableInsets);
mBoundedPinnedStackBounds.set(mWindowManager.getPictureInPictureMovementBounds(
info.displayId));
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d5a6a58..06fadd1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -76,6 +76,8 @@
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -599,13 +601,12 @@
}
return true;
}
- case KeyEvent.KEYCODE_DPAD_UP: {
- EventBus.getDefault().send(
- new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
- return true;
- }
- case KeyEvent.KEYCODE_DPAD_DOWN: {
- EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT: {
+ final Direction direction = NavigateTaskViewEvent.getDirectionFromKeyCode(keyCode);
+ EventBus.getDefault().send(new NavigateTaskViewEvent(direction));
return true;
}
case KeyEvent.KEYCODE_DEL:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
new file mode 100644
index 0000000..5508d26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
@@ -0,0 +1,49 @@
+/*
+ * 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.systemui.recents.events.ui.focus;
+
+import android.view.KeyEvent;
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Navigates the task view by arrow keys.
+ */
+public class NavigateTaskViewEvent extends EventBus.Event {
+ public enum Direction {
+ UNDEFINED, UP, DOWN, LEFT, RIGHT;
+ }
+
+ public Direction direction;
+ public NavigateTaskViewEvent(Direction direction) {
+ this.direction = direction;
+ }
+
+ public static Direction getDirectionFromKeyCode(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_UP:
+ return Direction.UP;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ return Direction.DOWN;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ return Direction.LEFT;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ return Direction.RIGHT;
+ default:
+ return Direction.UNDEFINED;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fc2550a..3f28d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -87,6 +87,7 @@
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -1869,6 +1870,26 @@
setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
}
+ public final void onBusEvent(NavigateTaskViewEvent event) {
+ if (useGridLayout()) {
+ final int taskCount = mStack.getTaskCount();
+ final int currentIndex = mStack.indexOfStackTask(getFocusedTask());
+ final int nextIndex = mLayoutAlgorithm.mTaskGridLayoutAlgorithm.navigateFocus(taskCount,
+ currentIndex, event.direction);
+ setFocusedTask(nextIndex, false, true);
+ } else {
+ switch (event.direction) {
+ case UP:
+ EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
+ break;
+ case DOWN:
+ EventBus.getDefault().send(
+ new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
+ break;
+ }
+ }
+ }
+
public final void onBusEvent(UserInteractionEvent event) {
// Poke the doze trigger on user interaction
mUIDozeTrigger.poke();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
index 70536b1..78c26dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -23,6 +23,8 @@
import android.view.WindowManager;
import com.android.systemui.R;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskViewTransform;
@@ -63,6 +65,8 @@
Rect size;
int[] xOffsets;
int[] yOffsets;
+ int tasksPerLine;
+ int lines;
TaskGridRectInfo(int taskCount) {
size = new Rect();
@@ -71,10 +75,10 @@
int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
- int tasksPerLine = layoutTaskCount < 2 ? 1 : (
+ tasksPerLine = layoutTaskCount < 2 ? 1 : (
layoutTaskCount < 5 ? 2 : (
layoutTaskCount < 7 ? 3 : 4));
- int lines = layoutTaskCount < 3 ? 1 : 2;
+ lines = layoutTaskCount < 3 ? 1 : 2;
// A couple of special cases.
boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
@@ -200,6 +204,48 @@
return transformOut;
}
+ /**
+ * Return the proper task index to focus for arrow key navigation.
+ * @param taskCount The amount of tasks.
+ * @param currentFocusedIndex The index of the currently focused task.
+ * @param direction The direction we're navigating.
+ * @return The index of the task that should get the focus.
+ */
+ public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
+ if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
+ return -1;
+ }
+ if (currentFocusedIndex == -1) {
+ return 0;
+ }
+ int newIndex = currentFocusedIndex;
+ final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+ final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
+ switch (direction) {
+ case UP:
+ newIndex += gridInfo.tasksPerLine;
+ newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
+ break;
+ case DOWN:
+ newIndex -= gridInfo.tasksPerLine;
+ newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
+ break;
+ case LEFT:
+ newIndex++;
+ final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
+ newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
+ break;
+ case RIGHT:
+ newIndex--;
+ int rightMostIndex =
+ (taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
+ rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
+ newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
+ break;
+ }
+ return newIndex;
+ }
+
public void initialize(Rect windowRect) {
mWindowRect = windowRect;
// Define paddings in terms of percentage of the total area.
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index 962ac6f..1bdff6b 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -22,13 +22,20 @@
import android.os.StatFs;
import android.os.SystemClock;
import android.os.storage.StorageManager;
+import android.service.diskstats.DiskStatsAppSizesProto;
+import android.service.diskstats.DiskStatsCachedValuesProto;
+import android.service.diskstats.DiskStatsFreeSpaceProto;
+import android.service.diskstats.DiskStatsServiceDumpProto;
import android.util.Log;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
import libcore.io.IoUtils;
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -78,30 +85,68 @@
long after = SystemClock.uptimeMillis();
if (tmp.exists()) tmp.delete();
- if (error != null) {
- pw.print("Test-Error: ");
- pw.println(error.toString());
+ boolean protoFormat = hasOption(args, "--proto");
+ ProtoOutputStream proto = null;
+
+ if (protoFormat) {
+ proto = new ProtoOutputStream(fd);
+ pw = null;
+ proto.write(DiskStatsServiceDumpProto.HAS_TEST_ERROR, error != null);
+ if (error != null) {
+ proto.write(DiskStatsServiceDumpProto.ERROR_MESSAGE, error.toString());
+ } else {
+ proto.write(DiskStatsServiceDumpProto.WRITE_512B_LATENCY_MILLIS, after - before);
+ }
} else {
- pw.print("Latency: ");
- pw.print(after - before);
- pw.println("ms [512B Data Write]");
+ if (error != null) {
+ pw.print("Test-Error: ");
+ pw.println(error.toString());
+ } else {
+ pw.print("Latency: ");
+ pw.print(after - before);
+ pw.println("ms [512B Data Write]");
+ }
}
- reportFreeSpace(Environment.getDataDirectory(), "Data", pw);
- reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw);
- reportFreeSpace(new File("/system"), "System", pw);
+ reportFreeSpace(Environment.getDataDirectory(), "Data", pw, proto,
+ DiskStatsFreeSpaceProto.FOLDER_DATA);
+ reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw, proto,
+ DiskStatsFreeSpaceProto.FOLDER_CACHE);
+ reportFreeSpace(new File("/system"), "System", pw, proto,
+ DiskStatsFreeSpaceProto.FOLDER_SYSTEM);
- if (StorageManager.isFileEncryptedNativeOnly()) {
+ boolean fileBased = StorageManager.isFileEncryptedNativeOnly();
+ boolean blockBased = fileBased ? false : StorageManager.isBlockEncrypted();
+ if (protoFormat) {
+ if (fileBased) {
+ proto.write(DiskStatsServiceDumpProto.ENCRYPTION,
+ DiskStatsServiceDumpProto.ENCRYPTION_FILE_BASED);
+ } else if (blockBased) {
+ proto.write(DiskStatsServiceDumpProto.ENCRYPTION,
+ DiskStatsServiceDumpProto.ENCRYPTION_FULL_DISK);
+ } else {
+ proto.write(DiskStatsServiceDumpProto.ENCRYPTION,
+ DiskStatsServiceDumpProto.ENCRYPTION_NONE);
+ }
+ } else if (fileBased) {
pw.println("File-based Encryption: true");
}
- reportCachedValues(pw);
+ if (protoFormat) {
+ reportCachedValuesProto(proto);
+ } else {
+ reportCachedValues(pw);
+ }
+ if (protoFormat) {
+ proto.flush();
+ }
// TODO: Read /proc/yaffs and report interesting values;
// add configurable (through args) performance test parameters.
}
- private void reportFreeSpace(File path, String name, PrintWriter pw) {
+ private void reportFreeSpace(File path, String name, PrintWriter pw,
+ ProtoOutputStream proto, int folderType) {
try {
StatFs statfs = new StatFs(path.getPath());
long bsize = statfs.getBlockSize();
@@ -112,22 +157,44 @@
"Invalid stat: bsize=" + bsize + " avail=" + avail + " total=" + total);
}
- pw.print(name);
- pw.print("-Free: ");
- pw.print(avail * bsize / 1024);
- pw.print("K / ");
- pw.print(total * bsize / 1024);
- pw.print("K total = ");
- pw.print(avail * 100 / total);
- pw.println("% free");
+ if (proto != null) {
+ long freeSpaceToken = proto.start(DiskStatsServiceDumpProto.PARTITIONS_FREE_SPACE);
+ proto.write(DiskStatsFreeSpaceProto.FOLDER, folderType);
+ proto.write(DiskStatsFreeSpaceProto.AVAILABLE_SPACE, avail * bsize / 1024);
+ proto.write(DiskStatsFreeSpaceProto.TOTAL_SPACE, total * bsize / 1024);
+ proto.end(freeSpaceToken);
+ } else {
+ pw.print(name);
+ pw.print("-Free: ");
+ pw.print(avail * bsize / 1024);
+ pw.print("K / ");
+ pw.print(total * bsize / 1024);
+ pw.print("K total = ");
+ pw.print(avail * 100 / total);
+ pw.println("% free");
+ }
} catch (IllegalArgumentException e) {
- pw.print(name);
- pw.print("-Error: ");
- pw.println(e.toString());
+ if (proto != null) {
+ // Empty proto
+ } else {
+ pw.print(name);
+ pw.print("-Error: ");
+ pw.println(e.toString());
+ }
return;
}
}
+ private boolean hasOption(String[] args, String arg) {
+ for (String opt : args) {
+ if (arg.equals(opt)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // If you change this method, make sure to modify the Proto version of this method as well.
private void reportCachedValues(PrintWriter pw) {
try {
String jsonString = IoUtils.readFileAsString(DISKSTATS_DUMP_FILE);
@@ -159,4 +226,52 @@
}
}
+ private void reportCachedValuesProto(ProtoOutputStream proto) {
+ try {
+ String jsonString = IoUtils.readFileAsString(DISKSTATS_DUMP_FILE);
+ JSONObject json = new JSONObject(jsonString);
+ long cachedValuesToken = proto.start(DiskStatsServiceDumpProto.CACHED_FOLDER_SIZES);
+
+ proto.write(DiskStatsCachedValuesProto.AGG_APPS_SIZE,
+ json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY));
+ proto.write(DiskStatsCachedValuesProto.AGG_APPS_CACHE_SIZE,
+ json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY));
+ proto.write(DiskStatsCachedValuesProto.PHOTOS_SIZE,
+ json.getLong(DiskStatsFileLogger.PHOTOS_KEY));
+ proto.write(DiskStatsCachedValuesProto.VIDEOS_SIZE,
+ json.getLong(DiskStatsFileLogger.VIDEOS_KEY));
+ proto.write(DiskStatsCachedValuesProto.AUDIO_SIZE,
+ json.getLong(DiskStatsFileLogger.AUDIO_KEY));
+ proto.write(DiskStatsCachedValuesProto.DOWNLOADS_SIZE,
+ json.getLong(DiskStatsFileLogger.DOWNLOADS_KEY));
+ proto.write(DiskStatsCachedValuesProto.SYSTEM_SIZE,
+ json.getLong(DiskStatsFileLogger.SYSTEM_KEY));
+ proto.write(DiskStatsCachedValuesProto.OTHER_SIZE,
+ json.getLong(DiskStatsFileLogger.MISC_KEY));
+
+ JSONArray packageNamesArray = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
+ JSONArray appSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
+ JSONArray cacheSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
+ final int len = packageNamesArray.length();
+ if (len == appSizesArray.length() && len == cacheSizesArray.length()) {
+ for (int i = 0; i < len; i++) {
+ long packageToken = proto.start(DiskStatsCachedValuesProto.APP_SIZES);
+
+ proto.write(DiskStatsAppSizesProto.PACKAGE_NAME,
+ packageNamesArray.getString(i));
+ proto.write(DiskStatsAppSizesProto.APP_SIZE, appSizesArray.getLong(i));
+ proto.write(DiskStatsAppSizesProto.CACHE_SIZE, cacheSizesArray.getLong(i));
+
+ proto.end(packageToken);
+ }
+ } else {
+ Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray and cacheSizesArray "
+ + "are not the same");
+ }
+
+ proto.end(cachedValuesToken);
+ } catch (IOException | JSONException e) {
+ Log.w(TAG, "exception reading diskstats cache file", e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index e5787ae..e8ecc3e 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -604,7 +604,7 @@
@Override
public void requestRecommendationAsync(RecommendationRequest request,
RemoteCallback remoteCallback) {
- mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+ mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
final OneTimeCallback oneTimeCallback = new OneTimeCallback(remoteCallback);
final Pair<RecommendationRequest, OneTimeCallback> pair =
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7fd91cb..2a324eb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22776,6 +22776,52 @@
return mUserController.restartUser(userId, /* foreground */ false);
}
+ @Override
+ public void scheduleApplicationInfoChanged(List<String> packageNames, int userId) {
+ enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+ "scheduleApplicationInfoChanged()");
+
+ synchronized (this) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ updateApplicationInfoLocked(packageNames, userId);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
+ final boolean updateFrameworkRes = packagesToUpdate.contains("android");
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mLruProcesses.get(i);
+ if (app.thread == null) {
+ continue;
+ }
+
+ if (userId != UserHandle.USER_ALL && app.userId != userId) {
+ continue;
+ }
+
+ final int packageCount = app.pkgList.size();
+ for (int j = 0; j < packageCount; j++) {
+ final String packageName = app.pkgList.keyAt(j);
+ if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
+ try {
+ final ApplicationInfo ai = mPackageManagerInt.getApplicationInfo(
+ packageName, app.userId);
+ if (ai != null) {
+ app.thread.scheduleApplicationInfoChanged(ai);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
+ packageName, app));
+ }
+ }
+ }
+ }
+ }
+
/**
* Attach an agent to the specified process (proces name or PID)
*/
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 29a4781..ed31130 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -235,6 +235,8 @@
return runSupportsMultiwindow(pw);
case "supports-split-screen-multi-window":
return runSupportsSplitScreenMultiwindow(pw);
+ case "update-appinfo":
+ return runUpdateApplicationInfo(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -2323,6 +2325,19 @@
return 0;
}
+ int runUpdateApplicationInfo(PrintWriter pw) throws RemoteException {
+ int userid = UserHandle.parseUserArg(getNextArgRequired());
+ ArrayList<String> packages = new ArrayList<>();
+ packages.add(getNextArgRequired());
+ String packageName;
+ while ((packageName = getNextArg()) != null) {
+ packages.add(packageName);
+ }
+ mInternal.scheduleApplicationInfoChanged(packages, userid);
+ pw.println("Packages updated with most recent ApplicationInfos.");
+ return 0;
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@@ -2584,6 +2599,9 @@
pw.println(" Test command for sizing <TASK_ID> by <STEP_SIZE>");
pw.println(" increments within the screen applying the optional [DELAY_MS] between");
pw.println(" each step.");
+ pw.println(" update-appinfo <USER_ID> <PACKAGE_NAME> [<PACKAGE_NAME>...]");
+ pw.println(" Update the ApplicationInfo objects of the listed packages for <USER_ID>");
+ pw.println(" without restarting any processes.");
pw.println(" write");
pw.println(" Write all pending state to storage.");
pw.println();
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index de0d2a3..0130e30 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -242,6 +242,16 @@
}
}
+ public void setAppQuota(String uuid, int userId, int appId, long cacheQuota)
+ throws InstallerException {
+ if (!checkBeforeRemote()) return;
+ try {
+ mInstalld.setAppQuota(uuid, userId, appId, cacheQuota);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries)
@@ -351,10 +361,10 @@
}
}
- public void freeCache(String uuid, long freeStorageSize) throws InstallerException {
+ public void freeCache(String uuid, long freeStorageSize, int flags) throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- mInstalld.freeCache(uuid, freeStorageSize);
+ mInstalld.freeCache(uuid, freeStorageSize, flags);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index af1e007..6669889 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3585,7 +3585,7 @@
boolean success = true;
synchronized (mInstallLock) {
try {
- mInstaller.freeCache(volumeUuid, freeStorageSize);
+ mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
} catch (InstallerException e) {
Slog.w(TAG, "Couldn't clear application caches: " + e);
success = false;
@@ -3614,7 +3614,7 @@
boolean success = true;
synchronized (mInstallLock) {
try {
- mInstaller.freeCache(volumeUuid, freeStorageSize);
+ mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
} catch (InstallerException e) {
Slog.w(TAG, "Couldn't clear application caches: " + e);
success = false;
@@ -3637,7 +3637,7 @@
void freeStorage(String volumeUuid, long freeStorageSize) throws IOException {
synchronized (mInstallLock) {
try {
- mInstaller.freeCache(volumeUuid, freeStorageSize);
+ mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
} catch (InstallerException e) {
throw new IOException("Failed to free enough space", e);
}
@@ -13707,7 +13707,7 @@
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
try {
- mInstaller.freeCache(null, sizeBytes + lowThreshold);
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
} catch (InstallerException e) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e646ffc..1eb8b94 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1567,7 +1567,7 @@
}
synchronized(mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
- if (!userInfo.canHaveProfile()) {
+ if (userInfo == null || !userInfo.canHaveProfile()) {
return false;
}
int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index a900702..34f6752 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -87,7 +87,6 @@
private final Runnable mRemoveStartingWindow = () -> {
StartingSurface surface = null;
- StartingData data = null;
synchronized (mWindowMap) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " + mContainer
+ ": startingWindow=" + mContainer.startingWindow
@@ -97,14 +96,13 @@
}
if (mContainer.startingWindow != null) {
surface = mContainer.startingSurface;
- data = mContainer.startingData;
mContainer.startingData = null;
mContainer.startingSurface = null;
mContainer.startingWindow = null;
mContainer.startingDisplayed = false;
}
}
- if (data != null && surface != null) {
+ if (surface != null) {
try {
surface.remove();
} catch (Exception e) {
@@ -115,12 +113,14 @@
private final Runnable mAddStartingWindow = () -> {
final StartingData startingData;
+ final AppWindowToken container;
synchronized (mWindowMap) {
if (mContainer == null) {
return;
}
startingData = mContainer.startingData;
+ container = mContainer;
}
if (startingData == null) {
@@ -129,41 +129,40 @@
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
- + this + ": startingData=" + mContainer.startingData);
+ + this + ": startingData=" + container.startingData);
StartingSurface surface = null;
try {
- surface = startingData.createStartingSurface();
+ surface = startingData.createStartingSurface(container);
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when adding starting window", e);
}
if (surface != null) {
boolean abort = false;
synchronized(mWindowMap) {
- if (mContainer.removed || mContainer.startingData == null) {
+ if (container.removed || container.startingData == null) {
// If the window was successfully added, then
// we need to remove it.
- if (mContainer.startingWindow != null) {
+ if (container.startingWindow != null) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Aborted starting " + mContainer
- + ": removed=" + mContainer.removed
- + " startingData=" + mContainer.startingData);
+ "Aborted starting " + container
+ + ": removed=" + container.removed
+ + " startingData=" + container.startingData);
+ container.startingWindow = null;
+ container.startingData = null;
abort = true;
}
} else {
- mContainer.startingSurface = surface;
+ container.startingSurface = surface;
}
if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
"Added starting " + mContainer
+ ": startingWindow="
- + mContainer.startingWindow + " startingView="
- + mContainer.startingSurface);
+ + container.startingWindow + " startingView="
+ + container.startingSurface);
}
if (abort) {
- mRemoveStartingWindow.run();
- if (mContainer == null) {
- return;
- }
+ surface.remove();
}
}
};
@@ -465,7 +464,7 @@
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
- mContainer.startingData = new SplashScreenStartingData(mService, mContainer, pkg, theme,
+ mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
mContainer.getMergedOverrideConfiguration());
scheduleAddStartingWindow();
@@ -499,8 +498,7 @@
return false;
}
- mContainer.startingData = new SnapshotStartingData(mService, mContainer,
- snapshot.getSnapshot());
+ mContainer.startingData = new SnapshotStartingData(mService, snapshot.getSnapshot());
scheduleAddStartingWindow();
return true;
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 34633c2..bfb4269 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -82,6 +82,7 @@
// Used to calculate stack bounds across rotations
private final DisplayInfo mDisplayInfo = new DisplayInfo();
+ private final Rect mStableInsets = new Rect();
// The size and position information that describes where the pinned stack will go by default.
private int mDefaultStackGravity;
@@ -250,10 +251,12 @@
}
/**
- * @return the repositioned PIP bounds given it's pre-change bounds, and the new display info.
+ * @return the repositioned PIP bounds given it's pre-change bounds, and the new display
+ * content.
*/
- Rect onDisplayChanged(Rect preChangeStackBounds, DisplayInfo displayInfo) {
+ Rect onDisplayChanged(Rect preChangeStackBounds, DisplayContent displayContent) {
final Rect postChangeStackBounds = new Rect(preChangeStackBounds);
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (!mDisplayInfo.equals(displayInfo)) {
// Calculate the snap fraction of the current stack along the old movement bounds, and
// then update the stack bounds to the same fraction along the rotated movement bounds.
@@ -269,8 +272,9 @@
if (mIsMinimized) {
final Point displaySize = new Point(mDisplayInfo.logicalWidth,
mDisplayInfo.logicalHeight);
+ mService.getStableInsetsLocked(displayContent.getDisplayId(), mStableInsets);
mSnapAlgorithm.applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds,
- displaySize);
+ displaySize, mStableInsets);
}
}
return postChangeStackBounds;
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index 9d97a0c..e73d4d25 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -27,16 +27,14 @@
private final WindowManagerService mService;
private final GraphicBuffer mSnapshot;
- SnapshotStartingData(WindowManagerService service, AppWindowToken appWindowToken,
- GraphicBuffer snapshot) {
- super(service, appWindowToken);
+ SnapshotStartingData(WindowManagerService service, GraphicBuffer snapshot) {
+ super(service);
mService = service;
mSnapshot = snapshot;
}
@Override
- StartingSurface createStartingSurface() {
- return mService.mTaskSnapshotController.createStartingSurface(
- mAppWindowToken, mSnapshot);
+ StartingSurface createStartingSurface(AppWindowToken atoken) {
+ return mService.mTaskSnapshotController.createStartingSurface(atoken, mSnapshot);
}
}
diff --git a/services/core/java/com/android/server/wm/SplashScreenStartingData.java b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
index 664e600..ee4209f 100644
--- a/services/core/java/com/android/server/wm/SplashScreenStartingData.java
+++ b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
@@ -35,11 +35,10 @@
private final int mWindowFlags;
private final Configuration mMergedOverrideConfiguration;
- SplashScreenStartingData(WindowManagerService service, AppWindowToken appWindowToken,
- String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
- int labelRes, int icon, int logo, int windowFlags,
- Configuration mergedOverrideConfiguration) {
- super(service, appWindowToken);
+ SplashScreenStartingData(WindowManagerService service, String pkg, int theme,
+ CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
+ int logo, int windowFlags, Configuration mergedOverrideConfiguration) {
+ super(service);
mPkg = pkg;
mTheme = theme;
mCompatInfo = compatInfo;
@@ -52,8 +51,8 @@
}
@Override
- StartingSurface createStartingSurface() {
- return mService.mPolicy.addSplashScreen(mAppWindowToken.token, mPkg, mTheme, mCompatInfo,
+ StartingSurface createStartingSurface(AppWindowToken atoken) {
+ return mService.mPolicy.addSplashScreen(atoken.token, mPkg, mTheme, mCompatInfo,
mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,
mMergedOverrideConfiguration);
}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index fcc4c3c..8c564bb 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -24,19 +24,18 @@
public abstract class StartingData {
protected final WindowManagerService mService;
- protected final AppWindowToken mAppWindowToken;
- protected StartingData(WindowManagerService service, AppWindowToken appWindowToken) {
+ protected StartingData(WindowManagerService service) {
mService = service;
- mAppWindowToken = appWindowToken;
}
/**
* Creates the actual starting window surface. DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING
* THIS METHOD.
*
+ * @param atoken the app to add the starting window to
* @return a class implementing {@link StartingSurface} for easy removal with
* {@link StartingSurface#remove}
*/
- abstract StartingSurface createStartingSurface();
+ abstract StartingSurface createStartingSurface(AppWindowToken atoken);
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index bebc745..53292bb 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -413,7 +413,7 @@
switch (mStackId) {
case PINNED_STACK_ID:
mTmpRect2 = mDisplayContent.getPinnedStackController().onDisplayChanged(mBounds,
- getDisplayInfo());
+ mDisplayContent);
break;
case DOCKED_STACK_ID:
repositionDockedStackAfterRotation(mTmpRect2);
@@ -684,7 +684,7 @@
// Update the pinned stack controller after the display info is updated
if (mStackId == PINNED_STACK_ID) {
mDisplayContent.getPinnedStackController().onDisplayChanged(oldBounds,
- getDisplayInfo());
+ mDisplayContent);
}
super.onDisplayChanged(dc);
diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
index 17a6c297..545b3d7 100644
--- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
+++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
@@ -19,6 +19,7 @@
#include "JNIHelp.h"
#include "jni.h"
+#include <math.h>
#include <stdlib.h>
#include <android/hardware/thermal/1.0/IThermal.h>
@@ -56,10 +57,16 @@
jmethodID initMethod;
} gCpuUsageInfoClassInfo;
+jfloat gUndefinedTemperature;
+
static sp<IThermal> gThermalModule;
// ----------------------------------------------------------------------------
+float finalizeTemperature(float temperature) {
+ return isnan(temperature) ? gUndefinedTemperature : temperature;
+}
+
static void nativeInit(JNIEnv* env, jobject obj) {
// TODO(b/31632518)
if (gThermalModule == nullptr) {
@@ -128,16 +135,16 @@
if (static_cast<int>(list[i].type) == type) {
switch (source) {
case TEMPERATURE_CURRENT:
- values[length++] = list[i].currentValue;
+ values[length++] = finalizeTemperature(list[i].currentValue);
break;
case TEMPERATURE_THROTTLING:
- values[length++] = list[i].throttlingThreshold;
+ values[length++] = finalizeTemperature(list[i].throttlingThreshold);
break;
case TEMPERATURE_SHUTDOWN:
- values[length++] = list[i].shutdownThreshold;
+ values[length++] = finalizeTemperature(list[i].shutdownThreshold);
break;
case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
- values[length++] = list[i].vrThrottlingThreshold;
+ values[length++] = finalizeTemperature(list[i].vrThrottlingThreshold);
break;
}
}
@@ -204,6 +211,12 @@
gCpuUsageInfoClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gCpuUsageInfoClassInfo.initMethod = GetMethodIDOrDie(env, gCpuUsageInfoClassInfo.clazz,
"<init>", "(JJ)V");
+
+ clazz = env->FindClass("android/os/HardwarePropertiesManager");
+ jfieldID undefined_temperature_field = GetStaticFieldIDOrDie(env, clazz,
+ "UNDEFINED_TEMPERATURE", "F");
+ gUndefinedTemperature = env->GetStaticFloatField(clazz, undefined_temperature_field);
+
return res;
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 5d19b29..43c8957 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -290,12 +290,12 @@
@Test
public void testRequestRecommendationAsync_noPermission() throws Exception {
doThrow(new SecurityException()).when(mContext)
- .enforceCallingOrSelfPermission(eq(permission.BROADCAST_NETWORK_PRIVILEGED),
+ .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES),
anyString());
try {
mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
mRemoteCallback);
- fail("BROADCAST_NETWORK_PRIVILEGED not enforced.");
+ fail("REQUEST_NETWORK_SCORES not enforced.");
} catch (SecurityException e) {
// expected
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 7d258a0..b7391b4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -377,8 +377,16 @@
*/
public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6;
+ /**
+ * Set by the framework to indicate that the {@link Connection} originated from a self-managed
+ * {@link ConnectionService}.
+ * <p>
+ * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.
+ */
+ public static final int PROPERTY_SELF_MANAGED = 1<<7;
+
//**********************************************************************************************
- // Next PROPERTY value: 1<<7
+ // Next PROPERTY value: 1<<8
//**********************************************************************************************
/**
@@ -681,6 +689,10 @@
builder.append("Properties:");
}
+ if (can(properties, PROPERTY_SELF_MANAGED)) {
+ builder.append(isLong ? " PROPERTY_SELF_MANAGED" : " self_mng");
+ }
+
if (can(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
builder.append(isLong ? " PROPERTY_EMERGENCY_CALLBACK_MODE" : " ecbm");
}
@@ -741,6 +753,7 @@
public void onConnectionEvent(Connection c, String event, Bundle extras) {}
/** @hide */
public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
+ public void onAudioRouteChanged(Connection c, int audioRoute) {}
}
/**
@@ -2325,6 +2338,25 @@
}
/**
+ * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will
+ * be change to the {@link #getCallAudioState()}.
+ * <p>
+ * Used by self-managed {@link ConnectionService}s which wish to change the audio route for a
+ * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
+ * <p>
+ * See also {@link InCallService#setAudioRoute(int)}.
+ *
+ * @param route The audio route to use (one of {@link CallAudioState#ROUTE_BLUETOOTH},
+ * {@link CallAudioState#ROUTE_EARPIECE}, {@link CallAudioState#ROUTE_SPEAKER}, or
+ * {@link CallAudioState#ROUTE_WIRED_HEADSET}).
+ */
+ public final void setAudioRoute(int route) {
+ for (Listener l : mListeners) {
+ l.onAudioRouteChanged(this, route);
+ }
+ }
+
+ /**
* Notifies this Connection that the {@link #getAudioState()} property has a new value.
*
* @param state The new connection audio state.
@@ -2479,6 +2511,21 @@
*/
public void onExtrasChanged(Bundle extras) {}
+ /**
+ * Notifies this {@link Connection} that its {@link ConnectionService} is responsible for
+ * displaying its incoming call user interface for the {@link Connection}.
+ * <p>
+ * Will only be called for incoming calls added via a self-managed {@link ConnectionService}
+ * (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}), where the {@link ConnectionService}
+ * should show its own incoming call user interface.
+ * <p>
+ * Where there are ongoing calls in other self-managed {@link ConnectionService}s, or in a
+ * regular {@link ConnectionService}, the Telecom framework will display its own incoming call
+ * user interface to allow the user to choose whether to answer the new incoming call and
+ * disconnect other ongoing calls, or to reject the new incoming call.
+ */
+ public void onShowIncomingCallUi() {}
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index aba38fe..2343462 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -33,6 +33,7 @@
private final Bundle mExtras;
private final int mVideoState;
private final String mTelecomCallId;
+ private final boolean mShouldShowIncomingCallUi;
/**
* @param accountHandle The accountHandle which should be used to place the call.
@@ -43,7 +44,7 @@
PhoneAccountHandle accountHandle,
Uri handle,
Bundle extras) {
- this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null);
+ this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null, false);
}
/**
@@ -57,7 +58,7 @@
Uri handle,
Bundle extras,
int videoState) {
- this(accountHandle, handle, extras, videoState, null);
+ this(accountHandle, handle, extras, videoState, null, false);
}
/**
@@ -66,6 +67,10 @@
* @param extras Application-specific extra data.
* @param videoState Determines the video state for the connection.
* @param telecomCallId The telecom call ID.
+ * @param shouldShowIncomingCallUi For a self-managed {@link ConnectionService}, will be
+ * {@code true} if the {@link ConnectionService} should show its
+ * own incoming call UI for an incoming call. When
+ * {@code false}, Telecom shows the incoming call UI.
* @hide
*/
public ConnectionRequest(
@@ -73,12 +78,14 @@
Uri handle,
Bundle extras,
int videoState,
- String telecomCallId) {
+ String telecomCallId,
+ boolean shouldShowIncomingCallUi) {
mAccountHandle = accountHandle;
mAddress = handle;
mExtras = extras;
mVideoState = videoState;
mTelecomCallId = telecomCallId;
+ mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
}
private ConnectionRequest(Parcel in) {
@@ -87,6 +94,7 @@
mExtras = in.readParcelable(getClass().getClassLoader());
mVideoState = in.readInt();
mTelecomCallId = in.readString();
+ mShouldShowIncomingCallUi = in.readInt() == 1;
}
/**
@@ -129,6 +137,18 @@
return mTelecomCallId;
}
+ /**
+ * For a self-managed {@link ConnectionService}, indicates for an incoming call whether the
+ * {@link ConnectionService} should show its own incoming call UI for an incoming call.
+ *
+ * @return {@code true} if the {@link ConnectionService} should show its own incoming call UI.
+ * When {@code false}, Telecom shows the incoming call UI for the call.
+ * @hide
+ */
+ public boolean shouldShowIncomingCallUi() {
+ return mShouldShowIncomingCallUi;
+ }
+
@Override
public String toString() {
return String.format("ConnectionRequest %s %s",
@@ -165,5 +185,6 @@
destination.writeParcelable(mExtras, 0);
destination.writeInt(mVideoState);
destination.writeString(mTelecomCallId);
+ destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index b119e16..d0ccd55 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -42,10 +42,15 @@
import java.util.concurrent.ConcurrentHashMap;
/**
- * An abstract service that should be implemented by any apps which can make phone calls (VoIP or
- * otherwise) and want those calls to be integrated into the built-in phone app.
- * Once implemented, the {@code ConnectionService} needs two additional steps before it will be
- * integrated into the phone app:
+ * An abstract service that should be implemented by any apps which either:
+ * <ol>
+ * <li>Can make phone calls (VoIP or otherwise) and want those calls to be integrated into the
+ * built-in phone app. Referred to as a <b>system managed</b> {@link ConnectionService}.</li>
+ * <li>Are a standalone calling app and don't want their calls to be integrated into the
+ * built-in phone app. Referred to as a <b>self managed</b> {@link ConnectionService}.</li>
+ * </ol>
+ * Once implemented, the {@link ConnectionService} needs to take the following steps so that Telecom
+ * will bind to it:
* <p>
* 1. <i>Registration in AndroidManifest.xml</i>
* <br/>
@@ -63,16 +68,20 @@
* <br/>
* See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information.
* <p>
- * Once registered and enabled by the user in the phone app settings, telecom will bind to a
- * {@code ConnectionService} implementation when it wants that {@code ConnectionService} to place
- * a call or the service has indicated that is has an incoming call through
- * {@link TelecomManager#addNewIncomingCall}. The {@code ConnectionService} can then expect a call
- * to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection} wherein it
- * should provide a new instance of a {@link Connection} object. It is through this
- * {@link Connection} object that telecom receives state updates and the {@code ConnectionService}
+ * System managed {@link ConnectionService}s must be enabled by the user in the phone app settings
+ * before Telecom will bind to them. Self-manged {@link ConnectionService}s must be granted the
+ * appropriate permission before Telecom will bind to them.
+ * <p>
+ * Once registered and enabled by the user in the phone app settings or granted permission, telecom
+ * will bind to a {@link ConnectionService} implementation when it wants that
+ * {@link ConnectionService} to place a call or the service has indicated that is has an incoming
+ * call through {@link TelecomManager#addNewIncomingCall}. The {@link ConnectionService} can then
+ * expect a call to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection}
+ * wherein it should provide a new instance of a {@link Connection} object. It is through this
+ * {@link Connection} object that telecom receives state updates and the {@link ConnectionService}
* receives call-commands such as answer, reject, hold and disconnect.
* <p>
- * When there are no more live calls, telecom will unbind from the {@code ConnectionService}.
+ * When there are no more live calls, telecom will unbind from the {@link ConnectionService}.
*/
public abstract class ConnectionService extends Service {
/**
@@ -1054,6 +1063,7 @@
}
}
+ @Override
public void onExtrasRemoved(Connection c, List<String> keys) {
String id = mIdByConnection.get(c);
if (id != null) {
@@ -1061,7 +1071,6 @@
}
}
-
@Override
public void onConnectionEvent(Connection connection, String event, Bundle extras) {
String id = mIdByConnection.get(connection);
@@ -1069,6 +1078,14 @@
mAdapter.onConnectionEvent(id, event, extras);
}
}
+
+ @Override
+ public void onAudioRouteChanged(Connection c, int audioRoute) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.setAudioRoute(id, audioRoute);
+ }
+ }
};
/** {@inheritDoc} */
@@ -1146,6 +1163,13 @@
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),
connection.getExtras()));
+
+ if (isIncoming && request.shouldShowIncomingCallUi() &&
+ (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) ==
+ Connection.PROPERTY_SELF_MANAGED) {
+ // Tell ConnectionService to show its incoming call UX.
+ connection.onShowIncomingCallUi();
+ }
if (isUnknown) {
triggerConferenceRecalculate();
}
@@ -1587,6 +1611,38 @@
}
/**
+ * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+ * incoming {@link Connection} was denied.
+ * <p>
+ * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
+ * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time.
+ * The {@link ConnectionService} is responsible for silently rejecting the new incoming
+ * {@link Connection}.
+ * <p>
+ * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
+ *
+ * @param request The incoming connection request.
+ */
+ public void onCreateIncomingConnectionFailed(ConnectionRequest request) {
+ }
+
+ /**
+ * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+ * outgoing {@link Connection} was denied.
+ * <p>
+ * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
+ * {@link Connection}, but Telecom has determined that the call cannot be placed at this time.
+ * The {@link ConnectionService} is responisible for informing the user that the
+ * {@link Connection} cannot be made at this time.
+ * <p>
+ * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
+ *
+ * @param request The outgoing connection request.
+ */
+ public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {
+ }
+
+ /**
* Trigger recalculate functinality for conference calls. This is used when a Telephony
* Connection is part of a conference controller but is not yet added to Connection
* Service and hence cannot be added to the conference call.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index f3fada9..9542b73 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -515,6 +515,23 @@
}
/**
+ * Sets the audio route associated with a {@link Connection}.
+ *
+ * @param callId The unique ID of the call.
+ * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
+ */
+ void setAudioRoute(String callId, int audioRoute) {
+ Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute));
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+
+ /**
* Informs Telecom of a connection level event.
*
* @param callId The unique ID of the call.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index afe5e33..cc437f9 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -67,6 +67,7 @@
private static final int MSG_ON_CONNECTION_EVENT = 26;
private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
private static final int MSG_SET_PULLING = 28;
+ private static final int MSG_SET_AUDIO_ROUTE = 29;
private final IConnectionServiceAdapter mDelegate;
@@ -289,6 +290,16 @@
}
break;
}
+ case MSG_SET_AUDIO_ROUTE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setAudioRoute((String) args.arg1, args.argi1,
+ (Session.Info) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
}
}
};
@@ -507,6 +518,17 @@
}
@Override
+ public final void setAudioRoute(String connectionId, int audioRoute,
+ Session.Info sessionInfo) {
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.argi1 = audioRoute;
+ args.arg2 = sessionInfo;
+ mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, args).sendToTarget();
+ }
+
+ @Override
public final void onConnectionEvent(String connectionId, String event, Bundle extras,
Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index ca54486..845a103 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -189,6 +189,21 @@
public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 0x400;
/**
+ * Flag indicating that this {@link PhoneAccount} is responsible for managing its own
+ * {@link Connection}s. This type of {@link PhoneAccount} is ideal for use with standalone
+ * calling apps which do not wish to use the default phone app for {@link Connection} UX,
+ * but which want to leverage the call and audio routing capabilities of the Telecom framework.
+ * <p>
+ * When set, {@link Connection}s created by the self-managed {@link ConnectionService} will not
+ * be surfaced to implementations of the {@link InCallService} API. Thus it is the
+ * responsibility of a self-managed {@link ConnectionService} to provide a user interface for
+ * its {@link Connection}s.
+ * <p>
+ * Self-managed {@link Connection}s will, however, be displayed on connected Bluetooth devices.
+ */
+ public static final int CAPABILITY_SELF_MANAGED = 0x800;
+
+ /**
* URI scheme for telephone number URIs.
*/
public static final String SCHEME_TEL = "tel";
@@ -692,6 +707,14 @@
mIsEnabled = isEnabled;
}
+ /**
+ * @return {@code true} if the {@link PhoneAccount} is self-managed, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isSelfManaged() {
+ return (mCapabilities & CAPABILITY_SELF_MANAGED) == CAPABILITY_SELF_MANAGED;
+ }
+
//
// Parcelable implementation
//
@@ -815,6 +838,9 @@
*/
private String capabilitiesToString() {
StringBuilder sb = new StringBuilder();
+ if (hasCapabilities(CAPABILITY_SELF_MANAGED)) {
+ sb.append("SelfManaged ");
+ }
if (hasCapabilities(CAPABILITY_SUPPORTS_VIDEO_CALLING)) {
sb.append("SuppVideo ");
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index d8a226a..0c7404a 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -389,6 +389,15 @@
}
@Override
+ public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) {
+ if (hasConnection(callId)) {
+ // TODO(3pcalls): handle this for remote connections.
+ // Likely we don't want to do anything since it doesn't make sense for self-managed
+ // connections to go through a connection mgr.
+ }
+ }
+
+ @Override
public void onConnectionEvent(String callId, String event, Bundle extras,
Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f12886a..00e8f9f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1202,17 +1202,25 @@
/**
* Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
- * has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
- * with {@link #registerPhoneAccount} and the user must have enabled the corresponding
- * {@link PhoneAccount}. This can be checked using {@link #getPhoneAccount}. Once invoked, this
- * method will cause the system to bind to the {@link ConnectionService} associated with the
- * {@link PhoneAccountHandle} and request additional information about the call
- * (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming
+ * has an incoming call. For managed {@link ConnectionService}s, the specified
+ * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
+ * the user must have enabled the corresponding {@link PhoneAccount}. This can be checked using
+ * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call.
+ * <p>
+ * Once invoked, this method will cause the system to bind to the {@link ConnectionService}
+ * associated with the {@link PhoneAccountHandle} and request additional information about the
+ * call (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming
* call UI.
* <p>
- * A {@link SecurityException} will be thrown if either the {@link PhoneAccountHandle} does not
- * correspond to a registered {@link PhoneAccount} or the associated {@link PhoneAccount} is not
- * currently enabled by the user.
+ * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either
+ * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or
+ * the associated {@link PhoneAccount} is not currently enabled by the user.
+ * <p>
+ * For a self-managed {@link ConnectionService}, a {@link SecurityException} will be thrown if
+ * the {@link PhoneAccount} has {@link PhoneAccount#CAPABILITY_SELF_MANAGED} and the calling app
+ * does not have {@link android.Manifest.permission#MANAGE_OWN_CALLS}.
+ *
* @param phoneAccount A {@link PhoneAccountHandle} registered with
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
@@ -1379,7 +1387,8 @@
* method-caller is either the user selected default dialer app or preloaded system dialer
* app, then emergency calls will also be allowed.
*
- * Requires permission: {@link android.Manifest.permission#CALL_PHONE}
+ * Placing a call via a managed {@link ConnectionService} requires permission:
+ * {@link android.Manifest.permission#CALL_PHONE}
*
* Usage example:
* <pre>
@@ -1396,11 +1405,20 @@
* <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
* </ul>
+ * <p>
+ * An app which implements the self-managed {@link ConnectionService} API uses
+ * {@link #placeCall(Uri, Bundle)} to inform Telecom of a new outgoing call. A self-managed
+ * {@link ConnectionService} must include {@link #EXTRA_PHONE_ACCOUNT_HANDLE} to specify its
+ * associated {@link android.telecom.PhoneAccountHandle}.
+ *
+ * Self-managed {@link ConnectionService}s require permission
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS}.
*
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
- @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ @RequiresPermission(anyOf = {android.Manifest.permission.CALL_PHONE,
+ android.Manifest.permission.MANAGE_OWN_CALLS})
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1476,6 +1494,71 @@
return result;
}
+ /**
+ * Determines whether Telecom would permit an incoming call to be added via the
+ * {@link #addNewIncomingCall(PhoneAccountHandle, Bundle)} API for the specified
+ * {@link PhoneAccountHandle}.
+ * <p>
+ * A {@link ConnectionService} may not add a call for the specified {@link PhoneAccountHandle}
+ * in the following situations:
+ * <ul>
+ * <li>{@link PhoneAccount} does not have property
+ * {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set (i.e. it is a managed
+ * {@link ConnectionService}), and the active or held call limit has
+ * been reached.</li>
+ * <li>There is an ongoing emergency call.</li>
+ * </ul>
+ *
+ * @param phoneAccountHandle The {@link PhoneAccountHandle} the call will be added for.
+ * @return {@code true} if telecom will permit an incoming call to be added, {@code false}
+ * otherwise.
+ */
+ public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isIncomingCallPermitted(phoneAccountHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error isIncomingCallPermitted", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether Telecom would permit an outgoing call to be placed via the
+ * {@link #placeCall(Uri, Bundle)} API for the specified {@link PhoneAccountHandle}.
+ * <p>
+ * A {@link ConnectionService} may not place a call for the specified {@link PhoneAccountHandle}
+ * in the following situations:
+ * <ul>
+ * <li>{@link PhoneAccount} does not have property
+ * {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set (i.e. it is a managed
+ * {@link ConnectionService}), and the active, held or ringing call limit has
+ * been reached.</li>
+ * <li>{@link PhoneAccount} has property {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set
+ * (i.e. it is a self-managed {@link ConnectionService} and there is an ongoing call in
+ * another {@link ConnectionService}.</li>
+ * <li>There is an ongoing emergency call.</li>
+ * </ul>
+ *
+ * @param phoneAccountHandle The {@link PhoneAccountHandle} the call will be added for.
+ * @return {@code true} if telecom will permit an outgoing call to be placed, {@code false}
+ * otherwise.
+ */
+ public boolean isOutgoingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isOutgoingCallPermitted(phoneAccountHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error isOutgoingCallPermitted", e);
+ }
+ }
+ return false;
+ }
+
+
private ITelecomService getTelecomService() {
if (mTelecomServiceOverride != null) {
return mTelecomServiceOverride;
diff --git a/telecomm/java/android/telecom/package-info.java b/telecomm/java/android/telecom/package-info.java
new file mode 100644
index 0000000..a4140e5
--- /dev/null
+++ b/telecomm/java/android/telecom/package-info.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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
+ */
+
+/**
+ * The Android Telecom framework is responsible for managing calls on an Android device. This can
+ * include SIM-based calls using the {@code Telephony} framework, VOIP calls using SIP (e.g. the
+ * {@code SipConnectionService}), or via a third-party VOIP
+ * {@link android.telecom.ConnectionService}. Telecom acts as a switchboard, routing calls and
+ * audio focus between {@link android.telecom.Connection}s provided by
+ * {@link android.telecom.ConnectionService} implementations, and
+ * {@link android.telecom.InCallService} implementations which provide a user interface for calls.
+ * <p>
+ * Android supports the following calling use cases (with increasing level of complexity):
+ * <ul>
+ * <li>Implement the self-managed {@link android.telecom.ConnectionService} API - this is ideal
+ * for developers of standalone calling apps which do not wish to show their calls within the
+ * default phone app, and do not wish to have other calls shown in their user interface. Using
+ * a self-managed {@link android.telecom.ConnectionService} implementation within your
+ * standalone calling app helps you ensure that your app will interoperate not only with native
+ * telephony calling on the device, but also other standalone calling apps implementing this
+ * API. It also manages audio routing and focus for you.</li>
+ * <li>Implement the managed {@link android.telecom.ConnectionService} API - facilitates
+ * development of a calling solution that relies on the existing device phone application (see
+ * {@link android.telecom.TelecomManager#getDefaultDialerPackage()}) to provide the user
+ * interface for calls. An example might be a third party implementation of SIP calling, or a
+ * VOIP calling service. A {@link android.telecom.ConnectionService} alone provides only the
+ * means of connecting calls, but has no associated user interface.</li>
+ * <li>Implement the {@link android.telecom.InCallService} API - facilitates development of a
+ * replacement for the device's default Phone/Dialer app. The
+ * {@link android.telecom.InCallService} alone does not have any calling capability and consists
+ * of the user-interface side of calling only. An {@link android.telecom.InCallService} must
+ * handle all Calls the Telecom framework is aware of. It must not make assumptions about the
+ * nature of the calls (e.g. assuming calls are SIM-based telephony calls), and should not
+ * implement calling restrictions based on any one {@link android.telecom.ConnectionService}
+ * (e.g. it should not enforce Telephony restrictions for video calls).</li>
+ * <li>Implement both the {@link android.telecom.InCallService} and
+ * {@link android.telecom.ConnectionService} API - ideal if you wish to create your own
+ * {@link android.telecom.ConnectionService} based calling solution, complete with its own
+ * full user interface, while showing all other Android calls in the same user interface. Using
+ * this approach, you must still ensure that your {@link android.telecom.InCallService} makes
+ * no assumption about the source of the calls it displays. You must also ensure that your
+ * {@link android.telecom.ConnectionService} implementation can still function without the
+ * default phone app being set to your custom {@link android.telecom.InCallService}.</li>
+ * </ul>
+ */
+package android.telecom;
\ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 002c3bb..b58f8bc 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -102,6 +102,8 @@
void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
+ void setAudioRoute(String callId, int audioRoute, in Session.Info sessionInfo);
+
void onConnectionEvent(String callId, String event, in Bundle extras,
in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 5c412e7..6ca0bc5 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -249,4 +249,14 @@
* @see TelecomServiceImpl#createManageBlockedNumbersIntent
**/
Intent createManageBlockedNumbersIntent();
+
+ /**
+ * @see TelecomServiceImpl#isIncomingCallPermitted
+ */
+ boolean isIncomingCallPermitted(in PhoneAccountHandle phoneAccountHandle);
+
+ /**
+ * @see TelecomServiceImpl#isOutgoingCallPermitted
+ */
+ boolean isOutgoingCallPermitted(in PhoneAccountHandle phoneAccountHandle);
}