summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java96
-rw-r--r--services/accessibility/java/com/android/server/accessibility/TouchExplorer.java36
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java144
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java6
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java189
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java23
-rw-r--r--services/core/java/com/android/server/NetworkTimeUpdateService.java18
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java52
-rw-r--r--services/core/java/com/android/server/ShutdownActivity.java66
-rw-r--r--services/core/java/com/android/server/SystemConfig.java26
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java6
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java63
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityManagerService.java362
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java10
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java84
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java176
-rw-r--r--services/core/java/com/android/server/am/CoreSettingsObserver.java82
-rw-r--r--services/core/java/com/android/server/am/EventLogTags.logtags6
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java11
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java15
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java8
-rw-r--r--services/core/java/com/android/server/am/TaskPersister.java156
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java2
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java77
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java124
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java29
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java6
-rw-r--r--services/core/java/com/android/server/hdmi/ActiveSourceHandler.java5
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java10
-rw-r--r--services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java48
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java19
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java59
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java130
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java149
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiUtils.java13
-rw-r--r--services/core/java/com/android/server/hdmi/HotplugDetectionAction.java19
-rw-r--r--services/core/java/com/android/server/hdmi/NewDeviceAction.java6
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java2
-rw-r--r--services/core/java/com/android/server/hdmi/RoutingControlAction.java3
-rw-r--r--services/core/java/com/android/server/hdmi/SendKeyAction.java46
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java6
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java13
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java40
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java5
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java8
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java1
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java19
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/PreferredComponent.java4
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java3
-rw-r--r--services/core/java/com/android/server/telecom/TelecomLoaderService.java2
-rw-r--r--services/core/java/com/android/server/tv/TvInputHal.java5
-rw-r--r--services/core/java/com/android/server/tv/TvInputHardwareManager.java131
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java5
-rw-r--r--services/core/java/com/android/server/wm/CircularDisplayMask.java12
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java14
-rw-r--r--services/core/java/com/android/server/wm/StackTapPointerEventListener.java1
-rw-r--r--services/core/java/com/android/server/wm/Task.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java86
-rw-r--r--services/core/jni/com_android_server_tv_TvInputHal.cpp126
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java92
-rw-r--r--services/java/com/android/server/SystemServer.java3
64 files changed, 2110 insertions, 865 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 5ae26ef..93c65f3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -170,6 +170,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final Rect mTempRect = new Rect();
+ private final Rect mTempRect1 = new Rect();
+
private final Point mTempPoint = new Point();
private final PackageManager mPackageManager;
@@ -765,16 +767,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
/**
- * Gets the bounds of the active window.
+ * Gets the bounds of a window.
*
* @param outBounds The output to which to write the bounds.
*/
- boolean getActiveWindowBounds(Rect outBounds) {
- // TODO: This should be refactored to work with accessibility
- // focus in multiple windows.
+ boolean getWindowBounds(int windowId, Rect outBounds) {
IBinder token;
synchronized (mLock) {
- final int windowId = mSecurityPolicy.mActiveWindowId;
token = mGlobalWindowTokens.get(windowId);
if (token == null) {
token = getCurrentUserStateLocked().mWindowTokens.get(windowId);
@@ -2536,57 +2535,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
@Override
- public boolean computeClickPointInScreen(int accessibilityWindowId,
- long accessibilityNodeId, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
- throws RemoteException {
- final int resolvedWindowId;
- IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
- synchronized (mLock) {
- // We treat calls from a profile as if made by its parent as profiles
- // share the accessibility state of the parent. The call below
- // performs the current profile parent resolution.
- final int resolvedUserId = mSecurityPolicy
- .resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.USER_CURRENT);
- if (resolvedUserId != mCurrentUserId) {
- return false;
- }
- resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
- final boolean permissionGranted =
- mSecurityPolicy.canRetrieveWindowContentLocked(this);
- if (!permissionGranted) {
- return false;
- } else {
- connection = getConnectionLocked(resolvedWindowId);
- if (connection == null) {
- return false;
- }
- }
- if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
- resolvedWindowId, partialInteractiveRegion)) {
- partialInteractiveRegion = null;
- }
- }
- final int interrogatingPid = Binder.getCallingPid();
- final long identityToken = Binder.clearCallingIdentity();
- MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
- try {
- connection.computeClickPointInScreen(accessibilityNodeId, partialInteractiveRegion,
- interactionId, callback, interrogatingPid, interrogatingTid, spec);
- return true;
- } catch (RemoteException re) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "Error computeClickPointInScreen().");
- }
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- return false;
- }
-
- @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
synchronized (mLock) {
@@ -3239,38 +3187,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
synchronized (mLock) {
- Point point = mClient.computeClickPointInScreen(mConnectionId,
- focus.getWindowId(), focus.getSourceNodeId());
-
- if (point == null) {
+ Rect boundsInScreen = mTempRect;
+ focus.getBoundsInScreen(boundsInScreen);
+
+ // Clip to the window bounds.
+ Rect windowBounds = mTempRect1;
+ getWindowBounds(focus.getWindowId(), windowBounds);
+ boundsInScreen.intersect(windowBounds);
+ if (boundsInScreen.isEmpty()) {
return false;
}
+ // Apply magnification if needed.
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
if (spec != null && !spec.isNop()) {
- point.offset((int) -spec.offsetX, (int) -spec.offsetY);
- point.x = (int) (point.x * (1 / spec.scale));
- point.y = (int) (point.y * (1 / spec.scale));
+ boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY);
+ boundsInScreen.scale(1 / spec.scale);
}
- // Make sure the point is within the window.
- Rect windowBounds = mTempRect;
- getActiveWindowBounds(windowBounds);
- if (!windowBounds.contains(point.x, point.y)) {
- return false;
- }
-
- // Make sure the point is within the screen.
+ // Clip to the screen bounds.
Point screenSize = mTempPoint;
mDefaultDisplay.getRealSize(screenSize);
- if (point.x < 0 || point.x > screenSize.x
- || point.y < 0 || point.y > screenSize.y) {
+ boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y);
+ if (boundsInScreen.isEmpty()) {
return false;
}
- outPoint.set(point.x, point.y);
- return true;
+ outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY());
}
+
+ return true;
}
private AccessibilityNodeInfo getAccessibilityFocusNotLocked() {
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index b9ed89b..f18b5ef 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -77,6 +77,10 @@ class TouchExplorer implements EventStreamTransformation {
private static final int STATE_DELEGATING = 0x00000004;
private static final int STATE_GESTURE_DETECTING = 0x00000005;
+ private static final int CLICK_LOCATION_NONE = 0;
+ private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
+ private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
+
// The maximum of the cosine between the vectors of two moving
// pointers so they can be considered moving in the same direction.
private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
@@ -942,12 +946,16 @@ class TouchExplorer implements EventStreamTransformation {
*
* @param prototype The prototype from which to create the injected events.
* @param policyFlags The policy flags associated with the event.
+ * @param targetAccessibilityFocus Whether the event targets the accessibility focus.
*/
- private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) {
+ private void sendActionDownAndUp(MotionEvent prototype, int policyFlags,
+ boolean targetAccessibilityFocus) {
// Tap with the pointer that last explored.
final int pointerId = prototype.getPointerId(prototype.getActionIndex());
final int pointerIdBits = (1 << pointerId);
+ prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
+ prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
@@ -1155,7 +1163,8 @@ class TouchExplorer implements EventStreamTransformation {
final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
Point clickLocation = mTempPoint;
- if (!computeClickLocation(clickLocation)) {
+ final int result = computeClickLocation(clickLocation);
+ if (result == CLICK_LOCATION_NONE) {
return;
}
@@ -1171,7 +1180,8 @@ class TouchExplorer implements EventStreamTransformation {
secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0,
secondTapUp.getSource(), secondTapUp.getFlags());
- sendActionDownAndUp(event, policyFlags);
+ final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
+ sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus);
event.recycle();
}
@@ -1216,7 +1226,7 @@ class TouchExplorer implements EventStreamTransformation {
MAX_DRAGGING_ANGLE_COS);
}
- private boolean computeClickLocation(Point outLocation) {
+ private int computeClickLocation(Point outLocation) {
MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick();
if (lastExploreEvent != null) {
final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
@@ -1224,14 +1234,17 @@ class TouchExplorer implements EventStreamTransformation {
outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex);
if (!mAms.accessibilityFocusOnlyInActiveWindow()
|| mLastTouchedWindowId == mAms.getActiveWindowId()) {
- mAms.getAccessibilityFocusClickPointInScreen(outLocation);
+ if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
+ return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
+ } else {
+ return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
+ }
}
- return true;
}
if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
- return true;
+ return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
}
- return false;
+ return CLICK_LOCATION_NONE;
}
/**
@@ -1310,14 +1323,13 @@ class TouchExplorer implements EventStreamTransformation {
return;
}
- int clickLocationX;
- int clickLocationY;
-
final int pointerId = mEvent.getPointerId(mEvent.getActionIndex());
final int pointerIndex = mEvent.findPointerIndex(pointerId);
Point clickLocation = mTempPoint;
- if (!computeClickLocation(clickLocation)) {
+ final int result = computeClickLocation(clickLocation);
+
+ if (result == CLICK_LOCATION_NONE) {
return;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 6b8b5b1..c1e4994 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -130,6 +130,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
@@ -381,6 +382,7 @@ public class BackupManagerService {
// we're now good to go, so start the backup alarms
if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
+ scheduleNextFullBackupJob();
}
}
}
@@ -2695,6 +2697,84 @@ public class BackupManagerService {
}
}
+ // SHA-1 a byte array and return the result in hex
+ private String SHA1Checksum(byte[] input) {
+ final byte[] checksum;
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ checksum = md.digest(input);
+ } catch (NoSuchAlgorithmException e) {
+ Slog.e(TAG, "Unable to use SHA-1!");
+ return "00";
+ }
+
+ StringBuffer sb = new StringBuffer(checksum.length * 2);
+ for (int i = 0; i < checksum.length; i++) {
+ sb.append(Integer.toHexString(checksum[i]));
+ }
+ return sb.toString();
+ }
+
+ private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
+ throws IOException {
+ byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
+ UserHandle.USER_OWNER);
+ // has the widget state changed since last time?
+ final File widgetFile = new File(mStateDir, pkgName + "_widget");
+ final boolean priorStateExists = widgetFile.exists();
+
+ if (MORE_DEBUG) {
+ if (priorStateExists || widgetState != null) {
+ Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
+ + " prior=" + priorStateExists);
+ }
+ }
+
+ if (!priorStateExists && widgetState == null) {
+ // no prior state, no new state => nothing to do
+ return;
+ }
+
+ // if the new state is not null, we might need to compare checksums to
+ // determine whether to update the widget blob in the archive. If the
+ // widget state *is* null, we know a priori at this point that we simply
+ // need to commit a deletion for it.
+ String newChecksum = null;
+ if (widgetState != null) {
+ newChecksum = SHA1Checksum(widgetState);
+ if (priorStateExists) {
+ final String priorChecksum;
+ try (
+ FileInputStream fin = new FileInputStream(widgetFile);
+ DataInputStream in = new DataInputStream(fin)
+ ) {
+ priorChecksum = in.readUTF();
+ }
+ if (Objects.equals(newChecksum, priorChecksum)) {
+ // Same checksum => no state change => don't rewrite the widget data
+ return;
+ }
+ }
+ } // else widget state *became* empty, so we need to commit a deletion
+
+ BackupDataOutput out = new BackupDataOutput(fd);
+ if (widgetState != null) {
+ try (
+ FileOutputStream fout = new FileOutputStream(widgetFile);
+ DataOutputStream stateOut = new DataOutputStream(fout)
+ ) {
+ stateOut.writeUTF(newChecksum);
+ }
+
+ out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
+ out.writeEntityData(widgetState, widgetState.length);
+ } else {
+ // Widget state for this app has been removed; commit a deletion
+ out.writeEntityHeader(KEY_WIDGET_STATE, -1);
+ widgetFile.delete();
+ }
+ }
+
@Override
public void operationComplete() {
// Okay, the agent successfully reported back to us!
@@ -2733,17 +2813,7 @@ public class BackupManagerService {
}
// Piggyback the widget state payload, if any
- BackupDataOutput out = new BackupDataOutput(fd);
- byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
- UserHandle.USER_OWNER);
- if (widgetState != null) {
- out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
- out.writeEntityData(widgetState, widgetState.length);
- } else {
- // No widget state for this app, but push a 'delete' operation for it
- // in case they're trying to play games with the payload.
- out.writeEntityHeader(KEY_WIDGET_STATE, -1);
- }
+ writeWidgetPayloadIfAppropriate(fd, pkgName);
} catch (IOException e) {
// Hard disk error; recovery/failure policy TBD. For now roll back,
// but we may want to consider this a transport-level failure (i.e.
@@ -2786,21 +2856,30 @@ public class BackupManagerService {
addBackupTrace("finishing op on transport");
mStatus = mTransport.finishBackup();
addBackupTrace("finished: " + mStatus);
+ } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+ addBackupTrace("transport rejected package");
}
} else {
if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
addBackupTrace("no data to send");
}
- // After successful transport, delete the now-stale data
- // and juggle the files so that next time we supply the agent
- // with the new state file it just created.
if (mStatus == BackupTransport.TRANSPORT_OK) {
+ // After successful transport, delete the now-stale data
+ // and juggle the files so that next time we supply the agent
+ // with the new state file it just created.
mBackupDataName.delete();
mNewStateName.renameTo(mSavedStateName);
EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
logBackupComplete(pkgName);
+ } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+ // The transport has rejected backup of this specific package. Roll it
+ // back but proceed with running the rest of the queue.
+ mBackupDataName.delete();
+ mNewStateName.delete();
+ EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
} else {
+ // Actual transport-level failure to communicate the data to the backend
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
}
} catch (Exception e) {
@@ -2811,15 +2890,17 @@ public class BackupManagerService {
try { if (backupData != null) backupData.close(); } catch (IOException e) {}
}
- // If we encountered an error here it's a transport-level failure. That
- // means we need to halt everything and reschedule everything for next time.
final BackupState nextState;
- if (mStatus != BackupTransport.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK
+ || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+ // Success or single-package rejection. Proceed with the next app if any,
+ // otherwise we're done.
+ nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
+ } else {
+ // Any other error here indicates a transport-level failure. That means
+ // we need to halt everything and reschedule everything for next time.
revertAndEndBackup();
nextState = BackupState.FINAL;
- } else {
- // Success! Proceed with the next app if any, otherwise we're done.
- nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
}
executeNextState(nextState);
@@ -3773,6 +3854,16 @@ public class BackupManagerService {
PackageInfo currentPackage;
try {
+ if (!mEnabled || !mProvisioned) {
+ // Backups are globally disabled, so don't proceed.
+ if (DEBUG) {
+ Slog.i(TAG, "full backup requested but e=" + mEnabled
+ + " p=" + mProvisioned + "; ignoring");
+ }
+ mUpdateSchedule = false;
+ return;
+ }
+
IBackupTransport transport = getTransport(mCurrentTransport);
if (transport == null) {
Slog.w(TAG, "Transport not present; full data backup not performed");
@@ -4070,6 +4161,17 @@ public class BackupManagerService {
long now = System.currentTimeMillis();
FullBackupEntry entry = null;
+ if (!mEnabled || !mProvisioned) {
+ // Backups are globally disabled, so don't proceed. We also don't reschedule
+ // the job driving automatic backups; that job will be scheduled again when
+ // the user enables backup.
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+ + " p=" + mProvisioned + "; ignoring");
+ }
+ return false;
+ }
+
if (DEBUG_SCHEDULING) {
Slog.i(TAG, "Beginning scheduled full backup operation");
}
@@ -7963,8 +8065,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
// Record that we need a backup pass for the caller. Since multiple callers
// may share a uid, we need to note all candidates within that uid and schedule
// a backup pass for each of them.
- EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
-
if (targets == null) {
Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ " uid=" + Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index a9a756e..65a5c23 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1230,7 +1230,8 @@ class AlarmManagerService extends SystemService {
if (mAlarmBatches.size() > 0) {
final Batch firstWakeup = findFirstWakeupBatchLocked();
final Batch firstBatch = mAlarmBatches.get(0);
- if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
+ // always update the kernel alarms, as a backstop against missed wakeups
+ if (firstWakeup != null) {
mNextWakeup = firstWakeup.start;
setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
}
@@ -1243,7 +1244,8 @@ class AlarmManagerService extends SystemService {
nextNonWakeup = mNextNonWakeupDeliveryTime;
}
}
- if (nextNonWakeup != 0 && mNextNonWakeup != nextNonWakeup) {
+ // always update the kernel alarm, as a backstop against missed wakeups
+ if (nextNonWakeup != 0) {
mNextNonWakeup = nextNonWakeup;
setLocked(ELAPSED_REALTIME, nextNonWakeup);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 766e4c7..b72b29d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -832,6 +832,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
};
+ private Network[] getVpnUnderlyingNetworks(int uid) {
+ if (!mLockdownEnabled) {
+ int user = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(user);
+ if (vpn != null && vpn.appliesToUid(uid)) {
+ return vpn.getUnderlyingNetworks();
+ }
+ }
+ }
+ return null;
+ }
+
private NetworkState getUnfilteredActiveNetworkState(int uid) {
NetworkInfo info = null;
LinkProperties lp = null;
@@ -841,25 +854,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkAgentInfo nai = mNetworkForRequestId.get(mDefaultRequest.requestId);
- if (!mLockdownEnabled) {
- int user = UserHandle.getUserId(uid);
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(user);
- if (vpn != null && vpn.appliesToUid(uid)) {
- // getUnderlyingNetworks() returns:
- // null => the VPN didn't specify anything, so we use the default.
- // empty array => the VPN explicitly said "no default network".
- // non-empty array => the VPN specified one or more default networks; we use the
- // first one.
- Network[] networks = vpn.getUnderlyingNetworks();
- if (networks != null) {
- if (networks.length > 0) {
- nai = getNetworkAgentInfoForNetwork(networks[0]);
- } else {
- nai = null;
- }
- }
- }
+ final Network[] networks = getVpnUnderlyingNetworks(uid);
+ if (networks != null) {
+ // getUnderlyingNetworks() returns:
+ // null => there was no VPN, or the VPN didn't specify anything, so we use the default.
+ // empty array => the VPN explicitly said "no default network".
+ // non-empty array => the VPN specified one or more default networks; we use the
+ // first one.
+ if (networks.length > 0) {
+ nai = getNetworkAgentInfoForNetwork(networks[0]);
+ } else {
+ nai = null;
}
}
@@ -907,7 +912,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
- if (DBG) log("returning Blocked NetworkInfo");
+ if (DBG) {
+ log("returning Blocked NetworkInfo for ifname=" +
+ lp.getInterfaceName() + ", uid=" + uid);
+ }
}
if (info != null && mLockdownTracker != null) {
info = mLockdownTracker.augmentNetworkInfo(info);
@@ -987,6 +995,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
public NetworkInfo getNetworkInfo(int networkType) {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
+ if (getVpnUnderlyingNetworks(uid) != null) {
+ // A VPN is active, so we may need to return one of its underlying networks. This
+ // information is not available in LegacyTypeTracker, so we have to get it from
+ // getUnfilteredActiveNetworkState.
+ NetworkState state = getUnfilteredActiveNetworkState(uid);
+ if (state.networkInfo != null && state.networkInfo.getType() == networkType) {
+ return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
+ }
+ }
NetworkState state = getFilteredNetworkState(networkType, uid);
return state.networkInfo;
}
@@ -2069,6 +2086,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
// may trigger a re-evaluation of the network.
private void unlinger(NetworkAgentInfo nai) {
if (VDBG) log("Canceling linger of " + nai.name());
+ // If network has never been validated, it cannot have been lingered, so don't bother
+ // needlessly triggering a re-evaluation.
+ if (!nai.everValidated) return;
nai.networkLingered.clear();
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
}
@@ -2268,6 +2288,46 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ // Is nai unneeded by all NetworkRequests (and should be disconnected)?
+ // For validated Networks this is simply whether it is satsifying any NetworkRequests.
+ // For unvalidated Networks this is whether it is satsifying any NetworkRequests or
+ // were it to become validated, would it have a chance of satisfying any NetworkRequests.
+ private boolean unneeded(NetworkAgentInfo nai) {
+ if (!nai.created || nai.isVPN()) return false;
+ boolean unneeded = true;
+ if (nai.everValidated) {
+ for (int i = 0; i < nai.networkRequests.size() && unneeded; i++) {
+ final NetworkRequest nr = nai.networkRequests.valueAt(i);
+ try {
+ if (isRequest(nr)) unneeded = false;
+ } catch (Exception e) {
+ loge("Request " + nr + " not found in mNetworkRequests.");
+ loge(" it came from request list of " + nai.name());
+ }
+ }
+ } else {
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ // If this Network is already the highest scoring Network for a request, or if
+ // there is hope for it to become one if it validated, then it is needed.
+ if (nri.isRequest && nai.satisfies(nri.request) &&
+ (nai.networkRequests.get(nri.request.requestId) != null ||
+ // Note that this catches two important cases:
+ // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+ // is currently satisfying the request. This is desirable when
+ // cellular ends up validating but WiFi does not.
+ // 2. Unvalidated WiFi will not be reaped when validated cellular
+ // is currently satsifying the request. This is desirable when
+ // WiFi ends up validating and out scoring cellular.
+ mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
+ nai.getCurrentScoreAsValidated())) {
+ unneeded = false;
+ break;
+ }
+ }
+ }
+ return unneeded;
+ }
+
private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
NetworkRequestInfo nri = mNetworkRequests.get(request);
if (nri != null) {
@@ -2281,6 +2341,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nri.isRequest) {
// Find all networks that are satisfying this request and remove the request
// from their request lists.
+ // TODO - it's my understanding that for a request there is only a single
+ // network satisfying it, so this loop is wasteful
+ boolean wasKept = false;
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
if (nai.networkRequests.get(nri.request.requestId) != null) {
nai.networkRequests.remove(nri.request.requestId);
@@ -2289,29 +2352,42 @@ public class ConnectivityService extends IConnectivityManager.Stub
", leaving " + nai.networkRequests.size() +
" requests.");
}
- // check if has any requests remaining and if not,
- // disconnect (unless it's a VPN).
- boolean keep = nai.isVPN();
- for (int i = 0; i < nai.networkRequests.size() && !keep; i++) {
- NetworkRequest r = nai.networkRequests.valueAt(i);
- if (isRequest(r)) keep = true;
- }
- if (!keep) {
+ if (unneeded(nai)) {
if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
- nai.asyncChannel.disconnect();
+ teardownUnneededNetwork(nai);
+ } else {
+ // suspect there should only be one pass through here
+ // but if any were kept do the check below
+ wasKept |= true;
}
}
}
+ NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ if (nai != null) {
+ mNetworkForRequestId.remove(nri.request.requestId);
+ }
// Maintain the illusion. When this request arrived, we might have pretended
// that a network connected to serve it, even though the network was already
// connected. Now that this request has gone away, we might have to pretend
// that the network disconnected. LegacyTypeTracker will generate that
// phantom disconnect for this type.
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
- if (nai != null) {
- mNetworkForRequestId.remove(nri.request.requestId);
- if (nri.request.legacyType != TYPE_NONE) {
+ if (nri.request.legacyType != TYPE_NONE && nai != null) {
+ boolean doRemove = true;
+ if (wasKept) {
+ // check if any of the remaining requests for this network are for the
+ // same legacy type - if so, don't remove the nai
+ for (int i = 0; i < nai.networkRequests.size(); i++) {
+ NetworkRequest otherRequest = nai.networkRequests.valueAt(i);
+ if (otherRequest.legacyType == nri.request.legacyType &&
+ isRequest(otherRequest)) {
+ if (DBG) log(" still have other legacy request - leaving");
+ doRemove = false;
+ }
+ }
+ }
+
+ if (doRemove) {
mLegacyTypeTracker.remove(nri.request.legacyType, nai);
}
}
@@ -2560,9 +2636,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 100 percent is full good, 0 is full bad.
public void reportInetCondition(int networkType, int percentage) {
- if (percentage > 50) return; // don't handle good network reports
NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- if (nai != null) reportBadNetwork(nai.network);
+ if (nai == null) return;
+ boolean isGood = percentage > 50;
+ // Revalidate if the app report does not match our current validated state.
+ if (isGood != nai.lastValidated) {
+ // Make the message logged by reportBadNetwork below less confusing.
+ if (DBG && isGood) log("reportInetCondition: type=" + networkType + " ok, revalidate");
+ reportBadNetwork(nai.network);
+ }
}
public void reportBadNetwork(Network network) {
@@ -4034,6 +4116,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
if (DBG) log(" accepting network in place of null");
}
+ unlinger(newNetwork);
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
newNetwork.addRequest(nri.request);
keep = true;
@@ -4052,19 +4135,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
// Linger any networks that are no longer needed.
for (NetworkAgentInfo nai : affectedNetworks) {
- boolean teardown = !nai.isVPN() && nai.everValidated;
- for (int i = 0; i < nai.networkRequests.size() && teardown; i++) {
- NetworkRequest nr = nai.networkRequests.valueAt(i);
- try {
- if (isRequest(nr)) {
- teardown = false;
- }
- } catch (Exception e) {
- loge("Request " + nr + " not found in mNetworkRequests.");
- loge(" it came from request list of " + nai.name());
- }
- }
- if (teardown) {
+ if (nai.everValidated && unneeded(nai)) {
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING);
} else {
@@ -4164,27 +4235,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (!nai.created || nai.everValidated || nai.isVPN()) continue;
- boolean reap = true;
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- // If this Network is already the highest scoring Network for a request, or if
- // there is hope for it to become one if it validated, then don't reap it.
- if (nri.isRequest && nai.satisfies(nri.request) &&
- (nai.networkRequests.get(nri.request.requestId) != null ||
- // Note that this catches two important cases:
- // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
- // is currently satisfying the request. This is desirable when
- // cellular ends up validating but WiFi does not.
- // 2. Unvalidated WiFi will not be reaped when validated cellular
- // is currently satsifying the request. This is desirable when
- // WiFi ends up validating and out scoring cellular.
- mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
- nai.getCurrentScoreAsValidated())) {
- reap = false;
- break;
- }
- }
- if (reap) {
+ if (!nai.everValidated && unneeded(nai)) {
if (DBG) log("Reaping " + nai.name());
teardownUnneededNetwork(nai);
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 891f572..170c4e2 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1133,7 +1133,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public void setIpForwardingEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("ipfwd", enable ? "enable" : "disable");
+ mConnector.execute("ipfwd", enable ? "enable" : "disable", "tethering");
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
@@ -1259,6 +1259,27 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return filtered;
}
+ private void modifyInterfaceForward(boolean add, String fromIface, String toIface) {
+ final Command cmd = new Command("ipfwd", add ? "add" : "remove", fromIface, toIface);
+ try {
+ mConnector.execute(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void startInterfaceForwarding(String fromIface, String toIface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ modifyInterfaceForward(true, fromIface, toIface);
+ }
+
+ @Override
+ public void stopInterfaceForwarding(String fromIface, String toIface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ modifyInterfaceForward(false, fromIface, toIface);
+ }
+
private void modifyNat(String action, String internalInterface, String externalInterface)
throws SocketException {
final Command cmd = new Command("nat", action, internalInterface, externalInterface);
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index fddb54e..d6abce9 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -55,7 +55,7 @@ public class NetworkTimeUpdateService {
private static final int EVENT_AUTO_TIME_CHANGED = 1;
private static final int EVENT_POLL_NETWORK_TIME = 2;
- private static final int EVENT_NETWORK_CONNECTED = 3;
+ private static final int EVENT_NETWORK_CHANGED = 3;
private static final String ACTION_POLL =
"com.android.server.NetworkTimeUpdateService.action.POLL";
@@ -248,18 +248,8 @@ public class NetworkTimeUpdateService {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
- // There is connectivity
- final ConnectivityManager connManager = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- final NetworkInfo netInfo = connManager.getActiveNetworkInfo();
- if (netInfo != null) {
- // Verify that it's a WIFI connection
- if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
- (netInfo.getType() == ConnectivityManager.TYPE_WIFI ||
- netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) ) {
- mHandler.obtainMessage(EVENT_NETWORK_CONNECTED).sendToTarget();
- }
- }
+ // Don't bother checking if we have connectivity, NtpTrustedTime does that for us.
+ mHandler.obtainMessage(EVENT_NETWORK_CHANGED).sendToTarget();
}
}
};
@@ -276,7 +266,7 @@ public class NetworkTimeUpdateService {
switch (msg.what) {
case EVENT_AUTO_TIME_CHANGED:
case EVENT_POLL_NETWORK_TIME:
- case EVENT_NETWORK_CONNECTED:
+ case EVENT_NETWORK_CHANGED:
onPollNetworkTime(msg.what);
break;
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 9d4cd99..e5ace1b 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -103,9 +103,19 @@ public class PersistentDataBlockService extends SystemService {
@Override
public void onStart() {
enforceChecksumValidity();
+ formatIfOemUnlockEnabled();
publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
}
+ private void formatIfOemUnlockEnabled() {
+ if (doGetOemUnlockEnabled()) {
+ synchronized (mLock) {
+ formatPartitionLocked();
+ doSetOemUnlockEnabledLocked(true);
+ }
+ }
+ }
+
private void enforceOemUnlockPermission() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OEM_UNLOCK_STATE,
@@ -285,6 +295,28 @@ public class PersistentDataBlockService extends SystemService {
}
}
+ private boolean doGetOemUnlockEnabled() {
+ DataInputStream inputStream;
+ try {
+ inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available");
+ return false;
+ }
+
+ try {
+ synchronized (mLock) {
+ inputStream.skip(getBlockDeviceSize() - 1);
+ return inputStream.readByte() != 0;
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "unable to access persistent partition", e);
+ return false;
+ } finally {
+ IoUtils.closeQuietly(inputStream);
+ }
+ }
+
private native long nativeGetBlockDeviceSize(String path);
private native int nativeWipe(String path);
@@ -410,25 +442,7 @@ public class PersistentDataBlockService extends SystemService {
@Override
public boolean getOemUnlockEnabled() {
enforceOemUnlockPermission();
- DataInputStream inputStream;
- try {
- inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "partition not available");
- return false;
- }
-
- try {
- synchronized (mLock) {
- inputStream.skip(getBlockDeviceSize() - 1);
- return inputStream.readByte() != 0;
- }
- } catch (IOException e) {
- Slog.e(TAG, "unable to access persistent partition", e);
- return false;
- } finally {
- IoUtils.closeQuietly(inputStream);
- }
+ return doGetOemUnlockEnabled();
}
@Override
diff --git a/services/core/java/com/android/server/ShutdownActivity.java b/services/core/java/com/android/server/ShutdownActivity.java
deleted file mode 100644
index 56172ed..0000000
--- a/services/core/java/com/android/server/ShutdownActivity.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IPowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Slog;
-
-public class ShutdownActivity extends Activity {
-
- private static final String TAG = "ShutdownActivity";
- private boolean mReboot;
- private boolean mConfirm;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Intent intent = getIntent();
- mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());
- mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
- Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
-
- Thread thr = new Thread("ShutdownActivity") {
- @Override
- public void run() {
- IPowerManager pm = IPowerManager.Stub.asInterface(
- ServiceManager.getService(Context.POWER_SERVICE));
- try {
- if (mReboot) {
- pm.reboot(mConfirm, null, false);
- } else {
- pm.shutdown(mConfirm, false);
- }
- } catch (RemoteException e) {
- }
- }
- };
- thr.start();
- finish();
- // Wait for us to tell the power manager to shutdown.
- try {
- thr.join();
- } catch (InterruptedException e) {
- }
- }
-}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index eb89f21..6ad128c 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -205,8 +205,8 @@ public class SystemConfig {
}
if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
- throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
- ", expected 'permissions' or 'config'");
+ throw new XmlPullParserException("Unexpected start tag in " + permFile
+ + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
}
while (true) {
@@ -222,7 +222,7 @@ public class SystemConfig {
int gid = android.os.Process.getGidForName(gidStr);
mGlobalGids = appendInt(mGlobalGids, gid);
} else {
- Slog.w(TAG, "<group> without gid at "
+ Slog.w(TAG, "<group> without gid in " + permFile + " at "
+ parser.getPositionDescription());
}
@@ -231,7 +231,7 @@ public class SystemConfig {
} else if ("permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
- Slog.w(TAG, "<permission> without name at "
+ Slog.w(TAG, "<permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
@@ -242,14 +242,14 @@ public class SystemConfig {
} else if ("assign-permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
- Slog.w(TAG, "<assign-permission> without name at "
+ Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
- Slog.w(TAG, "<assign-permission> without uid at "
+ Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
@@ -257,7 +257,7 @@ public class SystemConfig {
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""
- + uidStr + "\" at "
+ + uidStr + " in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
@@ -275,10 +275,10 @@ public class SystemConfig {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
- Slog.w(TAG, "<library> without name at "
+ Slog.w(TAG, "<library> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (lfile == null) {
- Slog.w(TAG, "<library> without file at "
+ Slog.w(TAG, "<library> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
@@ -297,7 +297,7 @@ public class SystemConfig {
allowed = !"true".equals(notLowRam);
}
if (fname == null) {
- Slog.w(TAG, "<feature> without name at "
+ Slog.w(TAG, "<feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (allowed) {
//Log.i(TAG, "Got feature " + fname);
@@ -311,7 +311,7 @@ public class SystemConfig {
} else if ("unavailable-feature".equals(name)) {
String fname = parser.getAttributeValue(null, "name");
if (fname == null) {
- Slog.w(TAG, "<unavailable-feature> without name at "
+ Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mUnavailableFeatures.add(fname);
@@ -322,7 +322,7 @@ public class SystemConfig {
} else if ("allow-in-power-save".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
- Slog.w(TAG, "<allow-in-power-save> without package at "
+ Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mAllowInPowerSave.add(pkgname);
@@ -333,7 +333,7 @@ public class SystemConfig {
} else if ("fixed-ime-app".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
- Slog.w(TAG, "<fixed-ime-app> without package at "
+ Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mFixedImeApps.add(pkgname);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 72c2eaf..4fbf23b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -259,7 +259,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ newDefaultSubIdObj + " newDefaultPhoneId=" + newDefaultPhoneId);
}
- if(validatePhoneId(newDefaultPhoneId) && (newDefaultSubIdObj.equals(mDefaultSubId)
+ if(validatePhoneId(newDefaultPhoneId) && (!newDefaultSubIdObj.equals(mDefaultSubId)
|| (newDefaultPhoneId != mDefaultPhoneId))) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_DEFAULT_SUB,
newDefaultPhoneId, 0, newDefaultSubIdObj));
@@ -338,7 +338,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
- public void registerOnSubscriptionsChangedListener(String pkgForDebug,
+ public void addOnSubscriptionsChangedListener(String pkgForDebug,
IOnSubscriptionsChangedListener callback) {
int callerUid = UserHandle.getCallingUserId();
int myUid = UserHandle.myUserId();
@@ -393,7 +393,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
- public void unregisterOnSubscriptionsChangedListener(String pkgForDebug,
+ public void removeOnSubscriptionsChangedListener(String pkgForDebug,
IOnSubscriptionsChangedListener callback) {
if (DBG) log("listen oscl: Unregister");
remove(callback.asBinder());
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f02a815..aefbf60 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -19,11 +19,13 @@ package com.android.server.am;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import android.app.ActivityThread;
import android.os.Build;
import android.os.DeadObjectException;
import android.os.Handler;
@@ -34,6 +36,7 @@ import android.util.ArraySet;
import com.android.internal.app.ProcessStats;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.TransferPipe;
+import com.android.internal.util.FastPrintWriter;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.ActivityManagerService.NeededUriGrants;
@@ -141,6 +144,19 @@ public final class ActiveServices {
final ArrayList<ServiceRecord> mDestroyingServices
= new ArrayList<ServiceRecord>();
+ /** Amount of time to allow a last ANR message to exist before freeing the memory. */
+ static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours
+
+ String mLastAnrDump;
+
+ final Runnable mLastAnrDumpClearer = new Runnable() {
+ @Override public void run() {
+ synchronized (mAm) {
+ mLastAnrDump = null;
+ }
+ }
+ };
+
static final class DelayingProcess extends ArrayList<ServiceRecord> {
long timeoout;
}
@@ -751,6 +767,9 @@ public final class ActiveServices {
}
}
+ mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
+ s.appInfo.uid, s.name, s.processName);
+
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
@@ -1445,10 +1464,10 @@ public final class ActiveServices {
boolean created = false;
try {
- String nameTerm;
- int lastPeriod = r.shortName.lastIndexOf('.');
- nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
if (LOG_SERVICE_START_STOP) {
+ String nameTerm;
+ int lastPeriod = r.shortName.lastIndexOf('.');
+ nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
EventLogTags.writeAmCreateService(
r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
}
@@ -1635,6 +1654,7 @@ public final class ActiveServices {
}
if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
+ r.destroyTime = SystemClock.uptimeMillis();
if (LOG_SERVICE_START_STOP) {
EventLogTags.writeAmDestroyService(
r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1);
@@ -1673,6 +1693,7 @@ public final class ActiveServices {
try {
bumpServiceExecutingLocked(r, false, "destroy");
mDestroyingServices.add(r);
+ r.destroying = true;
mAm.updateOomAdjLocked(r.app);
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
@@ -1746,6 +1767,8 @@ public final class ActiveServices {
}
}
+ mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);
+
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
}
@@ -1792,7 +1815,7 @@ public final class ActiveServices {
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
- if (type == 1) {
+ if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart = true;
@@ -1841,6 +1864,20 @@ public final class ActiveServices {
if (res == Service.START_STICKY_COMPATIBILITY) {
r.callStart = false;
}
+ } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
+ // This is the final call from destroying the service... we should
+ // actually be getting rid of the service at this point. Do some
+ // validation of its state, and ensure it will be fully removed.
+ if (!inDestroying) {
+ // Not sure what else to do with this... if it is not actually in the
+ // destroying list, we don't need to make sure to remove it from it.
+ Slog.wtfStack(TAG, "Service done with onDestroy, but not inDestroying: "
+ + r);
+ } else if (r.executeNesting != 1) {
+ Slog.wtfStack(TAG, "Service done with onDestroy, but executeNesting="
+ + r.executeNesting + ": " + r);
+ r.executeNesting = 1;
+ }
}
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
@@ -2367,7 +2404,8 @@ public final class ActiveServices {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
- long maxTime = SystemClock.uptimeMillis() -
+ final long now = SystemClock.uptimeMillis();
+ final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
@@ -2383,7 +2421,15 @@ public final class ActiveServices {
}
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
- anrMessage = "Executing service " + timeout.shortName;
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new FastPrintWriter(sw, false, 1024);
+ pw.println(timeout);
+ timeout.dump(pw, " ");
+ pw.close();
+ mLastAnrDump = sw.toString();
+ mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
+ mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
+ anrMessage = "executing service " + timeout.shortName;
} else {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
@@ -2423,6 +2469,11 @@ public final class ActiveServices {
pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
try {
+ if (mLastAnrDump != null) {
+ pw.println(" Last ANR service:");
+ pw.print(mLastAnrDump);
+ pw.println();
+ }
int[] users = mAm.getUsersLocked();
for (int user : users) {
ServiceMap smap = getServiceMap(user);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c0fc890..e8f3757 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -749,6 +749,38 @@ public final class ActivityManagerService extends ActivityManagerNative
final ActiveServices mServices;
+ final static class Association {
+ final int mSourceUid;
+ final String mSourceProcess;
+ final int mTargetUid;
+ final ComponentName mTargetComponent;
+ final String mTargetProcess;
+
+ int mCount;
+ long mTime;
+
+ int mNesting;
+ long mStartTime;
+
+ Association(int sourceUid, String sourceProcess, int targetUid,
+ ComponentName targetComponent, String targetProcess) {
+ mSourceUid = sourceUid;
+ mSourceProcess = sourceProcess;
+ mTargetUid = targetUid;
+ mTargetComponent = targetComponent;
+ mTargetProcess = targetProcess;
+ }
+ }
+
+ /**
+ * When service association tracking is enabled, this is all of the associations we
+ * have seen. Mapping is target uid -> target component -> source uid -> source process name
+ * -> association data.
+ */
+ final SparseArray<ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>>>
+ mAssociations = new SparseArray<>();
+ boolean mTrackingAssociations;
+
/**
* Backup/restore process management
*/
@@ -2071,8 +2103,10 @@ public final class ActivityManagerService extends ActivityManagerNative
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
+ mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
+
mConfiguration.setToDefaults();
- mConfiguration.setLocale(Locale.getDefault());
+ mConfiguration.locale = Locale.getDefault();
mConfigurationSeq = mConfiguration.seq = 1;
mProcessCpuTracker.init();
@@ -2324,7 +2358,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return mAppBindArgs;
}
- final void setFocusedActivityLocked(ActivityRecord r) {
+ final void setFocusedActivityLocked(ActivityRecord r, String reason) {
if (mFocusedActivity != r) {
if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivityLocked: r=" + r);
mFocusedActivity = r;
@@ -2333,12 +2367,14 @@ public final class ActivityManagerService extends ActivityManagerNative
} else {
finishRunningVoiceLocked();
}
- mStackSupervisor.setFocusedStack(r);
+ mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity");
if (r != null) {
mWindowManager.setFocusedApp(r.appToken, true);
}
applyUpdateLockStateLocked(r);
}
+ EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY, mCurrentUserId,
+ mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName);
}
final void clearFocusedActivity(ActivityRecord r) {
@@ -2355,7 +2391,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (stack != null) {
ActivityRecord r = stack.topRunningActivityLocked(null);
if (r != null) {
- setFocusedActivityLocked(r);
+ setFocusedActivityLocked(r, "setFocusedStack");
}
}
}
@@ -2399,7 +2435,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mHandler.sendMessage(msg);
}
- private final int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
+ private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
@@ -2964,6 +3000,10 @@ public final class ActivityManagerService extends ActivityManagerNative
instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
}
+ app.gids = gids;
+ app.requiredAbi = requiredAbi;
+ app.instructionSet = instructionSet;
+
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
boolean isActivityProcess = (entryPoint == null);
@@ -2994,7 +3034,11 @@ public final class ActivityManagerService extends ActivityManagerNative
StringBuilder buf = mStringBuilder;
buf.setLength(0);
buf.append("Start proc ");
+ buf.append(startResult.pid);
+ buf.append(':');
buf.append(app.processName);
+ buf.append('/');
+ UserHandle.formatUid(buf, uid);
if (!isActivityProcess) {
buf.append(" [");
buf.append(entryPoint);
@@ -3006,23 +3050,6 @@ public final class ActivityManagerService extends ActivityManagerNative
buf.append(" ");
buf.append(hostingNameStr);
}
- buf.append(": pid=");
- buf.append(startResult.pid);
- buf.append(" uid=");
- buf.append(uid);
- buf.append(" gids={");
- if (gids != null) {
- for (int gi=0; gi<gids.length; gi++) {
- if (gi != 0) buf.append(", ");
- buf.append(gids[gi]);
-
- }
- }
- buf.append("}");
- if (requiredAbi != null) {
- buf.append(" abi=");
- buf.append(requiredAbi);
- }
Slog.i(TAG, buf.toString());
app.setPid(startResult.pid);
app.usingWrapper = startResult.usingWrapper;
@@ -3082,7 +3109,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return intent;
}
- boolean startHomeActivityLocked(int userId) {
+ boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -3104,7 +3131,7 @@ public final class ActivityManagerService extends ActivityManagerNative
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- mStackSupervisor.startHomeActivity(intent, aInfo);
+ mStackSupervisor.startHomeActivity(intent, aInfo, reason);
}
}
@@ -3619,6 +3646,10 @@ public final class ActivityManagerService extends ActivityManagerNative
if (task == null) {
throw new IllegalArgumentException("Task " + taskId + " not found.");
}
+ if (task.getRootActivity() != null) {
+ moveTaskToFrontLocked(task.taskId, 0, null);
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
callingUid = task.mCallingUid;
callingPackage = task.mCallingPackage;
intent = task.intent;
@@ -5289,7 +5320,6 @@ public final class ActivityManagerService extends ActivityManagerNative
int callingPid = Binder.getCallingPid();
if (callingPid == Process.myPid()) {
// Yeah, um, no.
- Slog.w(TAG, "Can't addPackageDependency on system process");
return;
}
ProcessRecord proc;
@@ -6173,7 +6203,7 @@ public final class ActivityManagerService extends ActivityManagerNative
startProcessLocked(procs.get(ip), "on-hold", null);
}
}
-
+
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
// Start looking for apps that are abusing wake locks.
Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
@@ -6313,7 +6343,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- stack.activityDestroyedLocked(token);
+ stack.activityDestroyedLocked(token, "activityDestroyed");
}
}
}
@@ -6424,7 +6454,7 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new IllegalArgumentException("File descriptors passed in options");
}
}
-
+
synchronized(this) {
int callingUid = Binder.getCallingUid();
int origUserId = userId;
@@ -6454,7 +6484,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return getIntentSenderLocked(type, packageName, callingUid, userId,
token, resultWho, requestCode, intents, resolvedTypes, flags, options);
-
+
} catch (RemoteException e) {
throw new SecurityException(e);
}
@@ -8182,17 +8212,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- private TaskRecord taskForIdLocked(int id) {
- final TaskRecord task = recentTaskForIdLocked(id);
- if (task != null) {
- return task;
- }
-
- // Don't give up. Sometimes it just hasn't made it to recents yet.
- return mStackSupervisor.anyTaskForIdLocked(id);
- }
-
- private TaskRecord recentTaskForIdLocked(int id) {
+ TaskRecord recentTaskForIdLocked(int id) {
final int N = mRecentTasks.size();
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
@@ -8208,7 +8228,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
"getTaskThumbnail()");
- TaskRecord tr = recentTaskForIdLocked(id);
+ TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id);
if (tr != null) {
return tr.getTaskThumbnailLocked();
}
@@ -8453,7 +8473,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* @return Returns true if the given task was found and removed.
*/
private boolean removeTaskByIdLocked(int taskId, boolean killProcess) {
- TaskRecord tr = taskForIdLocked(taskId);
+ TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId);
if (tr != null) {
tr.removeTaskActivitiesLocked();
cleanUpRemovedTaskLocked(tr, killProcess);
@@ -8516,7 +8536,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (prev != null && prev.isRecentsActivity()) {
task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE);
}
- mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options);
+ mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront");
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -8529,7 +8549,7 @@ public final class ActivityManagerService extends ActivityManagerNative
"moveTaskToBack()");
synchronized(this) {
- TaskRecord tr = taskForIdLocked(taskId);
+ TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId);
if (tr != null) {
if (tr == mStackSupervisor.mLockTaskModeTask) {
mStackSupervisor.showLockTaskToast();
@@ -8545,7 +8565,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final long origId = Binder.clearCallingIdentity();
try {
- stack.moveTaskToBackLocked(taskId, null);
+ stack.moveTaskToBackLocked(taskId);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -8575,7 +8595,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mStackSupervisor.showLockTaskToast();
return false;
}
- return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId, null);
+ return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -8667,7 +8687,7 @@ public final class ActivityManagerService extends ActivityManagerNative
try {
if (DEBUG_STACK) Slog.d(TAG, "moveTaskToStack: moving task=" + taskId + " to stackId="
+ stackId + " toTop=" + toTop);
- mStackSupervisor.moveTaskToStack(taskId, stackId, toTop);
+ mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -8721,7 +8741,7 @@ public final class ActivityManagerService extends ActivityManagerNative
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- TaskRecord tr = taskForIdLocked(taskId);
+ TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId);
return tr != null && tr.stack != null && tr.stack.isHomeStack();
}
} finally {
@@ -8773,7 +8793,8 @@ public final class ActivityManagerService extends ActivityManagerNative
|| (task != mStackSupervisor.getFocusedStack().topTask()))) {
throw new IllegalArgumentException("Invalid task, not in foreground");
}
- mStackSupervisor.setLockTaskModeLocked(task, !isSystemInitiated);
+ mStackSupervisor.setLockTaskModeLocked(task, !isSystemInitiated,
+ "startLockTask");
}
}
} finally {
@@ -8858,7 +8879,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Log.d(TAG, "stopLockTaskMode");
// Stop lock task
synchronized (this) {
- mStackSupervisor.setLockTaskModeLocked(null, false);
+ mStackSupervisor.setLockTaskModeLocked(null, false, "stopLockTask");
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -9088,6 +9109,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
cpr.connections.add(conn);
r.conProviders.add(conn);
+ startAssociationLocked(r.uid, r.processName, cpr.uid, cpr.name, cpr.info.processName);
return conn;
}
cpr.addExternalProcessHandleLocked(externalProcessToken);
@@ -9111,6 +9133,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (conn.stableCount == 0 && conn.unstableCount == 0) {
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
+ stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name);
return true;
}
return false;
@@ -11177,9 +11200,6 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mRecentTasks == null) {
mRecentTasks = mTaskPersister.restoreTasksLocked();
mTaskPersister.restoreTasksFromOtherDeviceLocked();
- if (!mRecentTasks.isEmpty()) {
- mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
- }
cleanupRecentTasksLocked(UserHandle.USER_ALL);
mTaskPersister.startPersisting();
}
@@ -11196,8 +11216,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mDidUpdate = true;
}
writeLastDonePreBootReceivers(doneReceivers);
- showBootMessage(mContext.getText(
- R.string.android_upgrading_complete),
+ showBootMessage(mContext.getText(R.string.android_upgrading_complete),
false);
systemReady(goingCallback);
}
@@ -11320,7 +11339,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Start up initial activity.
mBooting = true;
- startHomeActivityLocked(mCurrentUserId);
+ startHomeActivityLocked(mCurrentUserId, "systemReady");
try {
if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
@@ -12330,9 +12349,18 @@ public final class ActivityManagerService extends ActivityManagerNative
dumpAll = true;
} else if ("-c".equals(opt)) {
dumpClient = true;
+ } else if ("-p".equals(opt)) {
+ if (opti < args.length) {
+ dumpPackage = args[opti];
+ opti++;
+ } else {
+ pw.println("Error: -p option requires package argument");
+ return;
+ }
+ dumpClient = true;
} else if ("-h".equals(opt)) {
pw.println("Activity manager dump options:");
- pw.println(" [-a] [-c] [-h] [cmd] ...");
+ pw.println(" [-a] [-c] [-p package] [-h] [cmd] ...");
pw.println(" cmd may be one of:");
pw.println(" a[ctivities]: activity stack state");
pw.println(" r[recents]: recent activities state");
@@ -12343,17 +12371,21 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" prov[iders] [COMP_SPEC ...]: content provider state");
pw.println(" provider [COMP_SPEC]: provider client-side state");
pw.println(" s[ervices] [COMP_SPEC ...]: service state");
+ pw.println(" as[sociations]: tracked app associations");
pw.println(" service [COMP_SPEC]: service client-side state");
pw.println(" package [PACKAGE_NAME]: all state related to given package");
pw.println(" all: dump all activities");
pw.println(" top: dump the top activity");
pw.println(" write: write all pending state to storage");
+ pw.println(" track-associations: enable association tracking");
+ pw.println(" untrack-associations: disable and clear association tracking");
pw.println(" cmd may also be a COMP_SPEC to dump activities.");
pw.println(" COMP_SPEC may be a component name (com.foo/.myApp),");
pw.println(" a partial substring in a component name, a");
pw.println(" hex object identifier.");
pw.println(" -a: include all available server state.");
pw.println(" -c: include client state.");
+ pw.println(" -p: limit output to given package.");
return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
@@ -12368,11 +12400,11 @@ public final class ActivityManagerService extends ActivityManagerNative
opti++;
if ("activities".equals(cmd) || "a".equals(cmd)) {
synchronized (this) {
- dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, null);
+ dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
}
} else if ("recents".equals(cmd) || "r".equals(cmd)) {
synchronized (this) {
- dumpRecentsLocked(fd, pw, args, opti, true, null);
+ dumpRecentsLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
String[] newArgs;
@@ -12381,14 +12413,14 @@ public final class ActivityManagerService extends ActivityManagerNative
name = null;
newArgs = EMPTY_STRING_ARRAY;
} else {
- name = args[opti];
+ dumpPackage = args[opti];
opti++;
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
}
synchronized (this) {
- dumpBroadcastsLocked(fd, pw, args, opti, true, name);
+ dumpBroadcastsLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("intents".equals(cmd) || "i".equals(cmd)) {
String[] newArgs;
@@ -12397,14 +12429,14 @@ public final class ActivityManagerService extends ActivityManagerNative
name = null;
newArgs = EMPTY_STRING_ARRAY;
} else {
- name = args[opti];
+ dumpPackage = args[opti];
opti++;
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
}
synchronized (this) {
- dumpPendingIntentsLocked(fd, pw, args, opti, true, name);
+ dumpPendingIntentsLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("processes".equals(cmd) || "p".equals(cmd)) {
String[] newArgs;
@@ -12413,14 +12445,14 @@ public final class ActivityManagerService extends ActivityManagerNative
name = null;
newArgs = EMPTY_STRING_ARRAY;
} else {
- name = args[opti];
+ dumpPackage = args[opti];
opti++;
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
}
synchronized (this) {
- dumpProcessesLocked(fd, pw, args, opti, true, name);
+ dumpProcessesLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("oom".equals(cmd) || "o".equals(cmd)) {
synchronized (this) {
@@ -12478,14 +12510,39 @@ public final class ActivityManagerService extends ActivityManagerNative
opti = 0;
more = true;
}
+ } else if ("associations".equals(cmd) || "as".equals(cmd)) {
+ synchronized (this) {
+ dumpAssociationsLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
+ }
} else if ("services".equals(cmd) || "s".equals(cmd)) {
synchronized (this) {
- mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, null);
+ mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
}
} else if ("write".equals(cmd)) {
mTaskPersister.flush();
pw.println("All tasks persisted.");
return;
+ } else if ("track-associations".equals(cmd)) {
+ synchronized (this) {
+ if (!mTrackingAssociations) {
+ mTrackingAssociations = true;
+ pw.println("Association tracking started.");
+ } else {
+ pw.println("Association tracking already enabled.");
+ }
+ }
+ return;
+ } else if ("untrack-associations".equals(cmd)) {
+ synchronized (this) {
+ if (mTrackingAssociations) {
+ mTrackingAssociations = false;
+ mAssociations.clear();
+ pw.println("Association tracking stopped.");
+ } else {
+ pw.println("Association tracking not running.");
+ }
+ }
+ return;
} else {
// Dumping a single activity?
if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
@@ -12527,6 +12584,13 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println("-------------------------------------------------------------------------------");
}
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+ if (mAssociations.size() > 0) {
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+ }
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -12601,6 +12665,78 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ void dumpAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
+ pw.println("ACTIVITY MANAGER ASSOCIATIONS (dumpsys activity associations)");
+
+ int dumpUid = 0;
+ if (dumpPackage != null) {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ try {
+ dumpUid = pm.getPackageUid(dumpPackage, 0);
+ } catch (RemoteException e) {
+ }
+ }
+
+ boolean printedAnything = false;
+
+ final long now = SystemClock.uptimeMillis();
+
+ for (int i1=0, N1=mAssociations.size(); i1<N1; i1++) {
+ ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> targetComponents
+ = mAssociations.valueAt(i1);
+ for (int i2=0, N2=targetComponents.size(); i2<N2; i2++) {
+ SparseArray<ArrayMap<String, Association>> sourceUids
+ = targetComponents.valueAt(i2);
+ for (int i3=0, N3=sourceUids.size(); i3<N3; i3++) {
+ ArrayMap<String, Association> sourceProcesses = sourceUids.valueAt(i3);
+ for (int i4=0, N4=sourceProcesses.size(); i4<N4; i4++) {
+ Association ass = sourceProcesses.valueAt(i4);
+ if (dumpPackage != null) {
+ if (!ass.mTargetComponent.getPackageName().equals(dumpPackage)
+ && UserHandle.getAppId(ass.mSourceUid) != dumpUid) {
+ continue;
+ }
+ }
+ printedAnything = true;
+ pw.print(" ");
+ pw.print(ass.mTargetProcess);
+ pw.print("/");
+ UserHandle.formatUid(pw, ass.mTargetUid);
+ pw.print(" <- ");
+ pw.print(ass.mSourceProcess);
+ pw.print("/");
+ UserHandle.formatUid(pw, ass.mSourceUid);
+ pw.println();
+ pw.print(" via ");
+ pw.print(ass.mTargetComponent.flattenToShortString());
+ pw.println();
+ pw.print(" ");
+ long dur = ass.mTime;
+ if (ass.mNesting > 0) {
+ dur += now - ass.mStartTime;
+ }
+ TimeUtils.formatDuration(dur, pw);
+ pw.print(" (");
+ pw.print(ass.mCount);
+ pw.println(" times)");
+ if (ass.mNesting > 0) {
+ pw.print(" ");
+ pw.print(" Currently active: ");
+ TimeUtils.formatDuration(now - ass.mStartTime, pw);
+ pw.println();
+ }
+ }
+ }
+ }
+
+ }
+
+ if (!printedAnything) {
+ pw.println(" (nothing)");
+ }
+ }
+
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
@@ -14697,7 +14833,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// In the protocol here, we don't expect the client to correctly
// clean up this connection, we'll just remove it.
cpr.connections.remove(i);
- conn.client.conProviders.remove(conn);
+ if (conn.client.conProviders.remove(conn)) {
+ stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
+ }
}
}
@@ -14783,6 +14921,8 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<app.conProviders.size(); i++) {
ContentProviderConnection conn = app.conProviders.get(i);
conn.provider.connections.remove(conn);
+ stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
+ conn.provider.name);
}
app.conProviders.clear();
}
@@ -15217,6 +15357,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
+ Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
throw new IllegalArgumentException("Invalid service token");
}
mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
@@ -15848,6 +15989,9 @@ public final class ActivityManagerService extends ActivityManagerNative
removeUriPermissionsForPackageLocked(ssp, userId, true);
removeTasksByPackageNameLocked(ssp, userId);
+ if (userId == UserHandle.USER_OWNER) {
+ mTaskPersister.removeFromPackageCache(ssp);
+ }
}
} else {
removeTasksByRemovedPackageComponentsLocked(ssp, userId);
@@ -16468,6 +16612,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Configuration ci;
synchronized(this) {
ci = new Configuration(mConfiguration);
+ ci.userSetLocale = false;
}
return ci;
}
@@ -16725,6 +16870,69 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
+ Association startAssociationLocked(int sourceUid, String sourceProcess, int targetUid,
+ ComponentName targetComponent, String targetProcess) {
+ if (!mTrackingAssociations) {
+ return null;
+ }
+ ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> components
+ = mAssociations.get(targetUid);
+ if (components == null) {
+ components = new ArrayMap<>();
+ mAssociations.put(targetUid, components);
+ }
+ SparseArray<ArrayMap<String, Association>> sourceUids = components.get(targetComponent);
+ if (sourceUids == null) {
+ sourceUids = new SparseArray<>();
+ components.put(targetComponent, sourceUids);
+ }
+ ArrayMap<String, Association> sourceProcesses = sourceUids.get(sourceUid);
+ if (sourceProcesses == null) {
+ sourceProcesses = new ArrayMap<>();
+ sourceUids.put(sourceUid, sourceProcesses);
+ }
+ Association ass = sourceProcesses.get(sourceProcess);
+ if (ass == null) {
+ ass = new Association(sourceUid, sourceProcess, targetUid, targetComponent,
+ targetProcess);
+ sourceProcesses.put(sourceProcess, ass);
+ }
+ ass.mCount++;
+ ass.mNesting++;
+ if (ass.mNesting == 1) {
+ ass.mStartTime = SystemClock.uptimeMillis();
+ }
+ return ass;
+ }
+
+ void stopAssociationLocked(int sourceUid, String sourceProcess, int targetUid,
+ ComponentName targetComponent) {
+ if (!mTrackingAssociations) {
+ return;
+ }
+ ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> components
+ = mAssociations.get(targetUid);
+ if (components == null) {
+ return;
+ }
+ SparseArray<ArrayMap<String, Association>> sourceUids = components.get(targetComponent);
+ if (sourceUids == null) {
+ return;
+ }
+ ArrayMap<String, Association> sourceProcesses = sourceUids.get(sourceUid);
+ if (sourceProcesses == null) {
+ return;
+ }
+ Association ass = sourceProcesses.get(sourceProcess);
+ if (ass == null || ass.mNesting <= 0) {
+ return;
+ }
+ ass.mNesting--;
+ if (ass.mNesting == 0) {
+ ass.mTime += SystemClock.uptimeMillis() - ass.mStartTime;
+ }
+ }
+
private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
boolean doingAll, long now) {
if (mAdjSeq == app.adjSeq) {
@@ -18678,7 +18886,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return true;
}
- mStackSupervisor.setLockTaskModeLocked(null, false);
+ mStackSupervisor.setLockTaskModeLocked(null, false, "startUser");
final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
if (userInfo == null) {
@@ -18935,7 +19143,7 @@ public final class ActivityManagerService extends ActivityManagerNative
void moveUserToForeground(UserStartedState uss, int oldUserId, int newUserId) {
boolean homeInFront = mStackSupervisor.switchUserLocked(newUserId, uss);
if (homeInFront) {
- startHomeActivityLocked(newUserId);
+ startHomeActivityLocked(newUserId, "moveUserToFroreground");
} else {
mStackSupervisor.resumeTopActivitiesLocked();
}
@@ -19457,20 +19665,8 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void moveToFront() {
checkCaller();
-
- final TaskRecord tr;
- synchronized (ActivityManagerService.this) {
- tr = recentTaskForIdLocked(mTaskId);
- if (tr == null) {
- throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
- }
- if (tr.getRootActivity() != null) {
- moveTaskToFrontLocked(tr.taskId, 0, null);
- return;
- }
- }
-
- startActivityFromRecentsInner(tr.taskId, null);
+ // Will bring task to front if it already has a root activity.
+ startActivityFromRecentsInner(mTaskId, null);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 912ca62..b1b2a5c 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -81,7 +81,7 @@ final class ActivityRecord {
private static final String ATTR_USERID = "user_id";
private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid";
- private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
+ static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
private static final String ATTR_RESOLVEDTYPE = "resolved_type";
private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
@@ -90,7 +90,7 @@ final class ActivityRecord {
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final ApplicationInfo appInfo; // information about activity's app
- final int launchedFromUid; // always the uid who started the activity.
+ int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final int userId; // Which user is this running for?
final Intent intent; // the original intent that generated us
@@ -516,7 +516,7 @@ final class ActivityRecord {
void setTask(TaskRecord newTask, TaskRecord taskToAffiliateWith) {
if (task != null && task.removeActivity(this)) {
if (task != newTask) {
- task.stack.removeTask(task);
+ task.stack.removeTask(task, "setTask");
} else {
Slog.d(TAG, "!!! REMOVE THIS LOG !!! setTask: nearly removed stack=" +
(newTask == null ? null : newTask.stack));
@@ -1143,8 +1143,8 @@ final class ActivityRecord {
}
}
- static ActivityRecord restoreFromXml(XmlPullParser in,
- ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
+ static ActivityRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+ throws IOException, XmlPullParserException {
Intent intent = null;
PersistableBundle persistentState = null;
int launchedFromUid = 0;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9870a1a..7908da0 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -291,7 +291,7 @@ final class ActivityStack {
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity destroy timeout for " + r);
synchronized (mService) {
- activityDestroyedLocked(r != null ? r.appToken : null);
+ activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
}
} break;
case STOP_TIMEOUT_MSG: {
@@ -473,10 +473,10 @@ final class ActivityStack {
mActivityContainer.mActivityDisplay.mDisplayId == Display.DEFAULT_DISPLAY;
}
- final void moveToFront() {
+ final void moveToFront(String reason) {
if (isAttached()) {
if (isOnHomeDisplay()) {
- mStackSupervisor.moveHomeStack(isHomeStack());
+ mStackSupervisor.moveHomeStack(isHomeStack(), reason);
}
mStacks.remove(this);
mStacks.add(this);
@@ -1478,7 +1478,7 @@ final class ActivityStack {
cancelInitializingActivities();
// Find the first activity that is not finishing.
- ActivityRecord next = topRunningActivityLocked(null);
+ final ActivityRecord next = topRunningActivityLocked(null);
// Remember how we'll process this pause/resume situation, and ensure
// that the state is reset however we wind up proceeding.
@@ -1496,7 +1496,7 @@ final class ActivityStack {
final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
return isOnHomeDisplay() &&
- mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
+ mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "noMoreActivities");
}
next.delayedResume = false;
@@ -1532,7 +1532,7 @@ final class ActivityStack {
final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
return isOnHomeDisplay() &&
- mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
+ mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "prevFinished");
}
}
@@ -1817,9 +1817,8 @@ final class ActivityStack {
next.app.thread.scheduleNewIntent(next.newIntents, next.appToken);
}
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
- next.userId, System.identityHashCode(next),
- next.task.taskId, next.shortComponentName);
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
+ System.identityHashCode(next), next.task.taskId, next.shortComponentName);
next.sleeping = false;
mService.showAskCompatModeDialogLocked(next);
@@ -2466,18 +2465,19 @@ final class ActivityStack {
r.addResultLocked(null, resultWho, requestCode, resultCode, data);
}
- private void adjustFocusedActivityLocked(ActivityRecord r) {
+ private void adjustFocusedActivityLocked(ActivityRecord r, String reason) {
if (mStackSupervisor.isFrontStack(this) && mService.mFocusedActivity == r) {
ActivityRecord next = topRunningActivityLocked(null);
if (next != r) {
final TaskRecord task = r.task;
if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
- mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
+ mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(),
+ reason + " adjustFocus");
}
}
ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
if (top != null) {
- mService.setFocusedActivityLocked(top);
+ mService.setFocusedActivityLocked(top, reason + " adjustTopFocus");
}
}
}
@@ -2501,7 +2501,7 @@ final class ActivityStack {
}
if (r.app != null && r.app.thread != null) {
- adjustFocusedActivityLocked(r);
+ adjustFocusedActivityLocked(r, "stopActivity");
r.resumeKeyDispatchingLocked();
try {
r.stopped = false;
@@ -2576,7 +2576,7 @@ final class ActivityStack {
if (r != null && r.app == app) {
// If the top running activity is from this crashing
// process, then terminate it to avoid getting in a loop.
- Slog.w(TAG, " Force finishing activity "
+ Slog.w(TAG, " Force finishing activity 1 "
+ r.intent.getComponent().flattenToShortString());
int taskNdx = mTaskHistory.indexOf(r.task);
int activityNdx = r.task.mActivities.indexOf(r);
@@ -2600,7 +2600,7 @@ final class ActivityStack {
|| r.state == ActivityState.PAUSING
|| r.state == ActivityState.PAUSED) {
if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
- Slog.w(TAG, " Force finishing activity "
+ Slog.w(TAG, " Force finishing activity 2 "
+ r.intent.getComponent().flattenToShortString());
finishActivityLocked(r, Activity.RESULT_CANCELED, null, "crashed", false);
}
@@ -2705,7 +2705,7 @@ final class ActivityStack {
r.pauseKeyDispatchingLocked();
- adjustFocusedActivityLocked(r);
+ adjustFocusedActivityLocked(r, "finishActivity");
finishActivityResultsLocked(r, resultCode, resultData);
@@ -3008,7 +3008,7 @@ final class ActivityStack {
r.finishLaunchTickingLocked();
}
- private void removeActivityFromHistoryLocked(ActivityRecord r) {
+ private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
mStackSupervisor.removeChildActivityContainers(r);
finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
r.makeFinishing();
@@ -3033,9 +3033,9 @@ final class ActivityStack {
"removeActivityFromHistoryLocked: last activity removed from " + this);
if (mStackSupervisor.isFrontStack(this) && task == topTask() &&
task.isOverHomeStack()) {
- mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
+ mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), reason);
}
- removeTask(task);
+ removeTask(task, reason);
}
cleanUpActivityServicesLocked(r);
r.removeUriPermissionsLocked();
@@ -3200,7 +3200,7 @@ final class ActivityStack {
// up.
//Slog.w(TAG, "Exception thrown during finish", e);
if (r.finishing) {
- removeActivityFromHistoryLocked(r);
+ removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
removedFromHistory = true;
skipDestroy = true;
}
@@ -3230,7 +3230,7 @@ final class ActivityStack {
} else {
// remove this record from the history.
if (r.finishing) {
- removeActivityFromHistoryLocked(r);
+ removeActivityFromHistoryLocked(r, reason + " hadNoApp");
removedFromHistory = true;
} else {
if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (no app)");
@@ -3249,7 +3249,7 @@ final class ActivityStack {
return removedFromHistory;
}
- final void activityDestroyedLocked(IBinder token) {
+ final void activityDestroyedLocked(IBinder token, String reason) {
final long origId = Binder.clearCallingIdentity();
try {
ActivityRecord r = ActivityRecord.forToken(token);
@@ -3261,7 +3261,7 @@ final class ActivityStack {
if (isInStackLocked(token) != null) {
if (r.state == ActivityState.DESTROYING) {
cleanUpActivityLocked(r, true, false);
- removeActivityFromHistoryLocked(r);
+ removeActivityFromHistoryLocked(r, reason);
}
}
mStackSupervisor.resumeTopActivitiesLocked();
@@ -3397,7 +3397,7 @@ final class ActivityStack {
mService.updateUsageStats(r, false);
}
}
- removeActivityFromHistoryLocked(r);
+ removeActivityFromHistoryLocked(r, "appDied");
} else {
// We have the current state for this activity, so
@@ -3466,15 +3466,16 @@ final class ActivityStack {
}
}
- final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) {
+ final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options,
+ String reason) {
if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
final int numTasks = mTaskHistory.size();
final int index = mTaskHistory.indexOf(tr);
if (numTasks == 0 || index < 0) {
// nothing to do!
- if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ if (source != null &&
+ (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
ActivityOptions.abort(options);
} else {
updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
@@ -3485,11 +3486,11 @@ final class ActivityStack {
// Shift all activities with this task up to the top
// of the stack, keeping them in the same internal order.
insertTaskAtTop(tr);
- moveToFront();
+ moveToFront(reason);
if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr);
- if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ if (source != null &&
+ (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
ActivityRecord r = topRunningActivityLocked(null);
if (r != null) {
@@ -3519,7 +3520,7 @@ final class ActivityStack {
* @param taskId The taskId to collect and move to the bottom.
* @return Returns true if the move completed, false if not.
*/
- final boolean moveTaskToBackLocked(int taskId, ActivityRecord reason) {
+ final boolean moveTaskToBackLocked(int taskId) {
final TaskRecord tr = taskForIdLocked(taskId);
if (tr == null) {
Slog.i(TAG, "moveTaskToBack: bad taskId=" + taskId);
@@ -3574,16 +3575,7 @@ final class ActivityStack {
}
}
- if (reason != null &&
- (reason.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
- ActivityRecord r = topRunningActivityLocked(null);
- if (r != null) {
- mNoAnimActivities.add(r);
- }
- } else {
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_TO_BACK, false);
- }
+ mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_TO_BACK, false);
mWindowManager.moveTaskToBottom(taskId);
if (VALIDATE_TOKENS) {
@@ -3598,7 +3590,7 @@ final class ActivityStack {
}
final int taskToReturnTo = tr.getTaskToReturnTo();
tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
- return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null);
+ return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null, "moveTaskToBack");
}
mStackSupervisor.resumeTopActivitiesLocked();
@@ -3850,7 +3842,7 @@ final class ActivityStack {
}
}
didSomething = true;
- Slog.i(TAG, " Force finishing activity " + r);
+ Slog.i(TAG, " Force finishing activity 3 " + r);
if (samePackage) {
if (r.app != null) {
r.app.removed = true;
@@ -3960,7 +3952,7 @@ final class ActivityStack {
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.app == app) {
- Slog.w(TAG, " Force finishing activity "
+ Slog.w(TAG, " Force finishing activity 4 "
+ r.intent.getComponent().flattenToShortString());
// Force the destroy to skip right to removal.
r.app = null;
@@ -4040,7 +4032,7 @@ final class ActivityStack {
return starting;
}
- void removeTask(TaskRecord task) {
+ void removeTask(TaskRecord task, String reason) {
mStackSupervisor.endLockTaskModeIfTaskEnding(task);
mWindowManager.removeTask(task.taskId);
final ActivityRecord r = mResumedActivity;
@@ -4078,7 +4070,7 @@ final class ActivityStack {
if (mTaskHistory.isEmpty()) {
if (DEBUG_STACK) Slog.i(TAG, "removeTask: moving to back stack=" + this);
if (isOnHomeDisplay()) {
- mStackSupervisor.moveHomeStack(!isHomeStack());
+ mStackSupervisor.moveHomeStack(!isHomeStack(), reason + " leftTaskHistoryEmpty");
}
if (mStacks != null) {
mStacks.remove(this);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index df5b3c5..32787d8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -24,6 +24,7 @@ import static com.android.server.am.ActivityManagerService.localLOGV;
import static com.android.server.am.ActivityManagerService.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerService.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerService.DEBUG_PAUSE;
+import static com.android.server.am.ActivityManagerService.DEBUG_RECENTS;
import static com.android.server.am.ActivityManagerService.DEBUG_RESULTS;
import static com.android.server.am.ActivityManagerService.DEBUG_STACK;
import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
@@ -397,9 +398,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
return false;
}
- void moveHomeStack(boolean toFront) {
+ void moveHomeStack(boolean toFront, String reason) {
ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
- int topNdx = stacks.size() - 1;
+ final int topNdx = stacks.size() - 1;
if (topNdx <= 0) {
return;
}
@@ -413,18 +414,28 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (DEBUG_STACK) Slog.d(TAG, "moveHomeTask: topStack old=" + topStack + " new="
+ mFocusedStack);
}
+ EventLog.writeEvent(EventLogTags.AM_HOME_STACK_MOVED,
+ mCurrentUser, toFront ? 1 : 0, stacks.get(topNdx).getStackId(),
+ mFocusedStack == null ? -1 : mFocusedStack.getStackId(), reason);
+
+ if (mService.mBooting || !mService.mBooted) {
+ final ActivityRecord r = topRunningActivityLocked();
+ if (r != null && r.idle) {
+ checkFinishBootingLocked();
+ }
+ }
}
- void moveHomeStackTaskToTop(int homeStackTaskType) {
+ void moveHomeStackTaskToTop(int homeStackTaskType, String reason) {
if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
mWindowManager.showRecentApps();
return;
}
- moveHomeStack(true);
+ moveHomeStack(true, reason);
mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
}
- boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev) {
+ boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev, String reason) {
if (!mService.mBooting && !mService.mBooted) {
// Not ready yet!
return false;
@@ -434,7 +445,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
mWindowManager.showRecentApps();
return false;
}
- moveHomeStackTaskToTop(homeStackTaskType);
+ moveHomeStackTaskToTop(homeStackTaskType, reason);
if (prev != null) {
prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
}
@@ -442,10 +453,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
ActivityRecord r = mHomeStack.topRunningActivityLocked(null);
// if (r != null && (r.isHomeActivity() || r.isRecentsActivity())) {
if (r != null && r.isHomeActivity()) {
- mService.setFocusedActivityLocked(r);
+ mService.setFocusedActivityLocked(r, reason);
return resumeTopActivitiesLocked(mHomeStack, prev, null);
}
- return mService.startHomeActivityLocked(mCurrentUser);
+ return mService.startHomeActivityLocked(mCurrentUser, reason);
}
TaskRecord anyTaskForIdLocked(int id) {
@@ -460,7 +471,21 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
}
- return null;
+
+ // Don't give up! Look in recents.
+ if (DEBUG_RECENTS) Slog.v(TAG, "Looking for task id=" + id + " in recents");
+ TaskRecord task = mService.recentTaskForIdLocked(id);
+ if (task == null) {
+ if (DEBUG_RECENTS) Slog.d(TAG, "\tDidn't find task id=" + id + " in recents");
+ return null;
+ }
+
+ if (!restoreRecentTaskLocked(task)) {
+ if (DEBUG_RECENTS) Slog.w(TAG, "Couldn't restore task id=" + id + " found in recents");
+ return null;
+ }
+ if (DEBUG_RECENTS) Slog.w(TAG, "Restored task id=" + id + " from in recents");
+ return task;
}
ActivityRecord isInAnyStackLocked(IBinder token) {
@@ -803,8 +828,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
return aInfo;
}
- void startHomeActivity(Intent intent, ActivityInfo aInfo) {
- moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
+ void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
+ moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null,
0, 0, 0, null, false, null, null, null);
}
@@ -1556,7 +1581,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
return mHomeStack;
}
- void setFocusedStack(ActivityRecord r) {
+ void setFocusedStack(ActivityRecord r, String reason) {
if (r != null) {
final TaskRecord task = r.task;
boolean isHomeActivity = !r.isApplicationActivity();
@@ -1567,7 +1592,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
final ActivityRecord parent = task.stack.mActivityContainer.mParentActivity;
isHomeActivity = parent != null && parent.isHomeActivity();
}
- moveHomeStack(isHomeActivity);
+ moveHomeStack(isHomeActivity, reason);
}
}
@@ -1815,7 +1840,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
targetStack.mLastPausedActivity = null;
if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack
+ " from " + intentActivity);
- targetStack.moveToFront();
+ targetStack.moveToFront("intentActivityFound");
if (intentActivity.task.intent == null) {
// This task was started because of movement of
// the activity based on affinity... now that we
@@ -1844,7 +1869,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
intentActivity.setTaskToAffiliateWith(sourceRecord.task);
}
movedHome = true;
- targetStack.moveTaskToFrontLocked(intentActivity.task, r, options);
+ targetStack.moveTaskToFrontLocked(intentActivity.task, r, options,
+ "bringingFoundTaskToFront");
if ((launchFlags &
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
@@ -2042,7 +2068,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
newTask = true;
targetStack = adjustStackFocus(r, newTask);
if (!launchTaskBehind) {
- targetStack.moveToFront();
+ targetStack.moveToFront("startingNewTask");
}
if (reuseTask == null) {
r.setTask(targetStack.createTaskRecord(getNextTaskId(),
@@ -2071,10 +2097,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
targetStack = sourceTask.stack;
- targetStack.moveToFront();
+ targetStack.moveToFront("sourceStackToFront");
final TaskRecord topTask = targetStack.topTask();
if (topTask != sourceTask) {
- targetStack.moveTaskToFrontLocked(sourceTask, r, options);
+ targetStack.moveTaskToFrontLocked(sourceTask, r, options, "sourceTaskToFront");
}
if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
// In this case, we are adding the activity to an existing
@@ -2128,7 +2154,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
targetStack = inTask.stack;
- targetStack.moveTaskToFrontLocked(inTask, r, options);
+ targetStack.moveTaskToFrontLocked(inTask, r, options, "inTaskToFront");
// Check whether we should actually launch the new activity in to the task,
// or just reuse the current activity on top.
@@ -2164,7 +2190,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// of a new task... just put it in the top task, though these days
// this case should never happen.
targetStack = adjustStackFocus(r, newTask);
- targetStack.moveToFront();
+ targetStack.moveToFront("addingToTopTask");
ActivityRecord prev = targetStack.topActivity();
r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(),
r.info, intent, null, null, true), null);
@@ -2187,7 +2213,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
if (!launchTaskBehind) {
// Don't set focus on an activity that's going to the back.
- mService.setFocusedActivityLocked(r);
+ mService.setFocusedActivityLocked(r, "startedActivity");
}
return ActivityManager.START_SUCCESS;
}
@@ -2220,6 +2246,24 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
+ /**
+ * Called when the frontmost task is idle.
+ * @return the state of mService.mBooting before this was called.
+ */
+ private boolean checkFinishBootingLocked() {
+ final boolean booting = mService.mBooting;
+ boolean enableScreen = false;
+ mService.mBooting = false;
+ if (!mService.mBooted) {
+ mService.mBooted = true;
+ enableScreen = true;
+ }
+ if (booting || enableScreen) {
+ mService.postFinishBooting(booting, enableScreen);
+ }
+ return booting;
+ }
+
// Checked.
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
@@ -2231,7 +2275,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
int NS = 0;
int NF = 0;
boolean booting = false;
- boolean enableScreen = false;
boolean activityRemoved = false;
ActivityRecord r = ActivityRecord.forToken(token);
@@ -2259,12 +2302,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
//Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
if (isFrontStack(r.task.stack) || fromTimeout) {
- booting = mService.mBooting;
- mService.mBooting = false;
- if (!mService.mBooted) {
- mService.mBooted = true;
- enableScreen = true;
- }
+ booting = checkFinishBootingLocked();
}
}
@@ -2337,10 +2375,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
//dump();
//mWindowManager.dump();
- if (booting || enableScreen) {
- mService.postFinishBooting(booting, enableScreen);
- }
-
if (activityRemoved) {
resumeTopActivitiesLocked();
}
@@ -2476,7 +2510,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
- void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options) {
+ void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options, String reason) {
if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
mUserLeaving = true;
}
@@ -2485,7 +2519,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// we'll just indicate that this task returns to the home task.
task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
- task.stack.moveTaskToFrontLocked(task, null, options);
+ task.stack.moveTaskToFrontLocked(task, null, options, reason);
if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
+ task.stack);
}
@@ -2586,26 +2620,58 @@ public final class ActivityStackSupervisor implements DisplayListener {
return mLastStackId;
}
- void createStackForRestoredTaskHistory(ArrayList<TaskRecord> tasks) {
- int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
- final ActivityStack stack = getStack(stackId);
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final TaskRecord task = tasks.get(taskNdx);
- stack.addTask(task, false, false);
- final int taskId = task.taskId;
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- mWindowManager.addAppToken(0, r.appToken, taskId, stackId,
- r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
- r.userId, r.info.configChanges, task.voiceSession != null,
- r.mLaunchTaskBehind);
+ private boolean restoreRecentTaskLocked(TaskRecord task) {
+ ActivityStack stack = null;
+ // Determine stack to restore task to.
+ if (mLeanbackOnlyDevice) {
+ // There is only one stack for lean back devices.
+ stack = mHomeStack;
+ } else {
+ // Look for the top stack on the home display that isn't the home stack.
+ final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
+ for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack tmpStack = homeDisplayStacks.get(stackNdx);
+ if (!tmpStack.isHomeStack()) {
+ stack = tmpStack;
+ break;
+ }
}
}
+
+ if (stack == null) {
+ // We couldn't find a stack to restore the task to. Possible if are restoring recents
+ // before an application stack is created...Go ahead and create one on the default
+ // display.
+ stack = getStack(createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY));
+ // Restore home stack to top.
+ moveHomeStack(true, "restoreRecentTask");
+ if (DEBUG_RECENTS)
+ Slog.v(TAG, "Created stack=" + stack + " for recents restoration.");
+ }
+
+ if (stack == null) {
+ // What does this mean??? Not sure how we would get here...
+ if (DEBUG_RECENTS)
+ Slog.v(TAG, "Unable to find/create stack to restore recent task=" + task);
+ return false;
+ }
+
+ stack.addTask(task, false, false);
+ if (DEBUG_RECENTS)
+ Slog.v(TAG, "Added restored task=" + task + " to stack=" + stack);
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ mWindowManager.addAppToken(0, r.appToken, task.taskId, stack.mStackId,
+ r.info.screenOrientation, r.fullscreen,
+ (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+ r.userId, r.info.configChanges, task.voiceSession != null,
+ r.mLaunchTaskBehind);
+ }
+ return true;
}
- void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+ void moveTaskToStackLocked(int taskId, int stackId, boolean toTop) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
return;
@@ -2615,7 +2681,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId);
return;
}
- task.stack.removeTask(task);
+ task.stack.removeTask(task, "moveTaskToStack");
stack.addTask(task, toTop, true);
mWindowManager.addTask(taskId, stackId, toTop);
resumeTopActivitiesLocked();
@@ -2981,14 +3047,14 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
final boolean homeInFront = stack.isHomeStack();
if (stack.isOnHomeDisplay()) {
- moveHomeStack(homeInFront);
+ moveHomeStack(homeInFront, "switchUserOnHomeDisplay");
TaskRecord task = stack.topTask();
if (task != null) {
mWindowManager.moveTaskToTop(task.taskId);
}
} else {
// Stack was moved to another display while user was swapped out.
- resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
+ resumeHomeStackTask(HOME_ACTIVITY_TYPE, null, "switchUserOnOtherDisplay");
}
return homeInFront;
}
@@ -3389,7 +3455,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
mLockTaskNotify.showToast(mLockTaskIsLocked);
}
- void setLockTaskModeLocked(TaskRecord task, boolean isLocked) {
+ void setLockTaskModeLocked(TaskRecord task, boolean isLocked, String reason) {
if (task == null) {
// Take out of lock task mode if necessary
if (mLockTaskModeTask != null) {
@@ -3406,7 +3472,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
return;
}
mLockTaskModeTask = task;
- findTaskToMoveToFrontLocked(task, 0, null);
+ findTaskToMoveToFrontLocked(task, 0, null, reason);
resumeTopActivitiesLocked();
final Message lockTaskMsg = Message.obtain();
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 0dc163b..d1682b8 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -108,50 +108,46 @@ final class CoreSettingsObserver extends ContentObserver {
for (Map.Entry<String, Class<?>> entry : map.entrySet()) {
String setting = entry.getKey();
Class<?> type = entry.getValue();
- try {
- if (type == String.class) {
- final String value;
- if (map == sSecureSettingToTypeMap) {
- value = Settings.Secure.getString(context.getContentResolver(), setting);
- } else if (map == sSystemSettingToTypeMap) {
- value = Settings.System.getString(context.getContentResolver(), setting);
- } else {
- value = Settings.Global.getString(context.getContentResolver(), setting);
- }
- snapshot.putString(setting, value);
- } else if (type == int.class) {
- final int value;
- if (map == sSecureSettingToTypeMap) {
- value = Settings.Secure.getInt(context.getContentResolver(), setting);
- } else if (map == sSystemSettingToTypeMap) {
- value = Settings.System.getInt(context.getContentResolver(), setting);
- } else {
- value = Settings.Global.getInt(context.getContentResolver(), setting);
- }
- snapshot.putInt(setting, value);
- } else if (type == float.class) {
- final float value;
- if (map == sSecureSettingToTypeMap) {
- value = Settings.Secure.getFloat(context.getContentResolver(), setting);
- } else if (map == sSystemSettingToTypeMap) {
- value = Settings.System.getFloat(context.getContentResolver(), setting);
- } else {
- value = Settings.Global.getFloat(context.getContentResolver(), setting);
- }
- snapshot.putFloat(setting, value);
- } else if (type == long.class) {
- final long value;
- if (map == sSecureSettingToTypeMap) {
- value = Settings.Secure.getLong(context.getContentResolver(), setting);
- } else if (map == sSystemSettingToTypeMap) {
- value = Settings.System.getLong(context.getContentResolver(), setting);
- } else {
- value = Settings.Global.getLong(context.getContentResolver(), setting);
- }
- snapshot.putLong(setting, value);
+ if (type == String.class) {
+ final String value;
+ if (map == sSecureSettingToTypeMap) {
+ value = Settings.Secure.getString(context.getContentResolver(), setting);
+ } else if (map == sSystemSettingToTypeMap) {
+ value = Settings.System.getString(context.getContentResolver(), setting);
+ } else {
+ value = Settings.Global.getString(context.getContentResolver(), setting);
}
- } catch (SettingNotFoundException snfe) {
- Log.w(LOG_TAG, "Cannot find setting \"" + setting + "\"", snfe);
+ snapshot.putString(setting, value);
+ } else if (type == int.class) {
+ final int value;
+ if (map == sSecureSettingToTypeMap) {
+ value = Settings.Secure.getInt(context.getContentResolver(), setting, 0);
+ } else if (map == sSystemSettingToTypeMap) {
+ value = Settings.System.getInt(context.getContentResolver(), setting, 0);
+ } else {
+ value = Settings.Global.getInt(context.getContentResolver(), setting, 0);
+ }
+ snapshot.putInt(setting, value);
+ } else if (type == float.class) {
+ final float value;
+ if (map == sSecureSettingToTypeMap) {
+ value = Settings.Secure.getFloat(context.getContentResolver(), setting, 0);
+ } else if (map == sSystemSettingToTypeMap) {
+ value = Settings.System.getFloat(context.getContentResolver(), setting, 0);
+ } else {
+ value = Settings.Global.getFloat(context.getContentResolver(), setting, 0);
+ }
+ snapshot.putFloat(setting, value);
+ } else if (type == long.class) {
+ final long value;
+ if (map == sSecureSettingToTypeMap) {
+ value = Settings.Secure.getLong(context.getContentResolver(), setting, 0);
+ } else if (map == sSystemSettingToTypeMap) {
+ value = Settings.System.getLong(context.getContentResolver(), setting, 0);
+ } else {
+ value = Settings.Global.getLong(context.getContentResolver(), setting, 0);
+ }
+ snapshot.putLong(setting, value);
}
}
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index d3cc56b..c376744 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -89,3 +89,9 @@ option java_package com.android.server.am
# Activity fully drawn time
30042 am_activity_fully_drawn_time (User|1|5),(Token|1|5),(Component Name|3),(time|2|3)
+
+# Activity focused
+30043 am_focused_activity (User|1|5),(Component Name|3)
+
+# Home Stack brought to front or rear
+30044 am_home_stack_moved (User|1|5),(To Front|1|5),(Top Stack Id|1|5),(Focused Stack Id|1|5),(Reason|3)
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ae4af5f..c380160 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -235,15 +235,16 @@ final class ProcessList {
Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
}
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- // Increase the high min-free levels for cached processes for 64-bit
- mOomMinFreeHigh[4] = (mOomMinFreeHigh[4]*3)/2;
- mOomMinFreeHigh[5] = (mOomMinFreeHigh[5]*7)/4;
- }
+ final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
for (int i=0; i<mOomAdj.length; i++) {
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
+ if (is64bit) {
+ // Increase the high min-free levels for cached processes for 64-bit
+ if (i == 4) high = (high*3)/2;
+ else if (i == 5) high = (high*7)/4;
+ }
mOomMinFree[i] = (int)(low + ((high-low)*scale));
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 7c48f3e..a6c616a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -64,6 +64,9 @@ final class ProcessRecord {
ProcessStats.ProcessState baseProcessTracker;
BatteryStatsImpl.Uid.Proc curProcBatteryStats;
int pid; // The process of this application; 0 if none
+ int[] gids; // The gids this process was launched with
+ String requiredAbi; // The ABI this process was launched with
+ String instructionSet; // The instruction set this process was launched with
boolean starting; // True if the process is being started
long lastActivityTime; // For managing the LRU list
long lastPssTime; // Last time we retrieved PSS data
@@ -183,7 +186,17 @@ final class ProcessRecord {
if (uid != info.uid) {
pw.print(" ISOLATED uid="); pw.print(uid);
}
- pw.println();
+ pw.print(" gids={");
+ if (gids != null) {
+ for (int gi=0; gi<gids.length; gi++) {
+ if (gi != 0) pw.print(", ");
+ pw.print(gids[gi]);
+
+ }
+ }
+ pw.println("}");
+ pw.print(prefix); pw.print("requiredAbi="); pw.print(requiredAbi);
+ pw.print(" instructionSet="); pw.println(instructionSet);
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index d4a378b..1cb1bb8 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -105,6 +105,8 @@ final class ServiceRecord extends Binder {
long restartDelay; // delay until next restart attempt.
long restartTime; // time of last restart.
long nextRestartTime; // time when restartDelay will expire.
+ boolean destroying; // set when we have started destroying the service
+ long destroyTime; // time at which destory was initiated.
String stringName; // caching of toString
@@ -250,6 +252,12 @@ final class ServiceRecord extends Binder {
TimeUtils.formatDuration(executingStart, now, pw);
pw.println();
}
+ if (destroying || destroyTime != 0) {
+ pw.print(prefix); pw.print("destroying="); pw.print(destroying);
+ pw.print(" destroyTime=");
+ TimeUtils.formatDuration(destroyTime, now, pw);
+ pw.println();
+ }
if (crashCount != 0 || restartCount != 0
|| restartDelay != 0 || nextRestartTime != 0) {
pw.print(prefix); pw.print("restartCount="); pw.print(restartCount);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 629a05d..24c723f 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -132,6 +132,8 @@ public class TaskPersister {
// Data organization: <packageNameOfAffiliateTask, listOfAffiliatedTasksChains>
private ArrayMap<String, List<List<OtherDeviceTask>>> mOtherDeviceTasksMap =
new ArrayMap<>(10);
+ // Local cache of package names to uid used when restoring a task from another device.
+ private ArrayMap<String, Integer> mPackageUidMap;
// The next time in milliseconds we will remove expired task from
// {@link #mOtherDeviceTasksMap} and disk. Set to {@link Long.MAX_VALUE} to never clean-up
@@ -579,7 +581,12 @@ public class TaskPersister {
private void removeExpiredTasksIfNeeded() {
synchronized (mOtherDeviceTasksMap) {
final long now = System.currentTimeMillis();
- if (mOtherDeviceTasksMap.isEmpty() || now < mExpiredTasksCleanupTime) {
+ final boolean noMoreTasks = mOtherDeviceTasksMap.isEmpty();
+ if (noMoreTasks || now < mExpiredTasksCleanupTime) {
+ if (noMoreTasks && mPackageUidMap != null) {
+ // All done! package->uid map no longer needed.
+ mPackageUidMap = null;
+ }
return;
}
@@ -632,6 +639,20 @@ public class TaskPersister {
if (DEBUG_RESTORER) Slog.d(TAG, "Reset expiration time to "
+ DateUtils.formatDateTime(mService.mContext, mExpiredTasksCleanupTime,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME));
+ } else {
+ // All done! package->uid map no longer needed.
+ mPackageUidMap = null;
+ }
+ }
+ }
+
+ /**
+ * Removes the input package name from the local package->uid map.
+ */
+ void removeFromPackageCache(String packageName) {
+ synchronized (mOtherDeviceTasksMap) {
+ if (mPackageUidMap != null) {
+ mPackageUidMap.remove(packageName);
}
}
}
@@ -778,6 +799,27 @@ public class TaskPersister {
task.mAffiliatedTaskId = INVALID_TASK_ID;
task.mPrevAffiliateTaskId = INVALID_TASK_ID;
task.mNextAffiliateTaskId = INVALID_TASK_ID;
+ // Set up uids valid for this device.
+ Integer uid = mPackageUidMap.get(task.realActivity.getPackageName());
+ if (uid == null) {
+ // How did this happen???
+ Slog.wtf(TAG, "Can't find uid for task=" + task
+ + " in mPackageUidMap=" + mPackageUidMap);
+ return null;
+ }
+ task.effectiveUid = task.mCallingUid = uid;
+ for (int i = task.mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord activity = task.mActivities.get(i);
+ uid = mPackageUidMap.get(activity.launchedFromPackage);
+ if (uid == null) {
+ // How did this happen??
+ Slog.wtf(TAG, "Can't find uid for activity=" + activity
+ + " in mPackageUidMap=" + mPackageUidMap);
+ return null;
+ }
+ activity.launchedFromUid = uid;
+ }
+
} else {
Slog.e(TAG, "Unable to create task for backed-up file=" + file + ": "
+ fileToString(file));
@@ -801,42 +843,81 @@ public class TaskPersister {
/**
* Returns true if the input task chain backed-up from another device can be restored on this
- * device.
+ * device. Also, sets the {@link OtherDeviceTask#mUid} on the input tasks if they can be
+ * restored.
*/
private boolean canAddOtherDeviceTaskChain(List<OtherDeviceTask> chain) {
- // Get component names of all the tasks in the chain.
- // Mainly doing this to reduce checking for a component twice if two or more
- // affiliations belong to the same component which is highly likely.
- ArraySet<ComponentName> componentsToCheck = new ArraySet<>();
+ final ArraySet<ComponentName> validComponents = new ArraySet<>();
+ final IPackageManager pm = AppGlobals.getPackageManager();
for (int i = 0; i < chain.size(); i++) {
OtherDeviceTask task = chain.get(i);
// Quick check, we can't add the task chain if any of its task files don't exist.
if (!task.mFile.exists()) {
- if (DEBUG_RESTORER)
- Slog.d(TAG, "Can't add chain due to missing file=" + task.mFile);
+ if (DEBUG_RESTORER) Slog.d(TAG,
+ "Can't add chain due to missing file=" + task.mFile);
+ return false;
+ }
+
+ // Verify task package is installed.
+ if (!isPackageInstalled(task.mComponentName.getPackageName())) {
+ return false;
+ }
+ // Verify that all the launch packages are installed.
+ if (task.mLaunchPackages != null) {
+ for (int j = task.mLaunchPackages.size() - 1; j >= 0; --j) {
+ if (!isPackageInstalled(task.mLaunchPackages.valueAt(j))) {
+ return false;
+ }
+ }
+ }
+
+ if (validComponents.contains(task.mComponentName)) {
+ // Existance of component has already been verified.
+ continue;
+ }
+
+ // Check to see if the specific component is installed.
+ try {
+ if (pm.getActivityInfo(task.mComponentName, 0, UserHandle.USER_OWNER) == null) {
+ // Component isn't installed...
+ return false;
+ }
+ validComponents.add(task.mComponentName);
+ } catch (RemoteException e) {
+ // Should not happen???
return false;
}
- componentsToCheck.add(task.mComponentName);
}
- boolean canAdd = true;
+ return true;
+ }
+
+ /**
+ * Returns true if the input package name is installed. If the package is installed, an entry
+ * for the package is added to {@link #mPackageUidMap}.
+ */
+ private boolean isPackageInstalled(final String packageName) {
+ if (mPackageUidMap != null && mPackageUidMap.containsKey(packageName)) {
+ return true;
+ }
try {
- // Check to see if all the components for this task chain are installed.
- final IPackageManager pm = AppGlobals.getPackageManager();
- for (int i = 0; canAdd && i < componentsToCheck.size(); i++) {
- ComponentName cn = componentsToCheck.valueAt(i);
- canAdd &= pm.getActivityInfo(cn, 0, UserHandle.USER_OWNER) != null;
- if (DEBUG_RESTORER) Slog.d(TAG, "ComponentName=" + cn + " installed=" + canAdd);
+ int uid = AppGlobals.getPackageManager().getPackageUid(
+ packageName, UserHandle.USER_OWNER);
+ if (uid == -1) {
+ // package doesn't exist...
+ return false;
}
+ if (mPackageUidMap == null) {
+ mPackageUidMap = new ArrayMap<>();
+ }
+ mPackageUidMap.put(packageName, uid);
+ return true;
} catch (RemoteException e) {
// Should not happen???
- canAdd = false;
+ return false;
}
-
- if (DEBUG_RESTORER) Slog.d(TAG, "canAdd=" + canAdd);
- return canAdd;
}
private class LazyTaskWriterThread extends Thread {
@@ -993,12 +1074,17 @@ public class TaskPersister {
final int mTaskId;
final int mAffiliatedTaskId;
- private OtherDeviceTask(
- File file, ComponentName componentName, int taskId, int affiliatedTaskId) {
+ // Names of packages that launched activities in this task. All packages listed here need
+ // to be installed on the current device in order for the task to be restored successfully.
+ final ArraySet<String> mLaunchPackages;
+
+ private OtherDeviceTask(File file, ComponentName componentName, int taskId,
+ int affiliatedTaskId, ArraySet<String> launchPackages) {
mFile = file;
mComponentName = componentName;
mTaskId = taskId;
mAffiliatedTaskId = (affiliatedTaskId == INVALID_TASK_ID) ? taskId: affiliatedTaskId;
+ mLaunchPackages = launchPackages;
}
@Override
@@ -1036,6 +1122,7 @@ public class TaskPersister {
final String name = in.getName();
if (TAG_TASK.equals(name)) {
+ final int outerDepth = in.getDepth();
ComponentName componentName = null;
int taskId = INVALID_TASK_ID;
int taskAffiliation = INVALID_TASK_ID;
@@ -1056,10 +1143,31 @@ public class TaskPersister {
+ " taskId=" + taskId + " file=" + file);
return null;
}
+
+ ArraySet<String> launchPackages = null;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+ if (event == XmlPullParser.START_TAG) {
+ if (TaskRecord.TAG_ACTIVITY.equals(in.getName())) {
+ for (int j = in.getAttributeCount() - 1; j >= 0; --j) {
+ if (ActivityRecord.ATTR_LAUNCHEDFROMPACKAGE.equals(
+ in.getAttributeName(j))) {
+ if (launchPackages == null) {
+ launchPackages = new ArraySet();
+ }
+ launchPackages.add(in.getAttributeValue(j));
+ }
+ }
+ } else {
+ XmlUtils.skipCurrentTag(in);
+ }
+ }
+ }
if (DEBUG_RESTORER) Slog.d(TAG, "creating OtherDeviceTask from file="
+ file.getName() + " componentName=" + componentName
- + " taskId=" + taskId);
- return new OtherDeviceTask(file, componentName, taskId, taskAffiliation);
+ + " taskId=" + taskId + " launchPackages=" + launchPackages);
+ return new OtherDeviceTask(file, componentName, taskId,
+ taskAffiliation, launchPackages);
} else {
Slog.wtf(TAG,
"createFromFile: Unknown xml event=" + event + " name=" + name);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c3eda71..60f8a48 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -59,7 +59,7 @@ final class TaskRecord {
private static final String TAG_AFFINITYINTENT = "affinity_intent";
static final String ATTR_REALACTIVITY = "real_activity";
private static final String ATTR_ORIGACTIVITY = "orig_activity";
- private static final String TAG_ACTIVITY = "activity";
+ static final String TAG_ACTIVITY = "activity";
private static final String ATTR_AFFINITY = "affinity";
private static final String ATTR_ROOT_AFFINITY = "root_affinity";
private static final String ATTR_ROOTHASRESET = "root_has_reset";
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 9fa362c..87f78c1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -28,6 +28,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.net.wifi.WifiInfo;
@@ -68,7 +69,7 @@ import java.util.Random;
public class NetworkMonitor extends StateMachine {
private static final boolean DBG = true;
private static final String TAG = "NetworkMonitor";
- private static final String DEFAULT_SERVER = "clients3.google.com";
+ private static final String DEFAULT_SERVER = "connectivitycheck.android.com";
private static final int SOCKET_TIMEOUT_MS = 10000;
public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
"android.net.conn.NETWORK_CONDITIONS_MEASURED";
@@ -163,6 +164,7 @@ public class NetworkMonitor extends StateMachine {
/**
* Force evaluation even if it has succeeded in the past.
* arg1 = UID responsible for requesting this reeval. Will be billed for data.
+ * arg2 = Number of evaluation attempts to make. (If 0, make INITIAL_ATTEMPTS attempts.)
*/
public static final int CMD_FORCE_REEVALUATION = BASE + 8;
@@ -212,11 +214,14 @@ public class NetworkMonitor extends StateMachine {
// Negative values disable reevaluation.
private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay";
- // Default to 5s reevaluation delay.
+ // When connecting, attempt to validate 3 times, pausing 5s between them.
private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000;
- private static final int MAX_RETRIES = 10;
- // Between groups of MAX_RETRIES evaluation attempts, pause 10 mins in hopes ISP outage passes.
+ private static final int INITIAL_ATTEMPTS = 3;
+ // If a network is not validated, make one attempt every 10 mins to see if it starts working.
private static final int REEVALUATE_PAUSE_MS = 10*60*1000;
+ private static final int PERIODIC_ATTEMPTS = 1;
+ // When an application calls reportBadNetwork, only make one attempt.
+ private static final int REEVALUATE_ATTEMPTS = 1;
private final int mReevaluateDelayMs;
private int mReevaluateToken = 0;
private static final int INVALID_UID = -1;
@@ -236,6 +241,14 @@ public class NetworkMonitor extends StateMachine {
// Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
private boolean mUserDoesNotWant = false;
+ // How many times we should attempt validation. Only checked in EvaluatingState; must be set
+ // before entering EvaluatingState. Note that whatever code causes us to transition to
+ // EvaluatingState last decides how many attempts will be made, so if one codepath were to
+ // enter EvaluatingState with a specific number of attempts, and then another were to enter it
+ // with a different number of attempts, the second number would be used. This is not currently
+ // a problem because EvaluatingState is not reentrant.
+ private int mMaxAttempts;
+
public boolean systemReady = false;
private final State mDefaultState = new DefaultState();
@@ -305,6 +318,7 @@ public class NetworkMonitor extends StateMachine {
return HANDLED;
case CMD_NETWORK_CONNECTED:
if (DBG) log("Connected");
+ mMaxAttempts = INITIAL_ATTEMPTS;
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_NETWORK_DISCONNECTED:
@@ -318,6 +332,7 @@ public class NetworkMonitor extends StateMachine {
case CMD_FORCE_REEVALUATION:
if (DBG) log("Forcing reevaluation");
mUidResponsibleForReeval = message.arg1;
+ mMaxAttempts = message.arg2 != 0 ? message.arg2 : REEVALUATE_ATTEMPTS;
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_CAPTIVE_PORTAL_APP_FINISHED:
@@ -347,7 +362,10 @@ public class NetworkMonitor extends StateMachine {
public void enter() {
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
- if (!mUserDoesNotWant) sendMessageDelayed(CMD_FORCE_REEVALUATION, REEVALUATE_PAUSE_MS);
+ if (!mUserDoesNotWant) {
+ sendMessageDelayed(CMD_FORCE_REEVALUATION, 0 /* no UID */,
+ PERIODIC_ATTEMPTS, REEVALUATE_PAUSE_MS);
+ }
}
@Override
@@ -413,11 +431,11 @@ public class NetworkMonitor extends StateMachine {
// Being in the EvaluatingState State indicates the Network is being evaluated for internet
// connectivity.
private class EvaluatingState extends State {
- private int mRetries;
+ private int mAttempt;
@Override
public void enter() {
- mRetries = 0;
+ mAttempt = 1;
sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
if (mUidResponsibleForReeval != INVALID_UID) {
TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
@@ -454,18 +472,18 @@ public class NetworkMonitor extends StateMachine {
transitionTo(mValidatedState);
return HANDLED;
}
- // Note: This call to isCaptivePortal() could take minutes. Resolving the
- // server's IP addresses could hit the DNS timeout and attempting connections
- // to each of the server's several (e.g. 11) IP addresses could each take
- // SOCKET_TIMEOUT_MS. During this time this StateMachine will be unresponsive.
- // isCaptivePortal() could be executed on another Thread if this is found to
- // cause problems.
+ // Note: This call to isCaptivePortal() could take up to a minute. Resolving the
+ // server's IP addresses could hit the DNS timeout, and attempting connections
+ // to each of the server's several IP addresses (currently one IPv4 and one
+ // IPv6) could each take SOCKET_TIMEOUT_MS. During this time this StateMachine
+ // will be unresponsive. isCaptivePortal() could be executed on another Thread
+ // if this is found to cause problems.
int httpResponseCode = isCaptivePortal();
if (httpResponseCode == 204) {
transitionTo(mValidatedState);
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
transitionTo(mCaptivePortalState);
- } else if (++mRetries > MAX_RETRIES) {
+ } else if (++mAttempt > mMaxAttempts) {
transitionTo(mOfflineState);
} else if (mReevaluateDelayMs >= 0) {
Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
@@ -639,12 +657,36 @@ public class NetworkMonitor extends StateMachine {
int httpResponseCode = 599;
try {
URL url = new URL("http", mServer, "/generate_204");
+ // On networks with a PAC instead of fetching a URL that should result in a 204
+ // reponse, we instead simply fetch the PAC script. This is done for a few reasons:
+ // 1. At present our PAC code does not yet handle multiple PACs on multiple networks
+ // until something like https://android-review.googlesource.com/#/c/115180/ lands.
+ // Network.openConnection() will ignore network-specific PACs and instead fetch
+ // using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with
+ // NO_PROXY is the fetch of the PAC itself.
+ // 2. To proxy the generate_204 fetch through a PAC would require a number of things
+ // happen before the fetch can commence, namely:
+ // a) the PAC script be fetched
+ // b) a PAC script resolver service be fired up and resolve mServer
+ // Network validation could be delayed until these prerequisities are satisifed or
+ // could simply be left to race them. Neither is an optimal solution.
+ // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
+ // fact block fetching of the generate_204 URL which would lead to false negative
+ // results for network validation.
+ boolean fetchPac = false;
+ {
+ final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy();
+ if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
+ url = new URL(proxyInfo.getPacFileUrl().toString());
+ fetchPac = true;
+ }
+ }
if (DBG) {
log("Checking " + url.toString() + " on " +
mNetworkAgentInfo.networkInfo.getExtraInfo());
}
urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
- urlConnection.setInstanceFollowRedirects(false);
+ urlConnection.setInstanceFollowRedirects(fetchPac);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
@@ -678,6 +720,11 @@ public class NetworkMonitor extends StateMachine {
httpResponseCode = 204;
}
+ if (httpResponseCode == 200 && fetchPac) {
+ if (DBG) log("PAC fetch 200 response interpreted as 204 response.");
+ httpResponseCode = 204;
+ }
+
sendNetworkConditionsBroadcast(true /* response received */,
httpResponseCode != 204 /* isCaptivePortal */,
requestTimestamp, responseTimestamp);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index ef86c6c..5ff7022 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -20,6 +20,7 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -45,8 +46,10 @@ import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -63,6 +66,8 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* @hide
@@ -976,6 +981,12 @@ public class Tethering extends BaseNetworkObserver {
if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
}
try {
+ mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
+ } catch (Exception e) {
+ if (VDBG) Log.e(
+ TAG, "Exception in removeInterfaceForward: " + e.toString());
+ }
+ try {
mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
} catch (Exception e) {
if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
@@ -1028,9 +1039,14 @@ public class Tethering extends BaseNetworkObserver {
if (newUpstreamIfaceName != null) {
try {
mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
+ mNMService.startInterfaceForwarding(mIfaceName,
+ newUpstreamIfaceName);
} catch (Exception e) {
Log.e(TAG, "Exception enabling Nat: " + e.toString());
try {
+ mNMService.disableNat(mIfaceName, newUpstreamIfaceName);
+ } catch (Exception ee) {}
+ try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
@@ -1377,6 +1393,112 @@ public class Tethering extends BaseNetworkObserver {
}
}
+ private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
+ private SimChangeBroadcastReceiver mBroadcastReceiver = null;
+
+ // keep consts in sync with packages/apps/Settings TetherSettings.java
+ private static final int WIFI_TETHERING = 0;
+ private static final int USB_TETHERING = 1;
+ private static final int BLUETOOTH_TETHERING = 2;
+
+ // keep consts in sync with packages/apps/Settings TetherService.java
+ private static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+ private static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+
+ private void startListeningForSimChanges() {
+ if (DBG) Log.d(TAG, "startListeningForSimChanges");
+ if (mBroadcastReceiver == null) {
+ mBroadcastReceiver = new SimChangeBroadcastReceiver(
+ mSimBcastGenerationNumber.incrementAndGet());
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ }
+ }
+
+ private void stopListeningForSimChanges() {
+ if (DBG) Log.d(TAG, "stopListeningForSimChanges");
+ if (mBroadcastReceiver != null) {
+ mSimBcastGenerationNumber.incrementAndGet();
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ mBroadcastReceiver = null;
+ }
+ }
+
+ class SimChangeBroadcastReceiver extends BroadcastReceiver {
+ // used to verify this receiver is still current
+ final private int mGenerationNumber;
+
+ // we're interested in edge-triggered LOADED notifications, so
+ // ignore LOADED unless we saw an ABSENT state first
+ private boolean mSimAbsentSeen = false;
+
+ public SimChangeBroadcastReceiver(int generationNumber) {
+ super();
+ mGenerationNumber = generationNumber;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DBG) {
+ Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
+ ", current generationNumber=" + mSimBcastGenerationNumber.get());
+ }
+ if (mGenerationNumber != mSimBcastGenerationNumber.get()) return;
+
+ final String state =
+ intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+
+ Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" +
+ mSimAbsentSeen);
+ if (!mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state)) {
+ mSimAbsentSeen = true;
+ }
+
+ if (mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state)) {
+ mSimAbsentSeen = false;
+ try {
+ if (mContext.getResources().getString(com.android.internal.R.string.
+ config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
+ final String tetherService = mContext.getResources().getString(
+ com.android.internal.R.string.config_wifi_tether_enable);
+ ArrayList<Integer> tethered = new ArrayList<Integer>();
+ synchronized (mPublicSync) {
+ Set ifaces = mIfaces.keySet();
+ for (Object iface : ifaces) {
+ TetherInterfaceSM sm = mIfaces.get(iface);
+ if (sm != null && sm.isTethered()) {
+ if (isUsb((String)iface)) {
+ tethered.add(new Integer(USB_TETHERING));
+ } else if (isWifi((String)iface)) {
+ tethered.add(new Integer(WIFI_TETHERING));
+ } else if (isBluetooth((String)iface)) {
+ tethered.add(new Integer(BLUETOOTH_TETHERING));
+ }
+ }
+ }
+ }
+ for (int tetherType : tethered) {
+ Intent startProvIntent = new Intent();
+ startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
+ startProvIntent.setComponent(
+ ComponentName.unflattenFromString(tetherService));
+ mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+ }
+ Log.d(TAG, "re-evaluate provisioning");
+ } else {
+ Log.d(TAG, "no prov-check needed for new SIM");
+ }
+ } catch (Resources.NotFoundException e) {
+ Log.d(TAG, "no prov-check needed for new SIM");
+ // not defined, do nothing
+ }
+ }
+ }
+ }
+
class InitialState extends TetherMasterUtilState {
@Override
public void enter() {
@@ -1413,6 +1535,7 @@ public class Tethering extends BaseNetworkObserver {
@Override
public void enter() {
turnOnMasterTetherSettings(); // may transition us out
+ startListeningForSimChanges();
mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; // better try something first pass
// or crazy tests cases will fail
@@ -1422,6 +1545,7 @@ public class Tethering extends BaseNetworkObserver {
@Override
public void exit() {
turnOffUpstreamMobileConnection();
+ stopListeningForSimChanges();
notifyTetheredOfNewUpstreamIface(null);
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index f08a652..8533f69 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -846,9 +846,29 @@ public class Vpn {
/**
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
+ *
+ * Should only be called to respond to Binder requests as this enforces caller permission. Use
+ * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
+ * permission check only when the caller is trusted (or the call is initiated by the system).
*/
public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
enforceControlPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ startLegacyVpnPrivileged(profile, keyStore, egress);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
+ * permissions under the assumption that the caller is the system.
+ *
+ * Callers are responsible for checking permissions if needed.
+ */
+ public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
+ LinkProperties egress) {
if (!keyStore.isUnlocked()) {
throw new IllegalStateException("KeyStore isn't unlocked");
}
@@ -959,10 +979,10 @@ public class Vpn {
}
private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
- stopLegacyVpn();
+ stopLegacyVpnPrivileged();
- // Prepare for the new request. This also checks the caller.
- prepare(null, VpnConfig.LEGACY_VPN);
+ // Prepare for the new request.
+ prepareInternal(VpnConfig.LEGACY_VPN);
updateState(DetailedState.CONNECTING, "startLegacyVpn");
// Start a new LegacyVpnRunner and we are done!
@@ -970,7 +990,8 @@ public class Vpn {
mLegacyVpnRunner.start();
}
- public synchronized void stopLegacyVpn() {
+ /** Stop legacy VPN. Permissions must be checked by callers. */
+ public synchronized void stopLegacyVpnPrivileged() {
if (mLegacyVpnRunner != null) {
mLegacyVpnRunner.exit();
mLegacyVpnRunner = null;
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index f549f3d..6e61e41 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -464,13 +464,13 @@ final class ColorFade {
try {
SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), s);
+ st.updateTexImage();
+ st.getTransformMatrix(mTexMatrix);
} finally {
s.release();
+ st.release();
}
- st.updateTexImage();
- st.getTransformMatrix(mTexMatrix);
-
// Set up texture coordinates for a quad.
// We might need to change this if the texture ends up being
// a different size from the display for some reason.
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 59d5605..7f48768 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -62,11 +62,6 @@ final class ActiveSourceHandler {
void process(ActiveSource newActive, int deviceType) {
// Seq #17
HdmiCecLocalDeviceTv tv = mSource;
- ActiveSource activeSource = tv.getActiveSource();
- if (activeSource.equals(newActive)) {
- invokeCallback(HdmiControlManager.RESULT_SUCCESS);
- return;
- }
HdmiDeviceInfo device = mService.getDeviceInfo(newActive.logicalAddress);
if (device == null) {
tv.startNewDeviceAction(newActive, deviceType);
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index f6d4efd..0c86aed 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -230,13 +230,17 @@ final class Constants {
static final int OPTION_CEC_ENABLE = 2;
// If set to disabled, system service yields control of CEC to sub-microcontroller.
- // If enabled, it take the control back.
+ // If enabled, it takes the control back.
static final int OPTION_CEC_SERVICE_CONTROL = 3;
// Put other devices to standby when TV goes to standby. enabled by default.
// If set to disabled, TV doesn't send <Standby> to other devices.
static final int OPTION_CEC_AUTO_DEVICE_OFF = 4;
+ // Passes the language used in the system when updated. The value to use is the 3 byte
+ // code as defined in ISO/FDIS 639-2.
+ static final int OPTION_CEC_SET_LANGUAGE = 5;
+
// If set to disabled, TV does not switch ports when mobile device is connected.
static final int OPTION_MHL_INPUT_SWITCHING = 101;
@@ -246,6 +250,10 @@ final class Constants {
// If set to disabled, all MHL commands are discarded.
static final int OPTION_MHL_ENABLE = 103;
+ // If set to disabled, system service yields control of MHL to sub-microcontroller.
+ // If enabled, it takes the control back.
+ static final int OPTION_MHL_SERVICE_CONTROL = 104;
+
static final int DISABLED = 0;
static final int ENABLED = 1;
diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
index 68311de..77ffe0b 100644
--- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
+++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
@@ -54,6 +54,7 @@ final class DelayedMessageBuffer {
mBuffer.add(message);
break;
case Constants.MESSAGE_INITIATE_ARC:
+ case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
mBuffer.add(message);
break;
default:
@@ -75,36 +76,51 @@ final class DelayedMessageBuffer {
}
}
- void processAllMessages() {
+ boolean isBuffered(int opcode) {
for (HdmiCecMessage message : mBuffer) {
+ if (message.getOpcode() == opcode) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void processAllMessages() {
+ // Use the copied buffer.
+ ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+ mBuffer.clear();
+ for (HdmiCecMessage message : copiedBuffer) {
mDevice.onMessage(message);
HdmiLogger.debug("Processing message:" + message);
}
- mBuffer.clear();
}
/**
* Process messages from a given logical device. Called by
* {@link NewDeviceAction} actions when they finish adding the device
* information.
- * <p>&lt;Active Source&gt; is not processed in this method but processed
- * separately via {@link #processActiveSource()}.
+ * <p>&lt;Active Source&gt; is processed only when the TV input is ready.
+ * If not, {@link #processActiveSource()} will be invoked later to handle it.
*
* @param address logical address of CEC device which the messages to process
* are associated with
*/
void processMessagesForDevice(int address) {
- HdmiLogger.debug("Processing message for address:" + address);
- for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) {
- HdmiCecMessage message = iter.next();
- if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
+ ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+ mBuffer.clear();
+ HdmiLogger.debug("Checking message for address:" + address);
+ for (HdmiCecMessage message : copiedBuffer) {
+ if (message.getSource() != address) {
+ mBuffer.add(message);
continue;
}
- if (message.getSource() == address) {
- mDevice.onMessage(message);
- HdmiLogger.debug("Processing message:" + message);
- iter.remove();
+ if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
+ && !mDevice.isInputReady(HdmiDeviceInfo.idForCecDevice(address))) {
+ mBuffer.add(message);
+ continue;
}
+ mDevice.onMessage(message);
+ HdmiLogger.debug("Processing message:" + message);
}
}
@@ -120,13 +136,15 @@ final class DelayedMessageBuffer {
* @param address logical address of the device to be the active source
*/
void processActiveSource(int address) {
- for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) {
- HdmiCecMessage message = iter.next();
+ ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+ mBuffer.clear();
+ for (HdmiCecMessage message : copiedBuffer) {
if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
&& message.getSource() == address) {
mDevice.onMessage(message);
HdmiLogger.debug("Processing message:" + message);
- iter.remove();
+ } else {
+ mBuffer.add(message);
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 4f8b9fb..d17e9b3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -191,6 +191,25 @@ abstract class HdmiCecLocalDevice {
protected abstract void setPreferredAddress(int addr);
/**
+ * Returns true if the TV input associated with the CEC device is ready
+ * to accept further processing such as input switching. This is used
+ * to buffer certain CEC commands and process it later if the input is not
+ * ready yet. For other types of local devices(non-TV), this method returns
+ * true by default to let the commands be processed right away.
+ */
+ protected boolean isInputReady(int deviceId) {
+ return true;
+ }
+
+ /**
+ * Returns true if the local device allows the system to be put to standby.
+ * The default implementation returns true.
+ */
+ protected boolean canGoToStandby() {
+ return true;
+ }
+
+ /**
* Dispatch incoming message.
*
* @param message incoming message
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 85a1a15..a8f6954 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -19,6 +19,8 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Slog;
@@ -34,17 +36,17 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
private boolean mIsActiveSource = false;
+ // Used to keep the device awake while it is the active source. For devices that
+ // cannot wake up via CEC commands, this address the inconvenience of having to
+ // turn them on.
+ // Lazily initialized - should call getWakeLock() to get the instance.
+ private WakeLock mWakeLock;
+
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
}
@Override
- void init() {
- super.init();
- mIsActiveSource = false;
- }
-
- @Override
@ServiceThreadOnly
protected void onAddressAllocated(int logicalAddress, int reason) {
assertRunOnServiceThread();
@@ -129,12 +131,37 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
if (connected && mService.isPowerStandbyOrTransient()) {
mService.wakeUp();
}
+ if (!connected) {
+ getWakeLock().release();
+ }
}
@ServiceThreadOnly
- void markActiveSource() {
+ void setActiveSource(boolean on) {
assertRunOnServiceThread();
- mIsActiveSource = true;
+ mIsActiveSource = on;
+ if (on) {
+ getWakeLock().acquire();
+ HdmiLogger.debug("active source: %b. Wake lock acquired", mIsActiveSource);
+ } else {
+ getWakeLock().release();
+ HdmiLogger.debug("Wake lock released");
+ }
+ }
+
+ @ServiceThreadOnly
+ private WakeLock getWakeLock() {
+ assertRunOnServiceThread();
+ if (mWakeLock == null) {
+ mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock.setReferenceCounted(false);
+ }
+ return mWakeLock;
+ }
+
+ @Override
+ protected boolean canGoToStandby() {
+ return !getWakeLock().isHeld();
}
@Override
@@ -148,7 +175,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
private void mayResetActiveSource(int physicalAddress) {
if (physicalAddress != mService.getPhysicalAddress()) {
- mIsActiveSource = false;
+ setActiveSource(false);
}
}
@@ -163,9 +190,9 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
return true; // Broadcast message.
}
- // Samsung model, we tested, sends <RoutingChange> and <RequestActiveSource> consecutively,
- // Then if there is no <ActiveSource> response, it will change the input to
- // the internal source. To handle this, we'll set ActiveSource aggressively.
+ // Samsung model we tested sends <Routing Change> and <Request Active Source>
+ // in a row, and then changes the input to the internal source if there is no
+ // <Active Source> in response. To handle this, we'll set ActiveSource aggressively.
@Override
@ServiceThreadOnly
protected boolean handleRoutingChange(HdmiCecMessage message) {
@@ -185,11 +212,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
}
private void maySetActiveSource(int physicalAddress) {
- if (physicalAddress == mService.getPhysicalAddress()) {
- mIsActiveSource = true;
- } else {
- mIsActiveSource = false;
- }
+ setActiveSource(physicalAddress == mService.getPhysicalAddress());
}
private void wakeUpIfActiveSource() {
@@ -226,7 +249,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
mAddress, mService.getPhysicalAddress()));
}
- mIsActiveSource = false;
+ setActiveSource(false);
checkIfPendingActionsCleared();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index f40010d..8241cdc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -34,6 +34,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.HdmiRecordSources;
import android.hardware.hdmi.HdmiTimerRecordSources;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -63,6 +64,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.HashMap;
/**
* Represent a logical device of type TV residing in Android system.
@@ -143,12 +145,44 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
public void onInputAdded(String inputId) {
TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId);
HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo();
- if (info != null && info.isCecDevice()) {
- mDelayedMessageBuffer.processActiveSource(info.getLogicalAddress());
+ if (info == null) return;
+ addTvInput(inputId, info.getId());
+ if (info.isCecDevice()) {
+ processDelayedActiveSource(info.getLogicalAddress());
}
}
+
+ @Override
+ public void onInputRemoved(String inputId) {
+ removeTvInput(inputId);
+ }
};
+ // Keeps the mapping (TV input ID, HDMI device ID) to keep track of the TV inputs ready to
+ // accept input switching request from HDMI devices. Requests for which the corresponding
+ // input ID is not yet registered by TV input framework need to be buffered for delayed
+ // processing.
+ private final HashMap<String, Integer> mTvInputs = new HashMap<>();
+
+ @ServiceThreadOnly
+ private void addTvInput(String inputId, int deviceId) {
+ assertRunOnServiceThread();
+ mTvInputs.put(inputId, deviceId);
+ }
+
+ @ServiceThreadOnly
+ private void removeTvInput(String inputId) {
+ assertRunOnServiceThread();
+ mTvInputs.remove(inputId);
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean isInputReady(int deviceId) {
+ assertRunOnServiceThread();
+ return mTvInputs.containsValue(deviceId);
+ }
+
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_TV);
mPrevPortId = Constants.INVALID_PORT_ID;
@@ -168,6 +202,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, mService.getVendorId()));
mCecSwitches.add(mService.getPhysicalAddress()); // TV is a CEC switch too.
+ mTvInputs.clear();
mSkipRoutingControl = (reason == HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE);
launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC &&
reason != HdmiControlService.INITIATED_BY_BOOT_UP);
@@ -188,18 +223,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
@Override
- @ServiceThreadOnly
protected int getPreferredAddress() {
- assertRunOnServiceThread();
- return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_TV,
- Constants.ADDR_UNREGISTERED);
+ return Constants.ADDR_TV;
}
@Override
- @ServiceThreadOnly
protected void setPreferredAddress(int addr) {
- assertRunOnServiceThread();
- SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_TV, String.valueOf(addr));
+ Slog.w(TAG, "Preferred addres will not be stored for TV");
}
@Override
@@ -326,8 +356,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
ActiveSource activeSource = getActiveSource();
HdmiDeviceInfo info = getCecDeviceInfo(activeSource.logicalAddress);
if (info == null) {
- info = new HdmiDeviceInfo(Constants.ADDR_INVALID, path, getActivePortId(),
- HdmiDeviceInfo.DEVICE_RESERVED, 0, null);
+ info = mService.getDeviceInfoByPort(getActivePortId());
+ if (info == null) {
+ // No CEC/MHL device is present at the port. Attempt to switch to
+ // the hardware port itself for non-CEC devices that may be connected.
+ info = new HdmiDeviceInfo(path, getActivePortId());
+ }
}
mService.invokeInputChangeListener(info);
}
@@ -398,11 +432,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return;
}
List<SendKeyAction> action = getActions(SendKeyAction.class);
+ int logicalAddress = findKeyReceiverAddress();
+ if (logicalAddress == mAddress) {
+ Slog.w(TAG, "Discard key event to itself :" + keyCode + " pressed:" + isPressed);
+ return;
+ }
if (!action.isEmpty()) {
action.get(0).processKeyEvent(keyCode, isPressed);
} else {
if (isPressed) {
- int logicalAddress = findKeyReceiverAddress();
if (logicalAddress != Constants.ADDR_INVALID) {
addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode));
return;
@@ -443,8 +481,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
if (info == null) {
if (!handleNewDeviceAtTheTailOfActivePath(physicalAddress)) {
+ HdmiLogger.debug("Device info %X not found; buffering the command", logicalAddress);
mDelayedMessageBuffer.add(message);
}
+ } else if (!isInputReady(info.getId())) {
+ HdmiLogger.debug("Input not ready for device: %X; buffering the command", info.getId());
+ mDelayedMessageBuffer.add(message);
} else {
ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType());
@@ -480,6 +522,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
doManualPortSwitching(portId, null);
setPrevPortId(Constants.INVALID_PORT_ID);
+ } else {
+ // No HDMI port to switch to was found. Notify the input change listers to
+ // switch to the lastly shown internal input.
+ mActiveSource.invalidate();
+ setActivePath(Constants.INVALID_PHYSICAL_ADDRESS);
+ mService.invokeInputChangeListener(HdmiDeviceInfo.INACTIVE_DEVICE);
}
return true;
}
@@ -537,6 +585,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (!isInDeviceList(address, path)) {
handleNewDeviceAtTheTailOfActivePath(path);
}
+
+ // Add the device ahead with default information to handle <Active Source>
+ // promptly, rather than waiting till the new device action is finished.
+ HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(address, path, getPortId(path), type,
+ Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address));
+ addCecDevice(deviceInfo);
startNewDeviceAction(ActiveSource.of(address, path), type);
return true;
}
@@ -742,7 +796,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
void onNewAvrAdded(HdmiDeviceInfo avr) {
assertRunOnServiceThread();
- if (getSystemAudioModeSetting()) {
+ if (getSystemAudioModeSetting() && !isSystemAudioActivated()) {
addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
}
if (isArcFeatureEnabled() && !hasAction(SetArcTransmissionStateAction.class)) {
@@ -835,6 +889,17 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return oldStatus;
}
+ @ServiceThreadOnly
+ private void updateArcFeatureStatus(int portId, boolean isConnected) {
+ assertRunOnServiceThread();
+ // HEAC 2.4, HEACT 5-15
+ // Should not activate ARC if +5V status is false.
+ HdmiPortInfo portInfo = mService.getPortInfo(portId);
+ if (portInfo.isArcSupported()) {
+ changeArcFeatureEnabled(isConnected);
+ }
+ }
+
private void notifyArcStatusToAudioService(boolean enabled) {
// Note that we don't set any name to ARC.
mService.getAudioManager().setWiredDeviceConnectionState(
@@ -1037,6 +1102,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
assertRunOnServiceThread();
if (!isMessageForSystemAudio(message)) {
+ if (getAvrDeviceInfo() == null) {
+ // AVR may not have been discovered yet. Delay the message processing.
+ mDelayedMessageBuffer.add(message);
+ return true;
+ }
HdmiLogger.warning("Invalid <Set System Audio Mode> message:" + message);
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
return true;
@@ -1380,7 +1450,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
} else {
int activePath = mService.getPhysicalAddress();
setActivePath(activePath);
- if (!routingForBootup) {
+ if (!routingForBootup
+ && !mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(mAddress,
activePath));
}
@@ -1407,6 +1478,25 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
/**
+ * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
+ * the given routing path. This is the version accessible safely from threads
+ * other than service thread.
+ *
+ * @param path routing path or physical address
+ * @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
+ */
+ HdmiDeviceInfo getSafeDeviceInfoByPath(int path) {
+ synchronized (mLock) {
+ for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+ if (info.getPhysicalAddress() == path) {
+ return info;
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
* Whether a device of the specified physical address and logical address exists
* in a device info list. However, both are minimal condition and it could
* be different device from the original one.
@@ -1416,7 +1506,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
* @return true if exist; otherwise false
*/
@ServiceThreadOnly
- private boolean isInDeviceList(int logicalAddress, int physicalAddress) {
+ boolean isInDeviceList(int logicalAddress, int physicalAddress) {
assertRunOnServiceThread();
HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress);
if (device == null) {
@@ -1441,6 +1531,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// It covers seq #40, #43.
hotplugActions.get(0).pollAllDevicesNow();
}
+ updateArcFeatureStatus(portId, connected);
}
private void removeCecSwitches(int portId) {
@@ -1583,7 +1674,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
announceOneTouchRecordResult(recorderAddress,
ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN);
- return Constants.ABORT_UNABLE_TO_DETERMINE;
+ return Constants.ABORT_CANNOT_PROVIDE_SOURCE;
}
addAndStartAction(new OneTouchRecordAction(this, recorderAddress, recordSource));
@@ -1767,6 +1858,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mDelayedMessageBuffer.processMessagesForDevice(address);
}
+ @ServiceThreadOnly
+ void processDelayedActiveSource(int address) {
+ assertRunOnServiceThread();
+ mDelayedMessageBuffer.processActiveSource(address);
+ }
+
@Override
protected void dump(final IndentingPrintWriter pw) {
super.dump(pw);
@@ -1777,6 +1874,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
pw.println("mAutoWakeup: " + mAutoWakeup);
pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
+ pw.println("mPrevPortId: " + mPrevPortId);
pw.println("CEC devices:");
pw.increaseIndent();
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8ce6caf..49a96d8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -23,9 +23,11 @@ import static com.android.server.hdmi.Constants.ENABLED;
import static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP;
import static com.android.server.hdmi.Constants.OPTION_CEC_ENABLE;
import static com.android.server.hdmi.Constants.OPTION_CEC_SERVICE_CONTROL;
+import static com.android.server.hdmi.Constants.OPTION_CEC_SET_LANGUAGE;
import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
+import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -277,12 +279,78 @@ public final class HdmiControlService extends SystemService {
@Nullable
private TvInputManager mTvInputManager;
+ @Nullable
+ private PowerManager mPowerManager;
+
// Last input port before switching to the MHL port. Should switch back to this port
// when the mobile device sends the request one touch play with off.
// Gets invalidated if we go to other port/input.
@ServiceThreadOnly
private int mLastInputMhl = Constants.INVALID_PORT_ID;
+ // Set to true if the logical address allocation is completed.
+ private boolean mAddressAllocated = false;
+
+ // Buffer for processing the incoming cec messages while allocating logical addresses.
+ private final class CecMessageBuffer {
+ private List<HdmiCecMessage> mBuffer = new ArrayList<>();
+
+ public void bufferMessage(HdmiCecMessage message) {
+ switch (message.getOpcode()) {
+ case Constants.MESSAGE_ACTIVE_SOURCE:
+ bufferActiveSource(message);
+ break;
+ case Constants.MESSAGE_IMAGE_VIEW_ON:
+ case Constants.MESSAGE_TEXT_VIEW_ON:
+ bufferImageOrTextViewOn(message);
+ break;
+ // Add here if new message that needs to buffer
+ default:
+ // Do not need to buffer messages other than above
+ break;
+ }
+ }
+
+ public void processMessages() {
+ for (final HdmiCecMessage message : mBuffer) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ handleCecCommand(message);
+ }
+ });
+ }
+ mBuffer.clear();
+ }
+
+ private void bufferActiveSource(HdmiCecMessage message) {
+ if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) {
+ mBuffer.add(message);
+ }
+ }
+
+ private void bufferImageOrTextViewOn(HdmiCecMessage message) {
+ if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) &&
+ !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) {
+ mBuffer.add(message);
+ }
+ }
+
+ // Returns true if the message is replaced
+ private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) {
+ for (int i = 0; i < mBuffer.size(); i++) {
+ HdmiCecMessage bufferedMessage = mBuffer.get(i);
+ if (bufferedMessage.getOpcode() == opcode) {
+ mBuffer.set(i, message);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer();
+
public HdmiControlService(Context context) {
super(context);
mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
@@ -346,6 +414,7 @@ public final class HdmiControlService extends SystemService {
// Register ContentObserver to monitor the settings change.
registerContentObserver();
}
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
}
@Override
@@ -353,6 +422,7 @@ public final class HdmiControlService extends SystemService {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mTvInputManager = (TvInputManager) getContext().getSystemService(
Context.TV_INPUT_SERVICE);
+ mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
}
}
@@ -370,6 +440,10 @@ public final class HdmiControlService extends SystemService {
mTvInputManager.unregisterCallback(callback);
}
+ PowerManager getPowerManager() {
+ return mPowerManager;
+ }
+
/**
* Called when the initialization of local devices is complete.
*/
@@ -466,7 +540,9 @@ public final class HdmiControlService extends SystemService {
}
private void initializeCec(int initiatedBy) {
+ mAddressAllocated = false;
mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED);
+ mCecController.setOption(OPTION_CEC_SET_LANGUAGE, HdmiUtils.languageToInt(mLanguage));
initializeLocalDevices(initiatedBy);
}
@@ -496,6 +572,8 @@ public final class HdmiControlService extends SystemService {
mCecController.clearLogicalAddress();
final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>();
final int[] finished = new int[1];
+ mAddressAllocated = allocatingDevices.isEmpty();
+
for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
mCecController.allocateLogicalAddress(localDevice.getType(),
localDevice.getPreferredAddress(), new AllocateAddressCallback() {
@@ -516,12 +594,14 @@ public final class HdmiControlService extends SystemService {
// Address allocation completed for all devices. Notify each device.
if (allocatingDevices.size() == ++finished[0]) {
+ mAddressAllocated = true;
if (initiatedBy != INITIATED_BY_HOTPLUG) {
// In case of the hotplug we don't call onInitializeCecComplete()
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
}
notifyAddressAllocated(allocatedDevices, initiatedBy);
+ mCecMessageBuffer.processMessages();
}
}
});
@@ -672,6 +752,16 @@ public final class HdmiControlService extends SystemService {
return tv() == null ? null : tv().getCecDeviceInfo(logicalAddress);
}
+ @ServiceThreadOnly
+ HdmiDeviceInfo getDeviceInfoByPort(int port) {
+ assertRunOnServiceThread();
+ HdmiMhlLocalDeviceStub info = mMhlController.getLocalDevice(port);
+ if (info != null) {
+ return info.getInfo();
+ }
+ return null;
+ }
+
/**
* Returns version of CEC.
*/
@@ -744,6 +834,10 @@ public final class HdmiControlService extends SystemService {
@ServiceThreadOnly
boolean handleCecCommand(HdmiCecMessage message) {
assertRunOnServiceThread();
+ if (!mAddressAllocated) {
+ mCecMessageBuffer.bufferMessage(message);
+ return true;
+ }
int errorCode = mMessageValidator.isValid(message);
if (errorCode != HdmiCecMessageValidator.OK) {
// We'll not response on the messages with the invalid source or destination
@@ -1102,7 +1196,8 @@ public final class HdmiControlService extends SystemService {
}
int activePath = tv.getActivePath();
if (activePath != HdmiDeviceInfo.PATH_INVALID) {
- return new HdmiDeviceInfo(activePath, tv.getActivePortId());
+ HdmiDeviceInfo info = tv.getSafeDeviceInfoByPath(activePath);
+ return (info != null) ? info : new HdmiDeviceInfo(activePath, tv.getActivePortId());
}
return null;
}
@@ -1409,6 +1504,11 @@ public final class HdmiControlService extends SystemService {
runOnServiceThread(new Runnable() {
@Override
public void run() {
+ HdmiMhlLocalDeviceStub mhlDevice = mMhlController.getLocalDeviceById(deviceId);
+ if (mhlDevice != null) {
+ mhlDevice.sendStandby();
+ return;
+ }
HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
if (device == null) {
Slog.w(TAG, "Local device not available");
@@ -1528,6 +1628,12 @@ public final class HdmiControlService extends SystemService {
mCecController.dump(pw);
pw.decreaseIndent();
}
+
+ pw.println("mMhlController: ");
+ pw.increaseIndent();
+ mMhlController.dump(pw);
+ pw.decreaseIndent();
+
pw.println("mPortInfo: ");
pw.increaseIndent();
for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
@@ -1849,8 +1955,7 @@ public final class HdmiControlService extends SystemService {
void wakeUp() {
assertRunOnServiceThread();
mWakeUpMessageReceived = true;
- PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
- pm.wakeUp(SystemClock.uptimeMillis());
+ mPowerManager.wakeUp(SystemClock.uptimeMillis());
// PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
// the intent, the sequence will continue at onWakeUp().
}
@@ -1859,8 +1964,7 @@ public final class HdmiControlService extends SystemService {
void standby() {
assertRunOnServiceThread();
mStandbyMessageReceived = true;
- PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
- pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
+ mPowerManager.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
// PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
// the intent, the sequence will continue at onStandby().
}
@@ -1886,6 +1990,7 @@ public final class HdmiControlService extends SystemService {
@ServiceThreadOnly
private void onStandby() {
assertRunOnServiceThread();
+ if (!canGoToStandby()) return;
mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
invokeVendorCommandListenersOnControlStateChanged(false,
HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
@@ -1906,6 +2011,13 @@ public final class HdmiControlService extends SystemService {
});
}
+ private boolean canGoToStandby() {
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ if (!device.canGoToStandby()) return false;
+ }
+ return true;
+ }
+
@ServiceThreadOnly
private void onLanguageChanged(String language) {
assertRunOnServiceThread();
@@ -1913,6 +2025,7 @@ public final class HdmiControlService extends SystemService {
if (isTvDeviceEnabled()) {
tv().broadcastMenuLanguage(language);
+ mCecController.setOption(OPTION_CEC_SET_LANGUAGE, HdmiUtils.languageToInt(language));
}
}
@@ -1955,7 +2068,9 @@ public final class HdmiControlService extends SystemService {
device.onStandby(mStandbyMessageReceived);
}
mStandbyMessageReceived = false;
+ mAddressAllocated = false;
mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED);
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
}
private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
@@ -2137,16 +2252,17 @@ public final class HdmiControlService extends SystemService {
assertRunOnServiceThread();
if (tv() == null) return;
final int lastInput = contentOn ? tv().getActivePortId() : Constants.INVALID_PORT_ID;
- tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() {
- @Override
- public void onComplete(int result) throws RemoteException {
- // Keep the last input to switch back later when RAP[ContentOff] is received.
- // This effectively sets the port to invalid one if the switching is for
- // RAP[ContentOff].
- setLastInputForMhl(lastInput);
- }
- });
-
+ if (portId != Constants.INVALID_PORT_ID) {
+ tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) throws RemoteException {
+ // Keep the last input to switch back later when RAP[ContentOff] is received.
+ // This effectively sets the port to invalid one if the switching is for
+ // RAP[ContentOff].
+ setLastInputForMhl(lastInput);
+ }
+ });
+ }
// MHL device is always directly connected to the port. Update the active port ID to avoid
// unnecessary post-routing control task.
tv().setActivePortId(portId);
@@ -2156,7 +2272,8 @@ public final class HdmiControlService extends SystemService {
// may not be the MHL-enabled one. In this case the device info to be passed to
// input change listener should be the one describing the corresponding HDMI port.
HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
- HdmiDeviceInfo info = (device != null) ? device.getInfo() : mPortDeviceMap.get(portId);
+ HdmiDeviceInfo info = (device != null) ? device.getInfo()
+ : mPortDeviceMap.get(portId, HdmiDeviceInfo.INACTIVE_DEVICE);
invokeInputChangeListener(info);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java b/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java
index 708aee6..3883200 100644
--- a/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java
+++ b/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java
@@ -19,6 +19,7 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiPortInfo;
import android.util.SparseArray;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
/**
@@ -135,4 +136,7 @@ final class HdmiMhlControllerStub {
int getEcbusDeviceRoles(int portId) {
return INVALID_DEVICE_ROLES;
}
+
+ void dump(IndentingPrintWriter pw) {
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java b/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
index 53a7c5c..06ecb5a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
+++ b/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
@@ -43,4 +43,7 @@ final class HdmiMhlLocalDeviceStub {
void sendKeyEvent(int keycode, boolean isPressed) {
}
+
+ void sendStandby() {
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 22a519b..9aa9290 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -292,4 +292,17 @@ final class HdmiUtils {
info.getVendorId(), info.getDisplayName(), newPowerStatus);
}
+ /**
+ * Convert 3 byte-long language code in string to integer representation.
+ * English(eng), for example, is converted to 0x656e67.
+ *
+ * @param language language code in string
+ * @return language code in integer representation
+ */
+ static int languageToInt(String language) {
+ String normalized = language.toLowerCase();
+ return ((normalized.charAt(0) & 0xFF) << 16)
+ | ((normalized.charAt(1) & 0xFF) << 8)
+ | (normalized.charAt(2) & 0xFF);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 722be71..1bbd038 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -38,6 +38,7 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
private static final int POLLING_INTERVAL_MS = 5000;
private static final int TIMEOUT_COUNT = 3;
+ private static final int AVR_COUNT_MAX = 3;
// State in which waits for next polling
private static final int STATE_WAIT_FOR_NEXT_POLLING = 1;
@@ -48,6 +49,12 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
private int mTimeoutCount = 0;
+ // Counter used to ensure the connection to AVR is stable. Occasional failure to get
+ // polling response from AVR despite its presence leads to unstable status flipping.
+ // This is a workaround to deal with it, by removing the device only if the removal
+ // is detected {@code AVR_COUNT_MAX} times in a row.
+ private int mAvrStatusCount = 0;
+
/**
* Constructor
*
@@ -148,10 +155,22 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
BitSet removed = complement(currentInfos, polledResult);
int index = -1;
while ((index = removed.nextSetBit(index + 1)) != -1) {
+ if (index == Constants.ADDR_AUDIO_SYSTEM) {
+ ++mAvrStatusCount;
+ Slog.w(TAG, "Ack not returned from AVR. count: " + mAvrStatusCount);
+ if (mAvrStatusCount < AVR_COUNT_MAX) {
+ continue;
+ }
+ }
Slog.v(TAG, "Remove device by hot-plug detection:" + index);
removeDevice(index);
}
+ // Reset the counter if the ack is returned from AVR.
+ if (!removed.get(Constants.ADDR_AUDIO_SYSTEM)) {
+ mAvrStatusCount = 0;
+ }
+
// Next, check added devices.
BitSet added = complement(polledResult, currentInfos);
index = -1;
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 3d64cc5..6753368 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -163,6 +163,12 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
}
private void addDeviceInfo() {
+ // The device should be in the device list with default information.
+ if (!tv().isInDeviceList(mDeviceLogicalAddress, mDevicePhysicalAddress)) {
+ Slog.w(TAG, String.format("Device not found (%02x, %04x)",
+ mDeviceLogicalAddress, mDevicePhysicalAddress));
+ return;
+ }
if (mDisplayName == null) {
mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress);
}
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index e764a1c..a711102 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -82,7 +82,7 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
private void broadcastActiveSource() {
sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
// Because only playback device can create this action, it's safe to cast.
- playback().markActiveSource();
+ playback().setActiveSource(true);
}
private void queryDevicePowerStatus() {
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 435ab7f..ce5b9ab 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -119,10 +119,9 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
private void handleReportPowerStatus(int devicePowerStatus) {
if (isPowerOnOrTransient(getTvPowerStatus())) {
+ tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
if (isPowerOnOrTransient(devicePowerStatus)) {
sendSetStreamPath();
- } else {
- tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
}
}
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index eef5010..40d2583 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -35,14 +35,25 @@ import android.view.KeyEvent;
final class SendKeyAction extends HdmiCecFeatureAction {
private static final String TAG = "SendKeyAction";
+ // If the first key press lasts this much amount of time without any other key event
+ // coming down, we trigger the press-and-hold operation. Set to the value slightly
+ // shorter than the threshold(500ms) between two successive key press events
+ // as specified in the standard for the operation.
+ private static final int AWAIT_LONGPRESS_MS = 400;
+
// Amount of time this action waits for a new release key input event. When timed out,
// the action sends out UCR and finishes its lifecycle. Used to deal with missing key release
// event, which can lead the device on the receiving end to generating unintended key repeats.
private static final int AWAIT_RELEASE_KEY_MS = 1000;
- // State in which the action is at work. The state is set in {@link #start()} and
- // persists throughout the process till it is set back to {@code STATE_NONE} at the end.
- private static final int STATE_PROCESSING_KEYCODE = 1;
+ // State in which the long press is being checked at the beginning. The state is set in
+ // {@link #start()} and lasts for {@link #AWAIT_LONGPRESS_MS}.
+ private static final int STATE_CHECKING_LONGPRESS = 1;
+
+ // State in which the action is handling incoming keys. Persists throughout the process
+ // till it is set back to {@code STATE_NONE} at the end when a release key event for
+ // the last key is processed.
+ private static final int STATE_PROCESSING_KEYCODE = 2;
// Logical address of the device to which the UCP/UCP commands are sent.
private final int mTargetAddress;
@@ -77,8 +88,8 @@ final class SendKeyAction extends HdmiCecFeatureAction {
finish();
return true;
}
- mState = STATE_PROCESSING_KEYCODE;
- addTimer(mState, AWAIT_RELEASE_KEY_MS);
+ mState = STATE_CHECKING_LONGPRESS;
+ addTimer(mState, AWAIT_LONGPRESS_MS);
return true;
}
@@ -93,7 +104,7 @@ final class SendKeyAction extends HdmiCecFeatureAction {
* @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN}
*/
void processKeyEvent(int keycode, boolean isPressed) {
- if (mState != STATE_PROCESSING_KEYCODE) {
+ if (mState != STATE_CHECKING_LONGPRESS && mState != STATE_PROCESSING_KEYCODE) {
Slog.w(TAG, "Not in a valid state");
return;
}
@@ -152,12 +163,23 @@ final class SendKeyAction extends HdmiCecFeatureAction {
@Override
public void handleTimerEvent(int state) {
- // Timeout on waiting for the release key event. Send UCR and quit the action.
- if (mState != STATE_PROCESSING_KEYCODE) {
- Slog.w(TAG, "Not in a valid state");
- return;
+ switch (mState) {
+ case STATE_CHECKING_LONGPRESS:
+ // The first key press lasts long enough to start press-and-hold.
+ mActionTimer.clearTimerMessage();
+ mState = STATE_PROCESSING_KEYCODE;
+ sendKeyDown(mLastKeycode);
+ mLastSendKeyTime = getCurrentTime();
+ addTimer(mState, AWAIT_RELEASE_KEY_MS);
+ break;
+ case STATE_PROCESSING_KEYCODE:
+ // Timeout on waiting for the release key event. Send UCR and quit the action.
+ sendKeyUp();
+ finish();
+ break;
+ default:
+ Slog.w(TAG, "Not in a valid state");
+ break;
}
- sendKeyUp();
- finish();
}
}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 9c0f987..5ae6300 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -648,7 +648,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Register for SubscriptionInfo list changes which is guaranteed
// to invoke onSubscriptionsChanged the first time.
SubscriptionManager.from(mContext)
- .registerOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+ .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
// construct handler, listen for events
mHandler = new ProviderHandler(looper);
@@ -788,9 +788,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
if (info != null) {
- boolean dataEnabled = TelephonyManager.getIntWithSubId(mContext.getContentResolver(),
- Settings.Global.MOBILE_DATA, SubscriptionManager.getDefaultSubId(),
- 1) == 1;
+ boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
boolean networkAvailable = info.isAvailable() && dataEnabled;
String defaultApn = getSelectedApn();
if (defaultApn == null) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1fba7bb..53ae1ab 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -93,7 +93,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private final MediaSessionService mService;
private final boolean mUseMasterVolume;
- private final IBinder mICallback = new Binder();
private final Object mLock = new Object();
private final ArrayList<ISessionControllerCallback> mControllerCallbacks =
new ArrayList<ISessionControllerCallback>();
@@ -260,13 +259,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
boolean isMasterMute = mAudioManager.isMasterMute();
if (isMute) {
mAudioManagerInternal.setMasterMuteForUid(!isMasterMute,
- flags, packageName, mICallback, uid);
+ flags, packageName, mService.mICallback, uid);
} else {
mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName,
uid);
if (isMasterMute) {
mAudioManagerInternal.setMasterMuteForUid(false,
- flags, packageName, mICallback, uid);
+ flags, packageName, mService.mICallback, uid);
}
}
return;
@@ -280,7 +279,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
} else {
mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
flags, packageName, uid);
- if (isStreamMute) {
+ if (isStreamMute && direction != 0) {
mAudioManager.setStreamMute(stream, false);
}
}
@@ -295,7 +294,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName,
uid);
- if (isStreamMute) {
+ if (isStreamMute && direction != 0) {
mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE,
false);
}
@@ -307,7 +306,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
} else {
mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
packageName, uid);
- if (isStreamMute) {
+ if (isStreamMute && direction != 0) {
mAudioManager.setStreamMute(stream, false);
}
}
@@ -331,6 +330,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
if (volumeBefore != mOptimisticVolume) {
pushVolumeUpdate();
}
+ mService.notifyRemoteVolumeChanged(flags, this);
if (DEBUG) {
Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is "
@@ -358,6 +358,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
if (volumeBefore != mOptimisticVolume) {
pushVolumeUpdate();
}
+ mService.notifyRemoteVolumeChanged(flags, this);
if (DEBUG) {
Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is "
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 82be1bb..77a1fa9 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -77,6 +77,8 @@ public class MediaSessionService extends SystemService implements Monitor {
private static final int WAKELOCK_TIMEOUT = 5000;
+ /* package */final IBinder mICallback = new Binder();
+
private final SessionManagerImpl mSessionManagerImpl;
private final MediaSessionStack mPriorityStack;
@@ -91,6 +93,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
+ private AudioManager mAudioManager;
private ContentResolver mContentResolver;
private SettingsObserver mSettingsObserver;
@@ -118,6 +121,7 @@ public class MediaSessionService extends SystemService implements Monitor {
mKeyguardManager =
(KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
mAudioService = getAudioService();
+ mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mContentResolver = getContext().getContentResolver();
mSettingsObserver = new SettingsObserver();
mSettingsObserver.observe();
@@ -139,6 +143,20 @@ public class MediaSessionService extends SystemService implements Monitor {
mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
}
+ /**
+ * Tells the system UI that volume has changed on a remote session.
+ */
+ public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
+ if (mRvc == null) {
+ return;
+ }
+ try {
+ mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Error sending volume change to system UI.", e);
+ }
+ }
+
public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
boolean updateSessions = false;
synchronized (mLock) {
@@ -589,8 +607,6 @@ public class MediaSessionService extends SystemService implements Monitor {
"android.media.AudioService.WAKELOCK_ACQUIRED";
private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
- private final IBinder mICallback = new Binder();
-
private boolean mVoiceButtonDown = false;
private boolean mVoiceButtonHandled = false;
@@ -836,19 +852,23 @@ public class MediaSessionService extends SystemService implements Monitor {
mAudioService.setMasterMute(!isMasterMute, flags, packageName, mICallback);
} else {
mAudioService.adjustMasterVolume(direction, flags, packageName);
- if (isMasterMute) {
+ // Do not call setMasterMute when direction = 0 which is used just to
+ // show the UI.
+ if (isMasterMute && direction != 0) {
mAudioService.setMasterMute(false, flags, packageName, mICallback);
}
}
} else {
boolean isStreamMute = mAudioService.isStreamMute(suggestedStream);
if (direction == MediaSessionManager.DIRECTION_MUTE) {
- mAudioService.setStreamMute(suggestedStream, !isStreamMute, mICallback);
+ mAudioManager.setStreamMute(suggestedStream, !isStreamMute);
} else {
mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
flags, packageName);
- if (isStreamMute) {
- mAudioService.setStreamMute(suggestedStream, false, mICallback);
+ // Do not call setStreamMute when direction = 0 which is used just to
+ // show the UI.
+ if (isStreamMute && direction != 0) {
+ mAudioManager.setStreamMute(suggestedStream, false);
}
}
}
@@ -858,14 +878,6 @@ public class MediaSessionService extends SystemService implements Monitor {
} else {
session.adjustVolume(direction, flags, getContext().getPackageName(),
UserHandle.myUserId(), true);
- if (session.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE
- && mRvc != null && direction != MediaSessionManager.DIRECTION_MUTE) {
- try {
- mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
- } catch (Exception e) {
- Log.wtf(TAG, "Error sending volume change to system UI.", e);
- }
- }
}
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index e9b3f8b..bfdc400 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -98,6 +98,11 @@ public final class MediaProjectionManagerService extends SystemService
@Override
public void onSwitchUser(int userId) {
mMediaRouter.rebindAsUser(userId);
+ synchronized (mLock) {
+ if (mProjectionGrant != null) {
+ mProjectionGrant.stop();
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 3a1e4a4..752614f 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -140,7 +140,7 @@ public class LockdownVpnTracker {
if (egressDisconnected || egressChanged) {
clearSourceRulesLocked();
mAcceptedEgressIface = null;
- mVpn.stopLegacyVpn();
+ mVpn.stopLegacyVpnPrivileged();
}
if (egressDisconnected) {
hideNotification();
@@ -163,7 +163,9 @@ public class LockdownVpnTracker {
mAcceptedEgressIface = egressProp.getInterfaceName();
try {
- mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp);
+ // Use the privileged method because Lockdown VPN is initiated by the system, so
+ // no additional permission checks are necessary.
+ mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Slog.e(TAG, "Failed to start VPN", e);
@@ -250,7 +252,7 @@ public class LockdownVpnTracker {
mAcceptedEgressIface = null;
mErrorCount = 0;
- mVpn.stopLegacyVpn();
+ mVpn.stopLegacyVpnPrivileged();
try {
mNetService.setFirewallEgressDestRule(mProfile.server, 500, false);
mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false);
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 64d77c1..ab53fbc 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -168,7 +168,6 @@ public class ConditionProviders extends ManagedServices {
@Override
protected void onServiceAdded(ManagedServiceInfo info) {
- Slog.d(TAG, "onServiceAdded " + info);
final IConditionProvider provider = provider(info);
try {
provider.onConnected();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 825627f..f49d77d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -683,6 +683,9 @@ public class NotificationManagerService extends SystemService {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
boolean queryRestart = false;
boolean queryRemove = false;
@@ -928,6 +931,7 @@ public class NotificationManagerService extends SystemService {
mDisableNotificationEffects = true;
}
mZenModeHelper.readZenModeFromSetting();
+ mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
mUserProfiles.updateCache(getContext());
listenForCallState();
@@ -1668,6 +1672,7 @@ public class NotificationManagerService extends SystemService {
if (filter == null || zenOnly) {
pw.println("\n Zen Mode:");
+ pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
mZenModeHelper.dump(pw, " ");
pw.println("\n Zen Log:");
@@ -1829,7 +1834,7 @@ public class NotificationManagerService extends SystemService {
// can to avoid extracting signals.
handleGroupedNotificationLocked(r, old, callingUid, callingPid);
boolean ignoreNotification =
- removeUnusedGroupedNotificationLocked(r, callingUid, callingPid);
+ removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
@@ -1966,7 +1971,8 @@ public class NotificationManagerService extends SystemService {
*
* <p>Returns true if the given notification is a child of a group with a
* summary, which means that SysUI will never show it, and hence the new
- * notification can be safely ignored.</p>
+ * notification can be safely ignored. Also cancels any previous instance
+ * of the ignored notification.</p>
*
* <p>For summaries, cancels all children of that group, as SysUI will
* never show them anymore.</p>
@@ -1974,7 +1980,7 @@ public class NotificationManagerService extends SystemService {
* @return true if the given notification can be ignored as an optimization
*/
private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
- int callingUid, int callingPid) {
+ NotificationRecord old, int callingUid, int callingPid) {
// No optimizations are possible if listeners want groups.
if (mListeners.notificationGroupsDesired()) {
return false;
@@ -1992,6 +1998,13 @@ public class NotificationManagerService extends SystemService {
Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
+ summary.getKey());
}
+ // Make sure we don't leave an old version of the notification around.
+ if (old != null) {
+ if (DBG) {
+ Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
+ }
+ cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
+ }
return true;
} else if (isSummary) {
// Summary -> cancel children
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 91637e3..cc0a30a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3281,7 +3281,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// If the result set is different from when this
// was created, we need to clear it and re-ask the
// user their preference, if we're looking for an "always" type entry.
- if (always && !pa.mPref.sameSet(query, priority)) {
+ if (always && !pa.mPref.sameSet(query)) {
Slog.i(TAG, "Result set changed, dropping preferred activity for "
+ intent + " type " + resolvedType);
if (DEBUG_PREFERRED) {
@@ -8636,7 +8636,11 @@ public class PackageManagerService extends IPackageManager.Stub {
if (DEBUG_INSTALL) Log.v(TAG, "token " + token
+ " to BM for possible restore");
try {
- bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
+ if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
+ bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
+ } else {
+ doRestore = false;
+ }
} catch (RemoteException e) {
// can't happen; the backup manager is local
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index 69c1909..8e2e0cd 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -192,7 +192,7 @@ public class PreferredComponent {
}
}
- public boolean sameSet(List<ResolveInfo> query, int priority) {
+ public boolean sameSet(List<ResolveInfo> query) {
if (mSetPackages == null) {
return query == null;
}
@@ -201,10 +201,10 @@ public class PreferredComponent {
}
final int NQ = query.size();
final int NS = mSetPackages.length;
+
int numMatch = 0;
for (int i=0; i<NQ; i++) {
ResolveInfo ri = query.get(i);
- if (ri.priority != priority) continue;
ActivityInfo ai = ri.activityInfo;
boolean good = false;
for (int j=0; j<NS; j++) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index be3251c..d484b8f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -320,6 +320,9 @@ public class UserManagerService extends IUserManager.Stub {
checkManageUsersPermission("get the profile parent");
synchronized (mPackagesLock) {
UserInfo profile = getUserInfoLocked(userHandle);
+ if (profile == null) {
+ return null;
+ }
int parentUserId = profile.profileGroupId;
if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) {
return null;
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 64a67fc..22fee22 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -63,7 +63,7 @@ public class TelecomLoaderService extends SystemService {
private static final ComponentName SERVICE_COMPONENT = new ComponentName(
"com.android.server.telecom",
- "com.android.server.telecom.TelecomService");
+ "com.android.server.telecom.components.TelecomService");
private static final String SERVICE_ACTION = "com.android.ITelecomService";
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index c12dd63..de271b8 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -20,6 +20,7 @@ import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvStreamConfig;
import android.os.Handler;
import android.os.Message;
+import android.os.MessageQueue;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -53,7 +54,7 @@ final class TvInputHal implements Handler.Callback {
public void onFirstFrameCaptured(int deviceId, int streamId);
}
- private native long nativeOpen();
+ private native long nativeOpen(MessageQueue queue);
private static native int nativeAddOrUpdateStream(long ptr, int deviceId, int streamId,
Surface surface);
@@ -76,7 +77,7 @@ final class TvInputHal implements Handler.Callback {
public void init() {
synchronized (mLock) {
- mPtr = nativeOpen();
+ mPtr = nativeOpen(mHandler.getLooper().getQueue());
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 7786034..50b2262 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -216,10 +216,13 @@ class TvInputHardwareManager implements TvInputHal.Callback {
mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget();
}
- try {
- connection.getCallbackLocked().onStreamConfigChanged(configs);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onStreamConfigurationChanged", e);
+ ITvInputHardwareCallback callback = connection.getCallbackLocked();
+ if (callback != null) {
+ try {
+ callback.onStreamConfigChanged(configs);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onStreamConfigurationChanged", e);
+ }
}
}
}
@@ -664,7 +667,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
public void onServiceDied() {
synchronized (mImplLock) {
mAudioSource = null;
- mAudioSink = null;
+ mAudioSink.clear();
mAudioPatch = null;
}
}
@@ -672,9 +675,11 @@ class TvInputHardwareManager implements TvInputHal.Callback {
private int mOverrideAudioType = AudioManager.DEVICE_NONE;
private String mOverrideAudioAddress = "";
private AudioDevicePort mAudioSource;
- private AudioDevicePort mAudioSink;
+ private List<AudioDevicePort> mAudioSink = new ArrayList<>();
private AudioPatch mAudioPatch = null;
- private float mCommittedVolume = 0.0f;
+ // Set to an invalid value for a volume, so that current volume can be applied at the
+ // first call to updateAudioConfigLocked().
+ private float mCommittedVolume = -1f;
private float mSourceVolume = 0.0f;
private TvStreamConfig mActiveConfig = null;
@@ -688,22 +693,23 @@ class TvInputHardwareManager implements TvInputHal.Callback {
mAudioManager.registerAudioPortUpdateListener(mAudioListener);
if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
- mAudioSink = findAudioSinkFromAudioPolicy();
+ findAudioSinkFromAudioPolicy(mAudioSink);
}
}
- private AudioDevicePort findAudioSinkFromAudioPolicy() {
+ private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
+ sinks.clear();
ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
- if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
- int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
- for (AudioPort port : devicePorts) {
- AudioDevicePort devicePort = (AudioDevicePort) port;
- if ((devicePort.type() & sinkDevice) != 0) {
- return devicePort;
- }
+ if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
+ return;
+ }
+ int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
+ for (AudioPort port : devicePorts) {
+ AudioDevicePort devicePort = (AudioDevicePort) port;
+ if ((devicePort.type() & sinkDevice) != 0) {
+ sinks.add(devicePort);
}
}
- return null;
}
private AudioDevicePort findAudioDevicePort(int type, String address) {
@@ -789,7 +795,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
// We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
// because Java won't evaluate the latter if the former is true.
- if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) {
+ if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
if (mAudioPatch != null) {
mAudioManager.releaseAudioPatch(mAudioPatch);
mAudioPatch = null;
@@ -828,45 +834,53 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
AudioPortConfig sourceConfig = mAudioSource.activeConfig();
- AudioPortConfig sinkConfig = mAudioSink.activeConfig();
+ List<AudioPortConfig> sinkConfigs = new ArrayList<>();
AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
- int sinkSamplingRate = mDesiredSamplingRate;
- int sinkChannelMask = mDesiredChannelMask;
- int sinkFormat = mDesiredFormat;
- // If sinkConfig != null and values are set to default, fill in the sinkConfig values.
- if (sinkConfig != null) {
- if (sinkSamplingRate == 0) {
- sinkSamplingRate = sinkConfig.samplingRate();
- }
- if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
- sinkChannelMask = sinkConfig.channelMask();
- }
- if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
- sinkChannelMask = sinkConfig.format();
+ for (AudioDevicePort audioSink : mAudioSink) {
+ AudioPortConfig sinkConfig = audioSink.activeConfig();
+ int sinkSamplingRate = mDesiredSamplingRate;
+ int sinkChannelMask = mDesiredChannelMask;
+ int sinkFormat = mDesiredFormat;
+ // If sinkConfig != null and values are set to default,
+ // fill in the sinkConfig values.
+ if (sinkConfig != null) {
+ if (sinkSamplingRate == 0) {
+ sinkSamplingRate = sinkConfig.samplingRate();
+ }
+ if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
+ sinkChannelMask = sinkConfig.channelMask();
+ }
+ if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
+ sinkChannelMask = sinkConfig.format();
+ }
}
- }
- if (sinkConfig == null
- || sinkConfig.samplingRate() != sinkSamplingRate
- || sinkConfig.channelMask() != sinkChannelMask
- || sinkConfig.format() != sinkFormat) {
- // Check for compatibility and reset to default if necessary.
- if (!intArrayContains(mAudioSink.samplingRates(), sinkSamplingRate)
- && mAudioSink.samplingRates().length > 0) {
- sinkSamplingRate = mAudioSink.samplingRates()[0];
- }
- if (!intArrayContains(mAudioSink.channelMasks(), sinkChannelMask)) {
- sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
- }
- if (!intArrayContains(mAudioSink.formats(), sinkFormat)) {
- sinkFormat = AudioFormat.ENCODING_DEFAULT;
+ if (sinkConfig == null
+ || sinkConfig.samplingRate() != sinkSamplingRate
+ || sinkConfig.channelMask() != sinkChannelMask
+ || sinkConfig.format() != sinkFormat) {
+ // Check for compatibility and reset to default if necessary.
+ if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
+ && audioSink.samplingRates().length > 0) {
+ sinkSamplingRate = audioSink.samplingRates()[0];
+ }
+ if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
+ sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
+ }
+ if (!intArrayContains(audioSink.formats(), sinkFormat)) {
+ sinkFormat = AudioFormat.ENCODING_DEFAULT;
+ }
+ sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
+ sinkFormat, null);
+ shouldRecreateAudioPatch = true;
}
- sinkConfig = mAudioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
- sinkFormat, null);
- shouldRecreateAudioPatch = true;
+ sinkConfigs.add(sinkConfig);
}
+ // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
+ // non-empty at the beginning of this method.
+ AudioPortConfig sinkConfig = sinkConfigs.get(0);
if (sourceConfig == null || sourceGainConfig != null) {
int sourceSamplingRate = 0;
if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
@@ -896,7 +910,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
mAudioManager.createAudioPatch(
audioPatchArray,
new AudioPortConfig[] { sourceConfig },
- new AudioPortConfig[] { sinkConfig });
+ sinkConfigs.toArray(new AudioPortConfig[0]));
mAudioPatch = audioPatchArray[0];
if (sourceGainConfig != null) {
mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
@@ -974,17 +988,24 @@ class TvInputHardwareManager implements TvInputHal.Callback {
if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
return false;
}
- AudioDevicePort previousSink = mAudioSink;
+ List<AudioDevicePort> previousSink = mAudioSink;
+ mAudioSink = new ArrayList<>();
if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
- mAudioSink = findAudioSinkFromAudioPolicy();
+ findAudioSinkFromAudioPolicy(mAudioSink);
} else {
AudioDevicePort audioSink =
findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
if (audioSink != null) {
- mAudioSink = audioSink;
+ mAudioSink.add(audioSink);
}
}
- return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink);
+
+ // Returns true if mAudioSink and previousSink differs.
+ if (mAudioSink.size() != previousSink.size()) {
+ return true;
+ }
+ previousSink.removeAll(mAudioSink);
+ return !previousSink.isEmpty();
}
private void handleAudioSinkUpdated() {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index f859fd2..da25c53 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -304,10 +304,11 @@ class AppWindowToken extends WindowToken {
pw.print(prefix); pw.print("inPendingTransaction=");
pw.println(inPendingTransaction);
}
- if (startingData != null || removed || firstWindowDrawn) {
+ if (startingData != null || removed || firstWindowDrawn || mDeferRemoval) {
pw.print(prefix); pw.print("startingData="); pw.print(startingData);
pw.print(" removed="); pw.print(removed);
- pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn);
+ pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
+ pw.print(" mDeferRemoval="); pw.println(mDeferRemoval);
}
if (startingWindow != null || startingView != null
|| startingDisplayed || startingMoved) {
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index a7d41fa..9fdfc47 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -24,6 +24,7 @@ import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.view.Display;
import android.view.Surface;
@@ -35,7 +36,6 @@ import android.util.Slog;
class CircularDisplayMask {
private static final String TAG = "CircularDisplayMask";
- private static final int STROKE_WIDTH = 2;
// size of the chin
private int mScreenOffset = 0;
// Display dimensions
@@ -82,9 +82,7 @@ class CircularDisplayMask {
mDrawNeeded = true;
mPaint = new Paint();
mPaint.setAntiAlias(true);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setColor(Color.BLACK);
- mPaint.setStrokeWidth(STROKE_WIDTH);
+ mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mScreenOffset = screenOffset;
}
@@ -104,7 +102,6 @@ class CircularDisplayMask {
if (c == null) {
return;
}
- c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
switch (mRotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_90:
@@ -122,7 +119,10 @@ class CircularDisplayMask {
}
int circleRadius = mScreenSize.x / 2;
- c.drawCircle(circleRadius, circleRadius, circleRadius, mPaint);
+ c.drawColor(Color.BLACK);
+
+ // The radius is reduced by 1 to provide an anti aliasing effect on the display edges.
+ c.drawCircle(circleRadius, circleRadius, circleRadius - 1, mPaint);
mSurface.unlockCanvasAndPost(c);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b0feca8..f57adaf 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -379,14 +379,18 @@ class DisplayContent {
pw.println(" Application tokens in top down Z order:");
int ndx = 0;
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
+ final TaskStack stack = mStacks.get(stackNdx);
+ pw.print(" mStackId="); pw.println(stack.mStackId);
+ ArrayList<Task> tasks = stack.getTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
- for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+ final Task task = tasks.get(taskNdx);
+ pw.print(" mTaskId="); pw.println(task.taskId);
+ AppTokenList tokens = task.mAppTokens;
+ for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx, ++ndx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
- pw.print(" App #"); pw.print(ndx++);
+ pw.print(" Activity #"); pw.print(tokenNdx);
pw.print(' '); pw.print(wtoken); pw.println(":");
- wtoken.dump(pw, " ");
+ wtoken.dump(pw, " ");
}
}
}
diff --git a/services/core/java/com/android/server/wm/StackTapPointerEventListener.java b/services/core/java/com/android/server/wm/StackTapPointerEventListener.java
index 19d8ab3..8938358 100644
--- a/services/core/java/com/android/server/wm/StackTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/StackTapPointerEventListener.java
@@ -57,6 +57,7 @@ public class StackTapPointerEventListener implements PointerEventListener {
if (mPointerId >= 0) {
int index = motionEvent.findPointerIndex(mPointerId);
if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC
+ || index < 0
|| (motionEvent.getX(index) - mDownX) > mMotionSlop
|| (motionEvent.getY(index) - mDownY) > mMotionSlop) {
mPointerId = -1;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a60be3b..b49b87c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -68,6 +68,6 @@ class Task {
@Override
public String toString() {
- return "{taskId=" + taskId + " appTokens=" + mAppTokens + "}";
+ return "{taskId=" + taskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2750941..089d897 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -262,6 +262,8 @@ public class WindowManagerService extends IWindowManager.Stub
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
+ /** Amount of time to allow a last ANR message to exist before freeing the memory. */
+ static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours
/**
* If true, the window manager will do its own custom freezing and general
* management of the screen during rotation.
@@ -762,7 +764,7 @@ public class WindowManagerService extends IWindowManager.Stub
* Whether the UI is currently running in touch mode (not showing
* navigational focus because the user is directly pressing the screen).
*/
- boolean mInTouchMode = true;
+ boolean mInTouchMode;
private ViewServer mViewServer;
private final ArrayList<WindowChangeListener> mWindowChangeListeners =
@@ -823,6 +825,8 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.bool.config_sf_limitedAlpha);
mHasPermanentDpad = context.getResources().getBoolean(
com.android.internal.R.bool.config_hasPermanentDpad);
+ mInTouchMode = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_defaultInTouchMode);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplaySettings = new DisplaySettings();
@@ -940,7 +944,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void placeWindowAfter(WindowState pos, WindowState window) {
final WindowList windows = pos.getWindowList();
final int i = windows.indexOf(pos);
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
+ if (true || DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
+ (i+1) + " of " + windows.size() + " (after " + pos + ")");
windows.add(i+1, window);
@@ -950,7 +954,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void placeWindowBefore(WindowState pos, WindowState window) {
final WindowList windows = pos.getWindowList();
int i = windows.indexOf(pos);
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
+ if (true || DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
+ i + " of " + windows.size() + " (before " + pos + ")");
if (i < 0) {
@@ -1048,7 +1052,7 @@ public class WindowManagerService extends IWindowManager.Stub
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ if (true || DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
"not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " +
N);
windows.add(newIdx + 1, win);
@@ -1164,15 +1168,15 @@ public class WindowManagerService extends IWindowManager.Stub
// Just search for the start of this layer.
final int myLayer = win.mBaseLayer;
int i;
- for (i = 0; i < N; i++) {
+ for (i = N - 1; i >= 0; --i) {
WindowState w = windows.get(i);
- if (w.mBaseLayer > myLayer) {
+ if (w.mBaseLayer <= myLayer) {
break;
}
}
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
- "Based on layer: Adding window " + win + " at " + i + " of " + N);
- windows.add(i, win);
+ if (true || DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + N);
+ windows.add(i + 1, win);
mWindowsChanged = true;
return tokenWindowsPos;
}
@@ -3657,7 +3661,7 @@ public class WindowManagerService extends IWindowManager.Stub
atoken.layoutConfigChanges = (configChanges &
(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
atoken.mLaunchTaskBehind = launchTaskBehind;
- if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
+ if (true || DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
+ " to stack=" + stackId + " task=" + taskId + " at " + addPos);
Task task = mTaskIdToTask.get(taskId);
@@ -4808,7 +4812,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "removeAppToken: "
+ wtoken + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
final TaskStack stack = mTaskIdToTask.get(wtoken.groupId).mStack;
- if (delayed) {
+ if (delayed && !wtoken.allAppWindows.isEmpty()) {
// set the token aside because it has an active animation to be finished
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"removeAppToken make exiting: " + wtoken);
@@ -5192,7 +5196,7 @@ public class WindowManagerService extends IWindowManager.Stub
void removeTaskLocked(Task task) {
final int taskId = task.taskId;
final TaskStack stack = task.mStack;
- if (stack.isAnimating()) {
+ if (!task.mAppTokens.isEmpty() && stack.isAnimating()) {
if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + taskId);
task.mDeferRemoval = true;
return;
@@ -5200,8 +5204,17 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + taskId);
EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, taskId, "removeTask");
task.mDeferRemoval = false;
- task.mStack.removeTask(task);
+ stack.removeTask(task);
mTaskIdToTask.delete(task.taskId);
+
+ final ArrayList<AppWindowToken> exitingApps = stack.mExitingAppTokens;
+ for (int appNdx = exitingApps.size() - 1; appNdx >= 0; --appNdx) {
+ final AppWindowToken wtoken = exitingApps.get(appNdx);
+ if (wtoken.groupId == taskId) {
+ wtoken.mDeferRemoval = false;
+ exitingApps.remove(appNdx);
+ }
+ }
}
public void removeTask(int taskId) {
@@ -5370,7 +5383,12 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public boolean isKeyguardSecure() {
- return mPolicy.isKeyguardSecure();
+ long origId = Binder.clearCallingIdentity();
+ try {
+ return mPolicy.isKeyguardSecure();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
@Override
@@ -7599,6 +7617,7 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36;
public static final int CHECK_IF_BOOT_ANIMATION_FINISHED = 37;
+ public static final int RESET_ANR_MESSAGE = 38;
@Override
public void handleMessage(Message msg) {
@@ -8043,16 +8062,16 @@ public class WindowManagerService extends IWindowManager.Stub
break;
case TAP_OUTSIDE_STACK: {
- int stackId;
- synchronized (mWindowMap) {
- stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2);
- }
- if (stackId >= 0) {
- try {
- mActivityManager.setFocusedStack(stackId);
- } catch (RemoteException e) {
- }
- }
+// int stackId;
+// synchronized (mWindowMap) {
+// stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2);
+// }
+// if (stackId >= 0) {
+// try {
+// mActivityManager.setFocusedStack(stackId);
+// } catch (RemoteException e) {
+// }
+// }
}
break;
case NOTIFY_ACTIVITY_DRAWN:
@@ -8109,6 +8128,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
break;
+ case RESET_ANR_MESSAGE: {
+ synchronized (mWindowMap) {
+ mLastANRState = null;
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -10018,7 +10043,8 @@ public class WindowManagerService extends IWindowManager.Stub
mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
for (i = exitingAppTokens.size() - 1; i >= 0; i--) {
AppWindowToken token = exitingAppTokens.get(i);
- if (!token.hasVisible && !mClosingApps.contains(token) && !token.mDeferRemoval) {
+ if (!token.hasVisible && !mClosingApps.contains(token) &&
+ (!token.mDeferRemoval || token.allAppWindows.isEmpty())) {
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
// soon as their animations are complete
@@ -10028,6 +10054,10 @@ public class WindowManagerService extends IWindowManager.Stub
"performLayout: App token exiting now removed" + token);
removeAppFromTaskLocked(token);
exitingAppTokens.remove(i);
+ final Task task = mTaskIdToTask.get(token.groupId);
+ if (task != null && task.mDeferRemoval && task.mAppTokens.isEmpty()) {
+ removeTaskLocked(task);
+ }
}
}
}
@@ -11290,8 +11320,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.println();
dumpWindowsNoHeaderLocked(pw, true, null);
+ pw.println();
+ pw.println("Last ANR continued");
+ dumpDisplayContentsLocked(pw, true);
pw.close();
mLastANRState = sw.toString();
+
+ mH.removeMessages(H.RESET_ANR_MESSAGE);
+ mH.sendEmptyMessageDelayed(H.RESET_ANR_MESSAGE, LAST_ANR_LIFETIME_DURATION_MSECS);
}
@Override
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 5cb0543..dcb5199 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -18,6 +18,7 @@
//#define LOG_NDEBUG 0
+#include "android_os_MessageQueue.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
#include "JNIHelp.h"
@@ -27,6 +28,7 @@
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/Log.h>
+#include <utils/Looper.h>
#include <utils/NativeHandle.h>
#include <hardware/tv_input.h>
@@ -233,12 +235,17 @@ class JTvInputHal {
public:
~JTvInputHal();
- static JTvInputHal* createInstance(JNIEnv* env, jobject thiz);
+ static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper);
int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
int removeStream(int deviceId, int streamId);
const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
+ void onDeviceAvailable(const tv_input_device_info_t& info);
+ void onDeviceUnavailable(int deviceId);
+ void onStreamConfigurationsChanged(int deviceId);
+ void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
+
private:
// Connection between a surface and a stream.
class Connection {
@@ -254,28 +261,41 @@ private:
sp<BufferProducerThread> mThread;
};
- JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
+ class NotifyHandler : public MessageHandler {
+ public:
+ NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event);
+ ~NotifyHandler();
+
+ virtual void handleMessage(const Message& message);
+
+ private:
+ tv_input_event_t mEvent;
+ JTvInputHal* mHal;
+ };
+
+ JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev, const sp<Looper>& looper);
static void notify(
tv_input_device_t* dev, tv_input_event_t* event, void* data);
- void onDeviceAvailable(const tv_input_device_info_t& info);
- void onDeviceUnavailable(int deviceId);
- void onStreamConfigurationsChanged(int deviceId);
- void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
+ static void cloneTvInputEvent(
+ tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent);
Mutex mLock;
jweak mThiz;
tv_input_device_t* mDevice;
tv_input_callback_ops_t mCallback;
+ sp<Looper> mLooper;
KeyedVector<int, KeyedVector<int, Connection> > mConnections;
};
-JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device) {
+JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device,
+ const sp<Looper>& looper) {
mThiz = env->NewWeakGlobalRef(thiz);
mDevice = device;
mCallback.notify = &JTvInputHal::notify;
+ mLooper = looper;
mDevice->initialize(mDevice, &mCallback, this);
}
@@ -288,7 +308,7 @@ JTvInputHal::~JTvInputHal() {
mThiz = NULL;
}
-JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) {
+JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
tv_input_module_t* module = NULL;
status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
@@ -309,7 +329,7 @@ JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) {
return 0;
}
- return new JTvInputHal(env, thiz, device);
+ return new JTvInputHal(env, thiz, device, looper);
}
int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
@@ -428,30 +448,20 @@ const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numCo
void JTvInputHal::notify(
tv_input_device_t* dev, tv_input_event_t* event, void* data) {
JTvInputHal* thiz = (JTvInputHal*)data;
- switch (event->type) {
- case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
- thiz->onDeviceAvailable(event->device_info);
- } break;
- case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
- thiz->onDeviceUnavailable(event->device_info.device_id);
- } break;
- case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
- thiz->onStreamConfigurationsChanged(event->device_info.device_id);
- } break;
- case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
- thiz->onCaptured(event->capture_result.device_id,
- event->capture_result.stream_id,
- event->capture_result.seq,
- true /* succeeded */);
- } break;
- case TV_INPUT_EVENT_CAPTURE_FAILED: {
- thiz->onCaptured(event->capture_result.device_id,
- event->capture_result.stream_id,
- event->capture_result.seq,
- false /* succeeded */);
- } break;
- default:
- ALOGE("Unrecognizable event");
+ thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type);
+}
+
+// static
+void JTvInputHal::cloneTvInputEvent(
+ tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent) {
+ memcpy(dstEvent, srcEvent, sizeof(tv_input_event_t));
+ if ((srcEvent->type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
+ srcEvent->type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
+ srcEvent->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
+ srcEvent->device_info.audio_address != NULL){
+ char* audio_address = new char[strlen(srcEvent->device_info.audio_address) + 1];
+ strcpy(audio_address, srcEvent->device_info.audio_address);
+ dstEvent->device_info.audio_address = audio_address;
}
}
@@ -549,10 +559,54 @@ void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succ
}
}
+JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) {
+ mHal = hal;
+ cloneTvInputEvent(&mEvent, event);
+}
+
+JTvInputHal::NotifyHandler::~NotifyHandler() {
+ if ((mEvent.type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
+ mEvent.type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
+ mEvent.type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
+ mEvent.device_info.audio_address != NULL) {
+ delete mEvent.device_info.audio_address;
+ }
+}
+
+void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
+ switch (mEvent.type) {
+ case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
+ mHal->onDeviceAvailable(mEvent.device_info);
+ } break;
+ case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
+ mHal->onDeviceUnavailable(mEvent.device_info.device_id);
+ } break;
+ case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
+ mHal->onStreamConfigurationsChanged(mEvent.device_info.device_id);
+ } break;
+ case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
+ mHal->onCaptured(mEvent.capture_result.device_id,
+ mEvent.capture_result.stream_id,
+ mEvent.capture_result.seq,
+ true /* succeeded */);
+ } break;
+ case TV_INPUT_EVENT_CAPTURE_FAILED: {
+ mHal->onCaptured(mEvent.capture_result.device_id,
+ mEvent.capture_result.stream_id,
+ mEvent.capture_result.seq,
+ false /* succeeded */);
+ } break;
+ default:
+ ALOGE("Unrecognizable event");
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
-static jlong nativeOpen(JNIEnv* env, jobject thiz) {
- return (jlong)JTvInputHal::createInstance(env, thiz);
+static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
+ sp<MessageQueue> messageQueue =
+ android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
+ return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper());
}
static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
@@ -610,7 +664,7 @@ static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
static JNINativeMethod gTvInputHalMethods[] = {
/* name, signature, funcPtr */
- { "nativeOpen", "()J",
+ { "nativeOpen", "(Landroid/os/MessageQueue;)J",
(void*) nativeOpen },
{ "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I",
(void*) nativeAddOrUpdateStream },
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5e405ce..40e2056 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -60,6 +60,7 @@ import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.PersistableBundle;
@@ -989,6 +990,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ /**
+ * Creates and loads the policy data from xml for data that is shared between
+ * various profiles of a user. In contrast to {@link #getUserData(int)}
+ * it allows access to data of users other than the calling user.
+ *
+ * This function should only be used for shared data, e.g. everything regarding
+ * passwords and should be removed once multiple screen locks are present.
+ * @param userHandle the user for whom to load the policy data
+ * @return
+ */
+ DevicePolicyData getUserDataUnchecked(int userHandle) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return getUserData(userHandle);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
void removeUserData(int userHandle) {
synchronized (this) {
if (userHandle == UserHandle.USER_OWNER) {
@@ -1348,6 +1368,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.endTag(null, "policies");
out.endDocument();
+ stream.flush();
+ FileUtils.sync(stream);
stream.close();
journal.commit();
sendChangedNotification(userHandle);
@@ -1694,7 +1716,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int smallIconId;
String contentText;
final String ownerName = getDeviceOwnerName();
- if (ownerName != null) {
+ if (isManagedProfile(userHandle.getIdentifier())) {
+ contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_administrator);
+ smallIconId = R.drawable.stat_sys_certificate_info;
+ } else if (ownerName != null) {
contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed, ownerName);
smallIconId = R.drawable.stat_sys_certificate_info;
} else {
@@ -1924,7 +1949,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -1971,7 +1996,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2018,7 +2043,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2079,7 +2104,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2177,7 +2202,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2234,7 +2259,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2278,7 +2303,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2325,7 +2350,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2372,7 +2397,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2419,7 +2444,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2466,7 +2491,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2491,8 +2516,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// If the user this is called from is part of a profile group, that is the parent
// of the group.
UserInfo parent = getProfileParent(userHandle);
- int id = parent == null ? userHandle : parent.id;
- DevicePolicyData policy = getUserData(id);
+ int id = (parent == null) ? userHandle : parent.id;
+ DevicePolicyData policy = getUserDataUnchecked(id);
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
@@ -2522,7 +2547,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
// The active password is stored in the parent.
- DevicePolicyData policy = getUserData(getProfileParent(userHandle).id);
+ UserInfo parent = getProfileParent(userHandle);
+ int id = (parent == null) ? userHandle : parent.id;
+ DevicePolicyData policy = getUserDataUnchecked(id);
return policy.mFailedPasswordAttempts;
}
@@ -2585,7 +2612,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int count = 0;
ActiveAdmin strictestAdmin = null;
for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
for (ActiveAdmin admin : policy.mAdminList) {
if (admin.maximumFailedPasswordsForWipe ==
ActiveAdmin.DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
@@ -2798,7 +2825,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -3022,7 +3049,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mHandler.post(new Runnable() {
public void run() {
try {
- ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
+ IActivityManager am = ActivityManagerNative.getDefault();
+ if (am.getCurrentUser().id == userHandle) {
+ am.switchUser(UserHandle.USER_OWNER);
+ }
if (!mUserManager.removeUser(userHandle)) {
Slog.w(LOG_TAG, "Couldn't remove user " + userHandle);
}
@@ -3120,7 +3150,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
int profileId = userInfo.id;
- DevicePolicyData policy = getUserData(profileId);
+ DevicePolicyData policy = getUserDataUnchecked(profileId);
final int N = policy.mAdminList.size();
if (N > 0) {
for (int i=0; i<N; i++) {
@@ -4240,7 +4270,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// and return null.
boolean allAdminsHaveOptions = true;
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.id);
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i < N; i++) {
final ActiveAdmin active = policy.mAdminList.get(i);
@@ -4471,7 +4501,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
for (int i = 0; i < PROFILES_SIZE; ++i) {
// Just loop though all admins, only device or profiles
// owners can have permitted lists set.
- DevicePolicyData policy = getUserData(profiles.get(i).id);
+ DevicePolicyData policy = getUserDataUnchecked(profiles.get(i).id);
final int N = policy.mAdminList.size();
for (int j = 0; j < N; j++) {
ActiveAdmin admin = policy.mAdminList.get(j);
@@ -4636,7 +4666,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
for (int i = 0; i < PROFILES_SIZE; ++i) {
// Just loop though all admins, only device or profiles
// owners can have permitted lists set.
- DevicePolicyData policy = getUserData(profiles.get(i).id);
+ DevicePolicyData policy = getUserDataUnchecked(profiles.get(i).id);
final int N = policy.mAdminList.size();
for (int j = 0; j < N; j++) {
ActiveAdmin admin = policy.mAdminList.get(j);
@@ -4707,6 +4737,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public UserHandle createAndInitializeUser(ComponentName who, String name,
String ownerName, ComponentName profileOwnerComponent, Bundle adminExtras) {
UserHandle user = createUser(who, name);
+ if (user == null) {
+ return null;
+ }
long id = Binder.clearCallingIdentity();
try {
String profileOwnerPkg = profileOwnerComponent.getPackageName();
@@ -4866,6 +4899,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
mUserManager.setUserRestriction(key, enabled, user);
+ if (enabled != alreadyRestricted) {
+ if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
+ // Send out notifications however as some clients may want to reread the
+ // value which actually changed due to a restriction having been applied.
+ final String property = Settings.Secure.SYS_PROP_SETTING_VERSION;
+ long version = SystemProperties.getLong(property, 0) + 1;
+ SystemProperties.set(property, Long.toString(version));
+
+ final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
+ Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
+ mContext.getContentResolver().notifyChange(url, null, true, userHandle);
+ }
+ }
} finally {
restoreCallingIdentity(id);
}
@@ -5425,7 +5471,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return Collections.emptyList();
}
- DevicePolicyData policy = getUserData(profileId);
+ DevicePolicyData policy = getUserDataUnchecked(profileId);
ActiveAdmin admin = policy.mAdminMap.get(ownerComponent);
if (admin == null || admin.crossProfileWidgetProviders == null
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 334cdf6..0705fbd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -677,7 +677,8 @@ public final class SystemServer {
mSystemServiceManager.startService("com.android.server.wifi.RttService");
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET)) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
}