summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java113
-rw-r--r--services/accessibility/java/com/android/server/accessibility/TouchExplorer.java49
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java14
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java87
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java12
-rw-r--r--services/core/java/com/android/server/MmsServiceBroker.java6
-rw-r--r--services/core/java/com/android/server/NativeDaemonEvent.java20
-rw-r--r--services/core/java/com/android/server/NsdService.java45
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java86
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java6
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java7
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityManagerService.java124
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java13
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java161
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java130
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java2
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java59
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java3
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java21
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java8
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java25
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java77
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java11
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java21
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiLogger.java57
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java149
-rw-r--r--services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java2
-rw-r--r--services/core/java/com/android/server/hdmi/RequestArcAction.java1
-rw-r--r--services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java8
-rw-r--r--services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java8
-rw-r--r--services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java29
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioAction.java8
-rw-r--r--services/core/java/com/android/server/hdmi/VolumeControlAction.java221
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java21
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java22
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java19
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java2
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java5
-rw-r--r--services/core/java/com/android/server/notification/DowntimeConditionProvider.java4
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java98
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java16
-rw-r--r--services/core/java/com/android/server/notification/PackageVisibilityExtractor.java53
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java4
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java101
-rw-r--r--services/core/java/com/android/server/notification/ValidateNotificationPeople.java152
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java15
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java2
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java225
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java146
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerException.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java262
-rw-r--r--services/core/java/com/android/server/pm/Settings.java184
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java49
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java60
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java101
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java90
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java159
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java12
-rw-r--r--services/core/java/com/android/server/wm/CircularDisplayMask.java10
-rw-r--r--services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java127
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java15
-rw-r--r--services/core/java/com/android/server/wm/Session.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java376
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java11
-rw-r--r--services/core/jni/Android.mk1
-rw-r--r--services/core/jni/com_android_server_UsbHostManager.cpp3
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp20
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java27
-rw-r--r--services/print/java/com/android/server/print/PrintManagerService.java48
-rw-r--r--services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java2
-rw-r--r--services/usage/java/com/android/server/usage/IntervalStats.java74
-rw-r--r--services/usage/java/com/android/server/usage/UnixCalendar.java99
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java130
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java93
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsUtils.java63
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsXml.java5
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsXmlV1.java251
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java319
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java16
83 files changed, 3284 insertions, 1832 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index a43a2a6..ebe21ff 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -746,14 +746,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
/**
- * Gets the bounds of the accessibility focus in the active window.
+ * Gets a point within the accessibility focused node where we can send down
+ * and up events to perform a click.
*
- * @param outBounds The output to which to write the focus bounds.
- * @return Whether accessibility focus was found and the bounds are populated.
+ * @param outPoint The click point to populate.
+ * @return Whether accessibility a click point was found and set.
*/
// TODO: (multi-display) Make sure this works for multiple displays.
- boolean getAccessibilityFocusBounds(Rect outBounds) {
- return getInteractionBridgeLocked().getAccessibilityFocusBoundsNotLocked(outBounds);
+ boolean getAccessibilityFocusClickPointInScreen(Point outPoint) {
+ return getInteractionBridgeLocked()
+ .getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
}
/**
@@ -2196,8 +2198,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId, viewIdResName,
- partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid,
- interrogatingTid, spec);
+ partialInteractiveRegion, interactionId, callback, mFetchFlags,
+ interrogatingPid, interrogatingTid, spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -2248,8 +2250,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
- partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid,
- interrogatingTid, spec);
+ partialInteractiveRegion, interactionId, callback, mFetchFlags,
+ interrogatingPid, interrogatingTid, spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -2352,8 +2354,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
- connection.findFocus(accessibilityNodeId, focusType, partialInteractiveRegion, interactionId,
- callback, mFetchFlags, interrogatingPid, interrogatingTid, spec);
+ connection.findFocus(accessibilityNodeId, focusType, partialInteractiveRegion,
+ interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
+ spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -2403,8 +2406,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
- connection.focusSearch(accessibilityNodeId, direction, partialInteractiveRegion, interactionId,
- callback, mFetchFlags, interrogatingPid, interrogatingTid, spec);
+ connection.focusSearch(accessibilityNodeId, direction, partialInteractiveRegion,
+ interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
+ spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -2460,6 +2464,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return true;
}
+ @Override
public boolean performGlobalAction(int action) {
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
@@ -2501,6 +2506,57 @@ 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.getCallingUserId());
+ 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) {
@@ -3119,30 +3175,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
- public boolean getAccessibilityFocusBoundsNotLocked(Rect outBounds) {
+ public boolean getAccessibilityFocusClickPointInScreenNotLocked(Point outPoint) {
AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked();
if (focus == null) {
return false;
}
synchronized (mLock) {
- focus.getBoundsInScreen(outBounds);
+ Point point = mClient.computeClickPointInScreen(mConnectionId,
+ focus.getWindowId(), focus.getSourceNodeId());
+
+ if (point == null) {
+ return false;
+ }
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
if (spec != null && !spec.isNop()) {
- outBounds.offset((int) -spec.offsetX, (int) -spec.offsetY);
- outBounds.scale(1 / spec.scale);
+ point.offset((int) -spec.offsetX, (int) -spec.offsetY);
+ point.x = (int) (point.x * (1 / spec.scale));
+ point.y = (int) (point.y * (1 / spec.scale));
}
- // Clip to the window rectangle.
+ // Make sure the point is within the window.
Rect windowBounds = mTempRect;
getActiveWindowBounds(windowBounds);
- outBounds.intersect(windowBounds);
+ if (!windowBounds.contains(point.x, point.y)) {
+ return false;
+ }
- // Clip to the screen rectangle.
- mDefaultDisplay.getRealSize(mTempPoint);
- outBounds.intersect(0, 0, mTempPoint.x, mTempPoint.y);
+ // Make sure the point is within the screen.
+ Point screenSize = mTempPoint;
+ mDefaultDisplay.getRealSize(screenSize);
+ if (point.x < 0 || point.x > screenSize.x
+ || point.y < 0 || point.y > screenSize.y) {
+ return false;
+ }
+ outPoint.set(point.x, point.y);
return true;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index af5c13d..9e63433 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -24,6 +24,7 @@ import android.gesture.GesturePoint;
import android.gesture.GestureStore;
import android.gesture.GestureStroke;
import android.gesture.Prediction;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
@@ -171,6 +172,9 @@ class TouchExplorer implements EventStreamTransformation {
// Temporary rectangle to avoid instantiation.
private final Rect mTempRect = new Rect();
+ // Temporary point to avoid instantiation.
+ private final Point mTempPoint = new Point();
+
// Context in which this explorer operates.
private final Context mContext;
@@ -708,7 +712,6 @@ class TouchExplorer implements EventStreamTransformation {
// Send an event to the end of the drag gesture.
sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
- mCurrentState = STATE_TOUCH_EXPLORING;
} break;
case MotionEvent.ACTION_UP: {
mAms.onTouchInteractionEnd();
@@ -1158,18 +1161,18 @@ class TouchExplorer implements EventStreamTransformation {
mInjectedPointerTracker.getLastInjectedHoverEventForClick();
if (lastExploreEvent == null) {
// No last touch explored event but there is accessibility focus in
- // the active window. We click in the middle of the focus bounds.
- Rect focusBounds = mTempRect;
- if (mAms.getAccessibilityFocusBounds(focusBounds)) {
- clickLocationX = focusBounds.centerX();
- clickLocationY = focusBounds.centerY();
+ // the active window. We click in the focus bounds.
+ Point point = mTempPoint;
+ if (mAms.getAccessibilityFocusClickPointInScreen(point)) {
+ clickLocationX = point.x;
+ clickLocationY = point.y;
} else {
// Out of luck - do nothing.
return;
}
} else {
// If the click is within the active window but not within the
- // accessibility focus bounds we click in the focus center.
+ // accessibility focus bounds we click in the focus bounds.
final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex);
clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex);
@@ -1177,12 +1180,10 @@ class TouchExplorer implements EventStreamTransformation {
if (mLastTouchedWindowId == mAms.getActiveWindowId()) {
mAms.getActiveWindowBounds(activeWindowBounds);
if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
- Rect focusBounds = mTempRect;
- if (mAms.getAccessibilityFocusBounds(focusBounds)) {
- if (!focusBounds.contains(clickLocationX, clickLocationY)) {
- clickLocationX = focusBounds.centerX();
- clickLocationY = focusBounds.centerY();
- }
+ Point point = mTempPoint;
+ if (mAms.getAccessibilityFocusClickPointInScreen(point)) {
+ clickLocationX = point.x;
+ clickLocationY = point.y;
}
}
}
@@ -1331,18 +1332,18 @@ class TouchExplorer implements EventStreamTransformation {
mInjectedPointerTracker.getLastInjectedHoverEventForClick();
if (lastExploreEvent == null) {
// No last touch explored event but there is accessibility focus in
- // the active window. We click in the middle of the focus bounds.
- Rect focusBounds = mTempRect;
- if (mAms.getAccessibilityFocusBounds(focusBounds)) {
- clickLocationX = focusBounds.centerX();
- clickLocationY = focusBounds.centerY();
+ // the active window. We click in the focus bounds.
+ Point point = mTempPoint;
+ if (mAms.getAccessibilityFocusClickPointInScreen(point)) {
+ clickLocationX = point.x;
+ clickLocationY = point.y;
} else {
// Out of luck - do nothing.
return;
}
} else {
// If the click is within the active window but not within the
- // accessibility focus bounds we click in the focus center.
+ // accessibility focus bounds we click in the focus bounds.
final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex);
clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex);
@@ -1350,12 +1351,10 @@ class TouchExplorer implements EventStreamTransformation {
if (mLastTouchedWindowId == mAms.getActiveWindowId()) {
mAms.getActiveWindowBounds(activeWindowBounds);
if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
- Rect focusBounds = mTempRect;
- if (mAms.getAccessibilityFocusBounds(focusBounds)) {
- if (!focusBounds.contains(clickLocationX, clickLocationY)) {
- clickLocationX = focusBounds.centerX();
- clickLocationY = focusBounds.centerY();
- }
+ Point point = mTempPoint;
+ if (mAms.getAccessibilityFocusClickPointInScreen(point)) {
+ clickLocationX = point.x;
+ clickLocationY = point.y;
}
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7f7e5c3..d05de69 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -440,12 +440,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Permission Denial: can't dump from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- }
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
+ "Permission Denial: can't dump from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
synchronized (mLock) {
int N = mProviders.size();
@@ -1863,7 +1861,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private void deleteProviderLocked(Provider provider) {
int N = provider.widgets.size();
- for (int i = 0; i < N; i++) {
+ for (int i = N - 1; i >= 0; i--) {
Widget widget = provider.widgets.remove(i);
// Call back with empty RemoteViews
updateAppWidgetInstanceLocked(widget, null, false);
@@ -4038,4 +4036,4 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
}
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 3bab1bf..75090db 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -425,8 +425,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
TelephonyManager mTelephonyManager;
- // sequence number for Networks
- private final static int MIN_NET_ID = 10; // some reserved marks
+ // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
+ private final static int MIN_NET_ID = 100; // some reserved marks
private final static int MAX_NET_ID = 65535;
private int mNextNetId = MIN_NET_ID;
@@ -793,14 +793,28 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
- * Check if UID should be blocked from using the network represented by the
- * given {@link NetworkStateTracker}.
+ * Check if UID should be blocked from using the network represented by the given networkType.
+ * @deprecated Uses mLegacyTypeTracker; cannot deal with multiple Networks of the same type.
*/
private boolean isNetworkBlocked(int networkType, int uid) {
+ return isNetworkWithLinkPropertiesBlocked(getLinkPropertiesForType(networkType), uid);
+ }
+
+ /**
+ * Check if UID should be blocked from using the network represented by the given
+ * NetworkAgentInfo.
+ */
+ private boolean isNetworkBlocked(NetworkAgentInfo nai, int uid) {
+ return isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid);
+ }
+
+ /**
+ * Check if UID should be blocked from using the network with the given LinkProperties.
+ */
+ private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid) {
final boolean networkCostly;
final int uidRules;
- LinkProperties lp = getLinkPropertiesForType(networkType);
final String iface = (lp == null ? "" : lp.getInterfaceName());
synchronized (mRulesLock) {
networkCostly = mMeteredIfaces.contains(iface);
@@ -819,17 +833,36 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* Return a filtered {@link NetworkInfo}, potentially marked
* {@link DetailedState#BLOCKED} based on
* {@link #isNetworkBlocked}.
+ * @deprecated Uses mLegacyTypeTracker; cannot deal with multiple Networks of the same type.
*/
private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) {
NetworkInfo info = getNetworkInfoForType(networkType);
return getFilteredNetworkInfo(info, networkType, uid);
}
+ /*
+ * @deprecated Uses mLegacyTypeTracker; cannot deal with multiple Networks of the same type.
+ */
private NetworkInfo getFilteredNetworkInfo(NetworkInfo info, int networkType, int uid) {
if (isNetworkBlocked(networkType, uid)) {
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
+ if (VDBG) log("returning Blocked NetworkInfo");
+ }
+ if (mLockdownTracker != null) {
+ info = mLockdownTracker.augmentNetworkInfo(info);
+ if (VDBG) log("returning Locked NetworkInfo");
+ }
+ return info;
+ }
+
+ private NetworkInfo getFilteredNetworkInfo(NetworkAgentInfo nai, int uid) {
+ NetworkInfo info = nai.networkInfo;
+ if (isNetworkBlocked(nai, uid)) {
+ // network is blocked; clone and override state
+ info = new NetworkInfo(info);
+ info.setDetailedState(DetailedState.BLOCKED, null, null);
if (DBG) log("returning Blocked NetworkInfo");
}
if (mLockdownTracker != null) {
@@ -946,7 +979,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
synchronized (nai) {
if (nai.networkInfo == null) return null;
- return getFilteredNetworkInfo(nai.networkInfo, nai.networkInfo.getType(), uid);
+ return getFilteredNetworkInfo(nai, uid);
}
}
@@ -1315,6 +1348,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// }
}
+ private void enforceInternetPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERNET,
+ "ConnectivityService");
+ }
+
private void enforceAccessPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -2468,7 +2507,22 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
public void reportBadNetwork(Network network) {
- //TODO
+ enforceAccessPermission();
+ enforceInternetPermission();
+
+ if (network == null) return;
+
+ final int uid = Binder.getCallingUid();
+ NetworkAgentInfo nai = null;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(network.netId);
+ }
+ if (nai == null) return;
+ synchronized (nai) {
+ if (isNetworkBlocked(nai, uid)) return;
+
+ nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
+ }
}
public ProxyInfo getProxy() {
@@ -4436,10 +4490,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Unknown NetworkAgentInfo in handleLingerComplete");
return;
}
- if (DBG) log("handleLingerComplete for " + oldNetwork.name());
if (DBG) {
- if (oldNetwork.networkRequests.size() != 0) {
- loge("Dead network still had " + oldNetwork.networkRequests.size() + " requests");
+ log("handleLingerComplete for " + oldNetwork.name());
+ for (int i = 0; i < oldNetwork.networkRequests.size(); i++) {
+ NetworkRequest nr = oldNetwork.networkRequests.valueAt(i);
+ // Ignore listening requests.
+ if (mNetworkRequests.get(nr).isRequest == false) continue;
+ loge("Dead network still had at least " + nr);
+ break;
}
}
oldNetwork.asyncChannel.disconnect();
@@ -4463,6 +4521,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Unknown NetworkAgentInfo in handleConnectionValidated");
return;
}
+ if (newNetwork.validated) return;
+ newNetwork.validated = true;
boolean keep = newNetwork.isVPN();
boolean isNewDefault = false;
if (DBG) log("handleConnectionValidated for "+newNetwork.name());
@@ -4686,6 +4746,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void updateNetworkScore(NetworkAgentInfo nai, int score) {
if (DBG) log("updateNetworkScore for " + nai.name() + " to " + score);
+ if (score < 0) {
+ loge("updateNetworkScore for " + nai.name() + " got a negative score (" + score +
+ "). Bumping score to min of 0");
+ score = 0;
+ }
nai.currentScore = score;
@@ -4706,6 +4771,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// code will fire.
for (int i = 0; i < nai.networkRequests.size(); i++) {
NetworkRequest nr = nai.networkRequests.valueAt(i);
+ // Don't send listening requests to factories. b/17393458
+ if (mNetworkRequests.get(nr).isRequest == false) continue;
sendUpdatedScoreToFactories(nr, score);
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index c8718e3..060c8e3 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -106,6 +106,7 @@ import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
@@ -3455,9 +3456,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
final boolean isAuxiliary = "1".equals(String.valueOf(
parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
- final InputMethodSubtype subtype =
- new InputMethodSubtype(label, icon, imeSubtypeLocale,
- imeSubtypeMode, imeSubtypeExtraValue, isAuxiliary);
+ final InputMethodSubtype subtype = new InputMethodSubtypeBuilder()
+ .setSubtypeNameResId(label)
+ .setSubtypeIconResId(icon)
+ .setSubtypeLocale(imeSubtypeLocale)
+ .setSubtypeMode(imeSubtypeMode)
+ .setSubtypeExtraValue(imeSubtypeExtraValue)
+ .setIsAuxiliary(isAuxiliary)
+ .build();
tempSubtypesArray.add(subtype);
}
}
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 65ad1ce..b452a38 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -193,7 +193,7 @@ public class MmsServiceBroker extends SystemService {
private final class BinderService extends IMms.Stub {
@Override
public void sendMessage(long subId, String callingPkg, Uri contentUri,
- String locationUrl, ContentValues configOverrides, PendingIntent sentIntent)
+ String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
throws RemoteException {
mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
@@ -206,7 +206,7 @@ public class MmsServiceBroker extends SystemService {
@Override
public void downloadMessage(long subId, String callingPkg, String locationUrl,
- Uri contentUri, ContentValues configOverrides,
+ Uri contentUri, Bundle configOverrides,
PendingIntent downloadedIntent) throws RemoteException {
mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
"Download MMS message");
@@ -333,7 +333,7 @@ public class MmsServiceBroker extends SystemService {
@Override
public void sendStoredMessage(long subId, String callingPkg, Uri messageUri,
- ContentValues configOverrides, PendingIntent sentIntent) throws RemoteException {
+ Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
mContext.enforceCallingPermission(Manifest.permission.SEND_SMS,
"Send stored MMS message");
if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
diff --git a/services/core/java/com/android/server/NativeDaemonEvent.java b/services/core/java/com/android/server/NativeDaemonEvent.java
index 2095152..59d50bd 100644
--- a/services/core/java/com/android/server/NativeDaemonEvent.java
+++ b/services/core/java/com/android/server/NativeDaemonEvent.java
@@ -201,20 +201,16 @@ public class NativeDaemonEvent {
}
while (current < length) {
// find the end of the word
- if (quoted) {
- wordEnd = current;
- while ((wordEnd = rawEvent.indexOf('\"', wordEnd)) != -1) {
- if (rawEvent.charAt(wordEnd - 1) != '\\') {
- break;
- } else {
- wordEnd++; // skip this escaped quote and keep looking
- }
+ char terminator = quoted ? '\"' : ' ';
+ wordEnd = current;
+ while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
+ if (rawEvent.charAt(wordEnd) == '\\') {
+ // skip the escaped char
+ ++wordEnd;
}
- } else {
- wordEnd = rawEvent.indexOf(' ', current);
+ ++wordEnd;
}
- // if we didn't find the end-o-word token, take the rest of the string
- if (wordEnd == -1) wordEnd = length;
+ if (wordEnd > length) wordEnd = length;
String word = rawEvent.substring(current, wordEnd);
current += word.length();
if (!quoted) {
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index cf7e65c..cb1748d 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -397,8 +397,7 @@ public class NsdService extends INsdManager.Stub {
break;
case NsdManager.NATIVE_DAEMON_EVENT:
NativeEvent event = (NativeEvent) msg.obj;
- if (!handleNativeEvent(event.code, event.raw,
- NativeDaemonEvent.unescapeArgs(event.raw))) {
+ if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
result = NOT_HANDLED;
}
break;
@@ -474,8 +473,14 @@ public class NsdService extends INsdManager.Stub {
case NativeResponseCode.SERVICE_RESOLVED:
/* NNN resolveId fullName hostName port txtlen txtdata */
if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
- int index = cooked[2].indexOf(".");
- if (index == -1) {
+ int index = 0;
+ while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
+ if (cooked[2].charAt(index) == '\\') {
+ ++index;
+ }
+ ++index;
+ }
+ if (index >= cooked[2].length()) {
Slog.e(TAG, "Invalid service found " + raw);
break;
}
@@ -483,6 +488,8 @@ public class NsdService extends INsdManager.Stub {
String rest = cooked[2].substring(index);
String type = rest.replace(".local.", "");
+ name = unescape(name);
+
clientInfo.mResolvedService.setServiceName(name);
clientInfo.mResolvedService.setServiceType(type);
clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
@@ -541,6 +548,30 @@ public class NsdService extends INsdManager.Stub {
}
}
+ private String unescape(String s) {
+ StringBuilder sb = new StringBuilder(s.length());
+ for (int i = 0; i < s.length(); ++i) {
+ char c = s.charAt(i);
+ if (c == '\\') {
+ if (++i >= s.length()) {
+ Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
+ break;
+ }
+ c = s.charAt(i);
+ if (c != '.' && c != '\\') {
+ if (i + 2 >= s.length()) {
+ Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
+ break;
+ }
+ c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
+ i += 2;
+ }
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
private NativeDaemonConnector mNativeConnector;
private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
@@ -625,10 +656,12 @@ public class NsdService extends INsdManager.Stub {
private class NativeEvent {
final int code;
final String raw;
+ final String[] cooked;
- NativeEvent(int code, String raw) {
+ NativeEvent(int code, String raw, String[] cooked) {
this.code = code;
this.raw = raw;
+ this.cooked = cooked;
}
}
@@ -644,7 +677,7 @@ public class NsdService extends INsdManager.Stub {
public boolean onEvent(int code, String raw, String[] cooked) {
// TODO: NDC translates a message to a callback, we could enhance NDC to
// directly interact with a state machine through messages
- NativeEvent event = new NativeEvent(code, raw);
+ NativeEvent event = new NativeEvent(code, raw, cooked);
mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
return true;
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 7624314..37c23bb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -196,7 +196,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (VDBG) log("MSG_USER_SWITCHED userId=" + msg.arg1);
int numPhones = TelephonyManager.getDefault().getPhoneCount();
for (int sub = 0; sub < numPhones; sub++) {
- TelephonyRegistry.this.notifyCellLocationUsingSubId(sub,
+ TelephonyRegistry.this.notifyCellLocationForSubscriber(sub,
mCellLocation[sub]);
}
break;
@@ -326,7 +326,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
- public void listenUsingSubId(long subId, String pkgForDebug, IPhoneStateListener callback,
+ public void listenForSubscriber(long subId, String pkgForDebug, IPhoneStateListener callback,
int events, boolean notifyNow) {
listen(pkgForDebug, callback, events, notifyNow, subId, false);
}
@@ -542,12 +542,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
broadcastCallStateChanged(state, incomingNumber, mDefaultSubId);
}
- public void notifyCallStateUsingSubId(long subId, int state, String incomingNumber) {
+ public void notifyCallStateForSubscriber(long subId, int state, String incomingNumber) {
if (!checkNotifyPermission("notifyCallState()")) {
return;
}
if (VDBG) {
- log("notifyCallStateUsingSubId: subId=" + subId
+ log("notifyCallStateForSubscriber: subId=" + subId
+ " state=" + state + " incomingNumber=" + incomingNumber);
}
synchronized (mRecords) {
@@ -573,38 +573,38 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyServiceState(ServiceState state) {
- notifyServiceStateUsingSubId(mDefaultSubId, state);
+ notifyServiceStateForSubscriber(mDefaultSubId, state);
}
- public void notifyServiceStateUsingSubId(long subId, ServiceState state) {
+ public void notifyServiceStateForSubscriber(long subId, ServiceState state) {
if (!checkNotifyPermission("notifyServiceState()")){
return;
}
if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
subId = mDefaultSubId;
- if (VDBG) log("notifyServiceStateUsingSubId: using mDefaultSubId=" + mDefaultSubId);
+ if (VDBG) log("notifyServiceStateForSubscriber: using mDefaultSubId=" + mDefaultSubId);
}
synchronized (mRecords) {
int phoneId = SubscriptionManager.getPhoneId(subId);
if (VDBG) {
- log("notifyServiceStateUsingSubId: subId=" + subId + " phoneId=" + phoneId
+ log("notifyServiceStateForSubscriber: subId=" + subId + " phoneId=" + phoneId
+ " state=" + state);
}
if (validatePhoneId(phoneId)) {
mServiceState[phoneId] = state;
- logServiceStateChanged("notifyServiceStateUsingSubId", subId, phoneId, state);
- if (VDBG) toStringLogSSC("notifyServiceStateUsingSubId");
+ logServiceStateChanged("notifyServiceStateForSubscriber", subId, phoneId, state);
+ if (VDBG) toStringLogSSC("notifyServiceStateForSubscriber");
for (Record r : mRecords) {
if (VDBG) {
- log("notifyServiceStateUsingSubId: r=" + r + " subId=" + subId
+ log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " state=" + state);
}
if (((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) &&
(r.phoneId == phoneId)) {
try {
if (DBG) {
- log("notifyServiceStateUsingSubId: callback.onSSC r=" + r
+ log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
+ " state=" + state);
}
@@ -615,7 +615,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
} else {
- log("notifyServiceStateUsingSubId: INVALID phoneId=" + phoneId);
+ log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
}
handleRemoveListLocked();
}
@@ -623,33 +623,33 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifySignalStrength(SignalStrength signalStrength) {
- notifySignalStrengthUsingSubId(mDefaultSubId, signalStrength);
+ notifySignalStrengthForSubscriber(mDefaultSubId, signalStrength);
}
- public void notifySignalStrengthUsingSubId(long subId, SignalStrength signalStrength) {
+ public void notifySignalStrengthForSubscriber(long subId, SignalStrength signalStrength) {
if (!checkNotifyPermission("notifySignalStrength()")) {
return;
}
if (VDBG) {
- log("notifySignalStrengthUsingSubId: subId=" + subId
+ log("notifySignalStrengthForSubscriber: subId=" + subId
+ " signalStrength=" + signalStrength);
- toStringLogSSC("notifySignalStrengthUsingSubId");
+ toStringLogSSC("notifySignalStrengthForSubscriber");
}
synchronized (mRecords) {
int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
- if (VDBG) log("notifySignalStrengthUsingSubId: valid phoneId=" + phoneId);
+ if (VDBG) log("notifySignalStrengthForSubscriber: valid phoneId=" + phoneId);
mSignalStrength[phoneId] = signalStrength;
for (Record r : mRecords) {
if (VDBG) {
- log("notifySignalStrengthUsingSubId: r=" + r + " subId=" + subId
+ log("notifySignalStrengthForSubscriber: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " ss=" + signalStrength);
}
if (((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) &&
(r.phoneId == phoneId)) {
try {
if (DBG) {
- log("notifySignalStrengthUsingSubId: callback.onSsS r=" + r
+ log("notifySignalStrengthForSubscriber: callback.onSsS r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
+ " ss=" + signalStrength);
}
@@ -664,7 +664,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
if (DBG) {
- log("notifySignalStrengthUsingSubId: callback.onSS r=" + r
+ log("notifySignalStrengthForSubscriber: callback.onSS r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
+ " gsmSS=" + gsmSignalStrength + " ss=" + ss);
}
@@ -675,7 +675,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
} else {
- log("notifySignalStrengthUsingSubId: invalid phoneId=" + phoneId);
+ log("notifySignalStrengthForSubscriber: invalid phoneId=" + phoneId);
}
handleRemoveListLocked();
}
@@ -683,15 +683,15 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyCellInfo(List<CellInfo> cellInfo) {
- notifyCellInfoUsingSubId(mDefaultSubId, cellInfo);
+ notifyCellInfoForSubscriber(mDefaultSubId, cellInfo);
}
- public void notifyCellInfoUsingSubId(long subId, List<CellInfo> cellInfo) {
+ public void notifyCellInfoForSubscriber(long subId, List<CellInfo> cellInfo) {
if (!checkNotifyPermission("notifyCellInfo()")) {
return;
}
if (VDBG) {
- log("notifyCellInfoUsingSubId: subId=" + subId
+ log("notifyCellInfoForSubscriber: subId=" + subId
+ " cellInfo=" + cellInfo);
}
@@ -743,15 +743,15 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyMessageWaitingChanged(boolean mwi) {
- notifyMessageWaitingChangedUsingSubId(mDefaultSubId, mwi);
+ notifyMessageWaitingChangedForSubscriber(mDefaultSubId, mwi);
}
- public void notifyMessageWaitingChangedUsingSubId(long subId, boolean mwi) {
+ public void notifyMessageWaitingChangedForSubscriber(long subId, boolean mwi) {
if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
return;
}
if (VDBG) {
- log("notifyMessageWaitingChangedUsingSubId: subId=" + subId
+ log("notifyMessageWaitingChangedForSubscriber: subId=" + subId
+ " mwi=" + mwi);
}
synchronized (mRecords) {
@@ -774,15 +774,15 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyCallForwardingChanged(boolean cfi) {
- notifyCallForwardingChangedUsingSubId(mDefaultSubId, cfi);
+ notifyCallForwardingChangedForSubscriber(mDefaultSubId, cfi);
}
- public void notifyCallForwardingChangedUsingSubId(long subId, boolean cfi) {
+ public void notifyCallForwardingChangedForSubscriber(long subId, boolean cfi) {
if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
return;
}
if (VDBG) {
- log("notifyCallForwardingChangedUsingSubId: subId=" + subId
+ log("notifyCallForwardingChangedForSubscriber: subId=" + subId
+ " cfi=" + cfi);
}
synchronized (mRecords) {
@@ -805,10 +805,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyDataActivity(int state) {
- notifyDataActivityUsingSubId(mDefaultSubId, state);
+ notifyDataActivityForSubscriber(mDefaultSubId, state);
}
- public void notifyDataActivityUsingSubId(long subId, int state) {
+ public void notifyDataActivityForSubscriber(long subId, int state) {
if (!checkNotifyPermission("notifyDataActivity()" )) {
return;
}
@@ -831,12 +831,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
- notifyDataConnectionUsingSubId(mDefaultSubId, state, isDataConnectivityPossible,
+ notifyDataConnectionForSubscriber(mDefaultSubId, state, isDataConnectivityPossible,
reason, apn, apnType, linkProperties,
networkCapabilities, networkType, roaming);
}
- public void notifyDataConnectionUsingSubId(long subId, int state,
+ public void notifyDataConnectionForSubscriber(long subId, int state,
boolean isDataConnectivityPossible, String reason, String apn, String apnType,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int networkType, boolean roaming) {
@@ -844,7 +844,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
return;
}
if (VDBG) {
- log("notifyDataConnectionUsingSubId: subId=" + subId
+ log("notifyDataConnectionForSubscriber: subId=" + subId
+ " state=" + state + " isDataConnectivityPossible=" + isDataConnectivityPossible
+ " reason='" + reason
+ "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
@@ -921,16 +921,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyDataConnectionFailed(String reason, String apnType) {
- notifyDataConnectionFailedUsingSubId(mDefaultSubId, reason, apnType);
+ notifyDataConnectionFailedForSubscriber(mDefaultSubId, reason, apnType);
}
- public void notifyDataConnectionFailedUsingSubId(long subId,
+ public void notifyDataConnectionFailedForSubscriber(long subId,
String reason, String apnType) {
if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
return;
}
if (VDBG) {
- log("notifyDataConnectionFailedUsingSubId: subId=" + subId
+ log("notifyDataConnectionFailedForSubscriber: subId=" + subId
+ " reason=" + reason + " apnType=" + apnType);
}
synchronized (mRecords) {
@@ -954,17 +954,17 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyCellLocation(Bundle cellLocation) {
- notifyCellLocationUsingSubId(mDefaultSubId, cellLocation);
+ notifyCellLocationForSubscriber(mDefaultSubId, cellLocation);
}
- public void notifyCellLocationUsingSubId(long subId, Bundle cellLocation) {
- log("notifyCellLocationUsingSubId: subId=" + subId
+ public void notifyCellLocationForSubscriber(long subId, Bundle cellLocation) {
+ log("notifyCellLocationForSubscriber: subId=" + subId
+ " cellLocation=" + cellLocation);
if (!checkNotifyPermission("notifyCellLocation()")) {
return;
}
if (VDBG) {
- log("notifyCellLocationUsingSubId: subId=" + subId
+ log("notifyCellLocationForSubscriber: subId=" + subId
+ " cellLocation=" + cellLocation);
}
synchronized (mRecords) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index c469b42..888fa1a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -265,7 +265,11 @@ public class AccountManagerService
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context1, Intent intent) {
- purgeOldGrantsAll();
+ // Don't delete accounts when updating a authenticator's
+ // package.
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ purgeOldGrantsAll();
+ }
}
}, intentFilter);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0bdb964..599c3b9 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -308,7 +308,14 @@ public final class ActiveServices {
return new ComponentName("!", res.permission != null
? res.permission : "private to package");
}
+
ServiceRecord r = res.record;
+
+ if (!mAm.getUserManagerLocked().exists(r.userId)) {
+ Slog.d(TAG, "Trying to start service with non-existent user! " + r.userId);
+ return null;
+ }
+
NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
callingUid, r.packageName, service, service.getFlags(), null, r.userId);
if (unscheduleServiceRestartLocked(r, callingUid, false)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2e9df8a..4f179ce 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -196,6 +196,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
+import dalvik.system.VMRuntime;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -887,6 +888,8 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean mProcessesReady = false;
boolean mSystemReady = false;
boolean mBooting = false;
+ boolean mCallFinishBooting = false;
+ boolean mBootAnimationComplete = false;
boolean mWaitingUpdate = false;
boolean mDidUpdate = false;
boolean mOnBattery = false;
@@ -1074,13 +1077,16 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* Runtime CPU use collection thread. This object's lock is used to
- * protect all related state.
+ * perform synchronization with the thread (notifying it to run).
*/
final Thread mProcessCpuThread;
/**
- * Used to collect process stats when showing not responding dialog.
- * Protected by mProcessCpuThread.
+ * Used to collect per-process CPU use for ANRs, battery stats, etc.
+ * Must acquire this object's lock when accessing it.
+ * NOTE: this lock will be held while doing long operations (trawling
+ * through all processes in /proc), so it should never be acquired by
+ * any critical paths such as when holding the main activity manager lock.
*/
final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
MONITOR_THREAD_CPU_USAGE);
@@ -1594,7 +1600,7 @@ public final class ActivityManagerService extends ActivityManagerNative
infoMap.put(mi.pid, mi);
}
updateCpuStatsNow();
- synchronized (mProcessCpuThread) {
+ synchronized (mProcessCpuTracker) {
final int N = mProcessCpuTracker.countStats();
for (int i=0; i<N; i++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
@@ -1893,7 +1899,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (memInfo != null) {
updateCpuStatsNow();
long nativeTotalPss = 0;
- synchronized (mProcessCpuThread) {
+ synchronized (mProcessCpuTracker) {
final int N = mProcessCpuTracker.countStats();
for (int j=0; j<N; j++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(j);
@@ -1911,7 +1917,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
memInfo.readMemInfo();
- synchronized (this) {
+ synchronized (ActivityManagerService.this) {
if (DEBUG_PSS) Slog.d(TAG, "Collected native and kernel memory in "
+ (SystemClock.uptimeMillis()-start) + "ms");
mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
@@ -2182,7 +2188,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
- synchronized (mActivityManagerService.mProcessCpuThread) {
+ synchronized (mActivityManagerService.mProcessCpuTracker) {
pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
SystemClock.uptimeMillis()));
@@ -2378,7 +2384,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
void updateCpuStatsNow() {
- synchronized (mProcessCpuThread) {
+ synchronized (mProcessCpuTracker) {
mProcessCpuMutexFree.set(false);
final long now = SystemClock.uptimeMillis();
boolean haveNewCpuStats = false;
@@ -3124,6 +3130,11 @@ public final class ActivityManagerService extends ActivityManagerNative
requiredAbi = Build.SUPPORTED_ABIS[0];
}
+ String instructionSet = null;
+ if (app.info.primaryCpuAbi != null) {
+ instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
+ }
+
// 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);
@@ -3131,7 +3142,8 @@ public final class ActivityManagerService extends ActivityManagerNative
checkTime(startTime, "startProcess: asking zygote to start proc");
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
- app.info.targetSdkVersion, app.info.seinfo, requiredAbi, entryPointArgs);
+ app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
+ entryPointArgs);
checkTime(startTime, "startProcess: returned from zygote!");
if (app.isolated) {
@@ -3215,7 +3227,6 @@ public final class ActivityManagerService extends ActivityManagerNative
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
- System.currentTimeMillis(),
UsageEvents.Event.MOVE_TO_FOREGROUND);
}
synchronized (stats) {
@@ -3224,7 +3235,6 @@ public final class ActivityManagerService extends ActivityManagerNative
} else {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
- System.currentTimeMillis(),
UsageEvents.Event.MOVE_TO_BACKGROUND);
}
synchronized (stats) {
@@ -5069,7 +5079,7 @@ public final class ActivityManagerService extends ActivityManagerNative
String cpuInfo = null;
if (MONITOR_CPU_USAGE) {
updateCpuStatsNow();
- synchronized (mProcessCpuThread) {
+ synchronized (mProcessCpuTracker) {
cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
}
info.append(processCpuTracker.printCurrentLoad());
@@ -6190,6 +6200,14 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final void finishBooting() {
+ synchronized (this) {
+ if (!mBootAnimationComplete) {
+ mCallFinishBooting = true;
+ return;
+ }
+ mCallFinishBooting = false;
+ }
+
// Register receivers to handle package update events
mPackageMonitor.register(mContext, Looper.getMainLooper(), false);
@@ -6248,6 +6266,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
+ public void bootAnimationComplete() {
+ final boolean callFinishBooting;
+ synchronized (this) {
+ callFinishBooting = mCallFinishBooting;
+ mBootAnimationComplete = true;
+ }
+ if (callFinishBooting) {
+ finishBooting();
+ }
+ }
+
final void ensureBootCompleted() {
boolean booting;
boolean enableScreen;
@@ -6280,12 +6310,12 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
- public final void activityPaused(IBinder token, PersistableBundle persistentState) {
+ public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- stack.activityPausedLocked(token, false, persistentState);
+ stack.activityPausedLocked(token, false);
}
}
Binder.restoreCallingIdentity(origId);
@@ -7441,12 +7471,33 @@ public final class ActivityManagerService extends ActivityManagerNative
// Does the caller have this permission on the URI?
if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
- // Right now, if you are not the original owner of the permission,
- // you are not allowed to revoke it.
- //if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
- throw new SecurityException("Uid " + callingUid
- + " does not have permission to uri " + grantUri);
- //}
+ // Have they don't have direct access to the URI, then revoke any URI
+ // permissions that have been granted to them.
+ final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+ if (perms != null) {
+ boolean persistChanged = false;
+ for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) {
+ final UriPermission perm = it.next();
+ if (perm.uri.sourceUserId == grantUri.sourceUserId
+ && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
+ if (DEBUG_URI_PERMISSION)
+ Slog.v(TAG,
+ "Revoking " + perm.targetUid + " permission to " + perm.uri);
+ persistChanged |= perm.revokeModes(
+ modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+ if (perm.modeFlags == 0) {
+ it.remove();
+ }
+ }
+ }
+ if (perms.isEmpty()) {
+ mGrantedUriPermissions.remove(callingUid);
+ }
+ if (persistChanged) {
+ schedulePersistUriGrants();
+ }
+ }
+ return;
}
boolean persistChanged = false;
@@ -9660,14 +9711,9 @@ public final class ActivityManagerService extends ActivityManagerNative
long ident = 0;
boolean clearedIdentity = false;
userId = unsafeConvertIncomingUser(userId);
- if (UserHandle.getUserId(callingUid) != userId) {
- if (checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
- callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
- || checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
- callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
- clearedIdentity = true;
- ident = Binder.clearCallingIdentity();
- }
+ if (canClearIdentity(callingPid, callingUid, userId)) {
+ clearedIdentity = true;
+ ident = Binder.clearCallingIdentity();
}
ContentProviderHolder holder = null;
try {
@@ -9695,6 +9741,19 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
+ private boolean canClearIdentity(int callingPid, int callingUid, int userId) {
+ if (UserHandle.getUserId(callingUid) == userId) {
+ return true;
+ }
+ if (checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
+ || checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ return false;
+ }
+
// =========================================================
// GLOBAL MANAGEMENT
// =========================================================
@@ -9947,7 +10006,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private void updateEventDispatchingLocked() {
- mWindowManager.setEventDispatching(mBooted && !mWentToSleep && !mShuttingDown);
+ mWindowManager.setEventDispatching(mBooted && !mShuttingDown);
}
public void setLockScreenShown(boolean shown) {
@@ -10346,8 +10405,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final boolean translucentChanged = r.changeWindowTranslucency(false);
if (translucentChanged) {
r.task.stack.convertToTranslucent(r);
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
mWindowManager.setAppFullscreen(token, false);
return translucentChanged;
}
@@ -13695,7 +13754,7 @@ public final class ActivityManagerService extends ActivityManagerNative
findPid = Integer.parseInt(args[opti]);
} catch (NumberFormatException e) {
}
- synchronized (mProcessCpuThread) {
+ synchronized (mProcessCpuTracker) {
final int N = mProcessCpuTracker.countStats();
for (int i=0; i<N; i++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
@@ -13857,7 +13916,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// If we are showing aggregations, also look for native processes to
// include so that our aggregations are more accurate.
updateCpuStatsNow();
- synchronized (mProcessCpuThread) {
+ synchronized (mProcessCpuTracker) {
final int N = mProcessCpuTracker.countStats();
for (int i=0; i<N; i++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
@@ -15936,6 +15995,7 @@ public final class ActivityManagerService extends ActivityManagerNative
newConfig.seq = mConfigurationSeq;
mConfiguration = newConfig;
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
+ mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
//mUsageStatsService.noteStartConfig(newConfig);
final Configuration configCopy = new Configuration(mConfiguration);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 77b4cc9..e043f03 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -698,12 +698,12 @@ final class ActivityRecord {
case ActivityOptions.ANIM_SCALE_UP:
service.mWindowManager.overridePendingAppTransitionScaleUp(
pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getStartWidth(), pendingOptions.getStartHeight());
+ pendingOptions.getWidth(), pendingOptions.getHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
- pendingOptions.getStartX()+pendingOptions.getStartWidth(),
- pendingOptions.getStartY()+pendingOptions.getStartHeight()));
+ pendingOptions.getStartX()+pendingOptions.getWidth(),
+ pendingOptions.getStartY()+pendingOptions.getHeight()));
}
break;
case ActivityOptions.ANIM_THUMBNAIL_SCALE_UP:
@@ -728,15 +728,14 @@ final class ActivityRecord {
service.mWindowManager.overridePendingAppTransitionAspectScaledThumb(
pendingOptions.getThumbnail(),
pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight(),
pendingOptions.getOnAnimationStartListener(),
(animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP));
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
- pendingOptions.getStartX()
- + pendingOptions.getThumbnail().getWidth(),
- pendingOptions.getStartY()
- + pendingOptions.getThumbnail().getHeight()));
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
}
break;
default:
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 3efd049..d71f94e 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -241,6 +241,9 @@ final class ActivityStack {
/** Run all ActivityStacks through this */
final ActivityStackSupervisor mStackSupervisor;
+ /** Used to keep resumeTopActivityLocked() from being entered recursively */
+ private boolean inResumeTopActivity;
+
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -281,7 +284,7 @@ final class ActivityStack {
if (r.app != null) {
mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
- activityPausedLocked(r.appToken, true, r.persistentState);
+ activityPausedLocked(r.appToken, true);
}
} break;
case LAUNCH_TICK_MSG: {
@@ -422,13 +425,13 @@ final class ActivityStack {
}
final ActivityRecord topActivity() {
- // Iterate to find the first non-empty task stack. Note that this code can
- // be simplified once we stop storing tasks with empty mActivities lists.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- final int topActivityNdx = activities.size() - 1;
- if (topActivityNdx >= 0) {
- return activities.get(topActivityNdx);
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (!r.finishing) {
+ return r;
+ }
}
}
return null;
@@ -456,7 +459,7 @@ final class ActivityStack {
final ActivityRecord r = ActivityRecord.forToken(token);
if (r != null) {
final TaskRecord task = r.task;
- if (task.mActivities.contains(r) && mTaskHistory.contains(task)) {
+ if (task != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
if (task.stack != this) Slog.w(TAG,
"Illegal state! task does not point to stack it is in.");
return r;
@@ -709,7 +712,7 @@ final class ActivityStack {
// Still have something resumed; can't sleep until it is paused.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
- startPausingLocked(false, true);
+ startPausingLocked(false, true, false, false);
return true;
}
if (mPausingActivity != null) {
@@ -787,22 +790,38 @@ final class ActivityStack {
return null;
}
- final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+ /**
+ * Start pausing the currently resumed activity. It is an error to call this if there
+ * is already an activity being paused or there is no resumed activity.
+ *
+ * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+ * @param uiSleeping True if this is happening with the user interface going to sleep (the
+ * screen turning off).
+ * @param resuming True if this is being called as part of resuming the top activity, so
+ * we shouldn't try to instigate a resume here.
+ * @param dontWait True if the caller does not want to wait for the pause to complete. If
+ * set to true, we will immediately complete the pause here before returning.
+ * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+ * it to tell us when it is done.
+ */
+ final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
+ boolean dontWait) {
if (mPausingActivity != null) {
- Slog.e(TAG, "Trying to pause when pause is already pending for "
- + mPausingActivity, new RuntimeException("here").fillInStackTrace());
+ Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity);
+ completePauseLocked(false);
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
- Slog.e(TAG, "Trying to pause when nothing is resumed",
- new RuntimeException("here").fillInStackTrace());
- mStackSupervisor.resumeTopActivitiesLocked();
- return;
+ if (!resuming) {
+ Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+ mStackSupervisor.resumeTopActivitiesLocked();
+ }
+ return false;
}
if (mActivityContainer.mParentActivity == null) {
// Top level stack, not a child. Look for child stacks.
- mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping);
+ mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
}
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
@@ -831,7 +850,7 @@ final class ActivityStack {
prev.shortComponentName);
mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
- userLeaving, prev.configChangeFlags);
+ userLeaving, prev.configChangeFlags, dontWait);
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
@@ -862,39 +881,46 @@ final class ActivityStack {
if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
}
- // Schedule a pause timeout in case the app doesn't respond.
- // We don't give it much time because this directly impacts the
- // responsiveness seen by the user.
- Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
- msg.obj = prev;
- prev.pauseTime = SystemClock.uptimeMillis();
- mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
- if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ if (dontWait) {
+ // If the caller said they don't want to wait for the pause, then complete
+ // the pause now.
+ completePauseLocked(false);
+ return false;
+
+ } else {
+ // Schedule a pause timeout in case the app doesn't respond.
+ // We don't give it much time because this directly impacts the
+ // responsiveness seen by the user.
+ Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+ msg.obj = prev;
+ prev.pauseTime = SystemClock.uptimeMillis();
+ mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+ if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ return true;
+ }
+
} else {
// This activity failed to schedule the
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ if (!resuming) {
+ mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ }
+ return false;
}
}
- final void activityPausedLocked(IBinder token, boolean timeout,
- PersistableBundle persistentState) {
+ final void activityPausedLocked(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- if (persistentState != null) {
- r.persistentState = persistentState;
- mService.notifyTaskPersisterLocked(r.task, false);
- }
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
- r.state = ActivityState.PAUSED;
- completePauseLocked();
+ completePauseLocked(true);
} else {
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
r.userId, System.identityHashCode(r), r.shortComponentName,
@@ -945,11 +971,12 @@ final class ActivityStack {
}
}
- private void completePauseLocked() {
+ private void completePauseLocked(boolean resumeNext) {
ActivityRecord prev = mPausingActivity;
if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
if (prev != null) {
+ prev.state = ActivityState.PAUSED;
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
@@ -992,19 +1019,21 @@ final class ActivityStack {
mPausingActivity = null;
}
- final ActivityStack topStack = mStackSupervisor.getFocusedStack();
- if (!mService.isSleepingOrShuttingDown()) {
- mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
- } else {
- mStackSupervisor.checkReadyForSleepLocked();
- ActivityRecord top = topStack.topRunningActivityLocked(null);
- if (top == null || (prev != null && top != prev)) {
- // If there are no more activities available to run,
- // do resume anyway to start something. Also if the top
- // activity on the stack is not the just paused activity,
- // we need to go ahead and resume it to ensure we complete
- // an in-flight app switch.
- mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+ if (resumeNext) {
+ final ActivityStack topStack = mStackSupervisor.getFocusedStack();
+ if (!mService.isSleepingOrShuttingDown()) {
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
+ } else {
+ mStackSupervisor.checkReadyForSleepLocked();
+ ActivityRecord top = topStack.topRunningActivityLocked(null);
+ if (top == null || (prev != null && top != prev)) {
+ // If there are no more activities available to run,
+ // do resume anyway to start something. Also if the top
+ // activity on the stack is not the just paused activity,
+ // we need to go ahead and resume it to ensure we complete
+ // an in-flight app switch.
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+ }
}
}
@@ -1013,11 +1042,8 @@ final class ActivityStack {
if (prev.app != null && prev.cpuTimeAtResume > 0
&& mService.mBatteryStatsService.isOnBattery()) {
- long diff;
- synchronized (mService.mProcessCpuThread) {
- diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)
- - prev.cpuTimeAtResume;
- }
+ long diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)
+ - prev.cpuTimeAtResume;
if (diff > 0) {
BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
synchronized (bsi) {
@@ -1068,9 +1094,7 @@ final class ActivityStack {
// TODO: To be more accurate, the mark should be before the onCreate,
// not after the onResume. But for subsequent starts, onResume is fine.
if (next.app != null) {
- synchronized (mService.mProcessCpuThread) {
- next.cpuTimeAtResume = mService.mProcessCpuTracker.getCpuTimeForPid(next.app.pid);
- }
+ next.cpuTimeAtResume = mService.mProcessCpuTracker.getCpuTimeForPid(next.app.pid);
} else {
next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
}
@@ -1439,6 +1463,23 @@ final class ActivityStack {
}
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
+ if (inResumeTopActivity) {
+ // Don't even start recursing.
+ return false;
+ }
+
+ boolean result = false;
+ try {
+ // Protect against recursion.
+ inResumeTopActivity = true;
+ result = resumeTopActivityInnerLocked(prev, options);
+ } finally {
+ inResumeTopActivity = false;
+ }
+ return result;
+ }
+
+ final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
ActivityRecord parent = mActivityContainer.mParentActivity;
@@ -1587,10 +1628,10 @@ final class ActivityStack {
// We need to start pausing the current activity so the top one
// can be resumed...
- boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving);
+ boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;
+ boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
if (mResumedActivity != null) {
- pausing = true;
- startPausingLocked(userLeaving, false);
+ pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);
}
if (pausing) {
@@ -2700,7 +2741,7 @@ final class ActivityStack {
if (mPausingActivity == null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
- startPausingLocked(false, false);
+ startPausingLocked(false, false, false, false);
}
if (endTask) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 780efa1..b8261a4 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -592,7 +592,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
* @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
* @return true if any activity was paused as a result of this call.
*/
- boolean pauseBackStacks(boolean userLeaving) {
+ boolean pauseBackStacks(boolean userLeaving, boolean resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -601,8 +601,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (!isFrontStack(stack) && stack.mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.mResumedActivity);
- stack.startPausingLocked(userLeaving, false);
- someActivityPaused = true;
+ someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
+ dontWait);
}
}
}
@@ -631,7 +631,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
return pausing;
}
- void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping) {
+ void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping,
+ boolean resuming, boolean dontWait) {
// TODO: Put all stacks in supervisor and iterate through them instead.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -639,7 +640,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
final ActivityStack stack = stacks.get(stackNdx);
if (stack.mResumedActivity != null &&
stack.mActivityContainer.mParentActivity == parent) {
- stack.startPausingLocked(userLeaving, uiSleeping);
+ stack.startPausingLocked(userLeaving, uiSleeping, resuming, dontWait);
}
}
}
@@ -1640,57 +1641,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
- "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // The original activity who is starting us is running as a single
- // instance... this new activity it is starting must go on its
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (launchSingleInstance || launchSingleTask) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
-
- ActivityInfo newTaskInfo = null;
- Intent newTaskIntent = null;
- ActivityStack sourceStack;
- if (sourceRecord != null) {
- if (sourceRecord.finishing) {
- // If the source is finishing, we can't further count it as our source. This
- // is because the task it is associated with may now be empty and on its way out,
- // so we don't want to blindly throw it in to that task. Instead we will take
- // the NEW_TASK flow and try to find a task for it. But save the task information
- // so it can be used when creating the new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
- Slog.w(TAG, "startActivity called from finishing " + sourceRecord
- + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- newTaskInfo = sourceRecord.info;
- newTaskIntent = sourceRecord.task.intent;
- }
- sourceRecord = null;
- sourceStack = null;
- } else {
- sourceStack = sourceRecord.task.stack;
- }
- } else {
- sourceStack = null;
- }
-
boolean addingToTask = false;
- boolean movedHome = false;
TaskRecord reuseTask = null;
- ActivityStack targetStack;
-
- intent.setFlags(launchFlags);
// If the caller is not coming from another activity, but has given us an
// explicit task into which they would like us to launch the new activity,
@@ -1746,6 +1698,58 @@ public final class ActivityStackSupervisor implements DisplayListener {
inTask = null;
}
+ if (inTask == null) {
+ if (sourceRecord == null) {
+ // This activity is not being started from another... in this
+ // case we -always- start a new task.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
+ Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
+ "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // The original activity who is starting us is running as a single
+ // instance... this new activity it is starting must go on its
+ // own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ } else if (launchSingleInstance || launchSingleTask) {
+ // The activity being started is a single instance... it always
+ // gets launched into its own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ }
+
+ ActivityInfo newTaskInfo = null;
+ Intent newTaskIntent = null;
+ ActivityStack sourceStack;
+ if (sourceRecord != null) {
+ if (sourceRecord.finishing) {
+ // If the source is finishing, we can't further count it as our source. This
+ // is because the task it is associated with may now be empty and on its way out,
+ // so we don't want to blindly throw it in to that task. Instead we will take
+ // the NEW_TASK flow and try to find a task for it. But save the task information
+ // so it can be used when creating the new task.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+ + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ newTaskInfo = sourceRecord.info;
+ newTaskIntent = sourceRecord.task.intent;
+ }
+ sourceRecord = null;
+ sourceStack = null;
+ } else {
+ sourceStack = sourceRecord.task.stack;
+ }
+ } else {
+ sourceStack = null;
+ }
+
+ boolean movedHome = false;
+ ActivityStack targetStack;
+
+ intent.setFlags(launchFlags);
+
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
@@ -1768,7 +1772,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (intentActivity != null) {
if (isLockTaskModeViolation(intentActivity.task)) {
showLockTaskToast();
- Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
+ Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
if (r.task == null) {
@@ -2779,9 +2783,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
// A non-top activity is reporting a visibility change.
- if ((visible && (top.fullscreen || top.state != ActivityState.RESUMED)) ||
- top.app == null || top.app.thread == null) {
- // Can't carry out this request.
+ if (visible && top.fullscreen) {
+ // Let the caller know that it can't be seen.
if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG, "requestVisibleBehind: returning top.fullscreen="
+ top.fullscreen + " top.state=" + top.state + " top.app=" + top.app +
" top.app.thread=" + top.app.thread);
@@ -2803,9 +2806,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
mService.convertFromTranslucent(next.appToken);
}
}
- try {
- top.app.thread.scheduleBackgroundVisibleBehindChanged(top.appToken, visible);
- } catch (RemoteException e) {
+ if (top.app != null && top.app.thread != null) {
+ // Notify the top app of the change.
+ try {
+ top.app.thread.scheduleBackgroundVisibleBehindChanged(top.appToken, visible);
+ } catch (RemoteException e) {
+ }
}
return true;
}
@@ -3840,7 +3846,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
mContainerState = CONTAINER_STATE_NO_SURFACE;
((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
- mStack.startPausingLocked(false, true);
+ mStack.startPausingLocked(false, true, false, false);
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 5a97aee..bba786d 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -47,6 +47,7 @@ public class NetworkAgentInfo {
public final NetworkMonitor networkMonitor;
public final NetworkMisc networkMisc;
public boolean created;
+ public boolean validated;
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
@@ -68,6 +69,7 @@ public class NetworkAgentInfo {
networkMonitor = new NetworkMonitor(context, handler, this);
networkMisc = misc;
created = false;
+ validated = false;
}
public void addRequest(NetworkRequest networkRequest) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index ff319d3..96872a7 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -27,6 +27,7 @@ import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.TrafficStats;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
@@ -140,34 +141,28 @@ public class NetworkMonitor extends StateMachine {
private static final int CMD_REEVALUATE = BASE + 6;
/**
- * Message to self indicating network evaluation is complete.
- * arg1 = Token to ignore old messages.
- * arg2 = HTTP response code of network evaluation.
- */
- private static final int EVENT_REEVALUATION_COMPLETE = BASE + 7;
-
- /**
* Inform NetworkMonitor that the network has disconnected.
*/
- public static final int CMD_NETWORK_DISCONNECTED = BASE + 8;
+ public static final int CMD_NETWORK_DISCONNECTED = BASE + 7;
/**
* Force evaluation even if it has succeeded in the past.
+ * arg1 = UID responsible for requesting this reeval. Will be billed for data.
*/
- public static final int CMD_FORCE_REEVALUATION = BASE + 9;
+ public static final int CMD_FORCE_REEVALUATION = BASE + 8;
/**
* Message to self indicating captive portal login is complete.
* arg1 = Token to ignore old messages.
* arg2 = 1 if we should use this network, 0 otherwise.
*/
- private static final int CMD_CAPTIVE_PORTAL_LOGGED_IN = BASE + 10;
+ private static final int CMD_CAPTIVE_PORTAL_LOGGED_IN = BASE + 9;
/**
* Message to self indicating user desires to log into captive portal.
* arg1 = Token to ignore old messages.
*/
- private static final int CMD_USER_WANTS_SIGN_IN = BASE + 11;
+ private static final int CMD_USER_WANTS_SIGN_IN = BASE + 10;
/**
* Request ConnectivityService display provisioning notification.
@@ -175,22 +170,22 @@ public class NetworkMonitor extends StateMachine {
* arg2 = NetID.
* obj = Intent to be launched when notification selected by user, null if !arg1.
*/
- public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 12;
+ public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 11;
/**
* Message to self indicating sign-in app bypassed captive portal.
*/
- private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 13;
+ private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 12;
/**
* Message to self indicating no sign-in app responded.
*/
- private static final int EVENT_NO_APP_RESPONSE = BASE + 14;
+ private static final int EVENT_NO_APP_RESPONSE = BASE + 13;
/**
* Message to self indicating sign-in app indicates sign-in is not possible.
*/
- private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 15;
+ private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 14;
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
// Default to 30s linger time-out.
@@ -205,6 +200,8 @@ public class NetworkMonitor extends StateMachine {
private static final int MAX_RETRIES = 10;
private final int mReevaluateDelayMs;
private int mReevaluateToken = 0;
+ private static final int INVALID_UID = -1;
+ private int mUidResponsibleForReeval = INVALID_UID;
private int mCaptivePortalLoggedInToken = 0;
private int mUserPromptedToken = 0;
@@ -282,6 +279,7 @@ public class NetworkMonitor extends StateMachine {
return HANDLED;
case CMD_FORCE_REEVALUATION:
if (DBG) log("Forcing reevaluation");
+ mUidResponsibleForReeval = message.arg1;
transitionTo(mEvaluatingState);
return HANDLED;
default:
@@ -322,20 +320,14 @@ public class NetworkMonitor extends StateMachine {
private class EvaluatingState extends State {
private int mRetries;
- private class EvaluateInternetConnectivity extends Thread {
- private int mToken;
- EvaluateInternetConnectivity(int token) {
- mToken = token;
- }
- public void run() {
- sendMessage(EVENT_REEVALUATION_COMPLETE, mToken, isCaptivePortal());
- }
- }
-
@Override
public void enter() {
mRetries = 0;
sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
+ if (mUidResponsibleForReeval != INVALID_UID) {
+ TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
+ mUidResponsibleForReeval = INVALID_UID;
+ }
}
@Override
@@ -356,14 +348,7 @@ public class NetworkMonitor extends StateMachine {
transitionTo(mValidatedState);
return HANDLED;
}
- // Kick off a thread to perform internet connectivity evaluation.
- Thread thread = new EvaluateInternetConnectivity(mReevaluateToken);
- thread.run();
- return HANDLED;
- case EVENT_REEVALUATION_COMPLETE:
- if (message.arg1 != mReevaluateToken)
- return HANDLED;
- int httpResponseCode = message.arg2;
+ int httpResponseCode = isCaptivePortal();
if (httpResponseCode == 204) {
transitionTo(mValidatedState);
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
@@ -375,10 +360,18 @@ public class NetworkMonitor extends StateMachine {
sendMessageDelayed(msg, mReevaluateDelayMs);
}
return HANDLED;
+ case CMD_FORCE_REEVALUATION:
+ // Ignore duplicate requests.
+ return HANDLED;
default:
return NOT_HANDLED;
}
}
+
+ @Override
+ public void exit() {
+ TrafficStats.clearThreadStatsUid();
+ }
}
private class UserPromptedState extends State {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index f6beb9a..9292d45 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -16,6 +16,7 @@
package com.android.server.content;
+import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountAndUser;
import android.accounts.AccountManager;
@@ -478,7 +479,7 @@ public class SyncManager {
mContext.registerReceiverAsUser(mAccountsUpdatedReceiver,
UserHandle.ALL,
new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION),
- null, null);
+ Manifest.permission.ACCOUNT_MANAGER, null);
}
// Pick a random second in a day to seed all periodic syncs
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index aa1310d..6ce235b 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -159,6 +159,8 @@ final class Constants {
static final int TRUE = 1;
static final int FALSE = 0;
+ // Internal abort error code. It's the same as success.
+ static final int ABORT_NO_ERROR = -1;
// Constants related to operands of HDMI CEC commands.
// Refer to CEC Table 29 in HDMI Spec v1.4b.
// [Abort Reason]
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index bb22b4d..c5a6dbd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -31,8 +31,6 @@ import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
import libcore.util.EmptyArray;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -102,11 +100,6 @@ final class HdmiCecController {
// Stores the local CEC devices in the system. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
- @IoThreadOnly
- private final HdmiLogger mIoThreadLogger = new HdmiLogger(TAG);
- @ServiceThreadOnly
- private final HdmiLogger mServiceThreadLogger = new HdmiLogger(TAG);
-
// Private constructor. Use HdmiCecController.create().
private HdmiCecController(HdmiControlService service) {
mService = service;
@@ -210,9 +203,8 @@ final class HdmiCecController {
}
final int assignedAddress = logicalAddress;
- mIoThreadLogger.debug(
- String.format("New logical address for device [%d]: [preferred:%d, assigned:%d]",
- deviceType, preferredAddress, assignedAddress));
+ HdmiLogger.debug("New logical address for device [%d]: [preferred:%d, assigned:%d]",
+ deviceType, preferredAddress, assignedAddress);
if (callback != null) {
runOnServiceThread(new Runnable() {
@Override
@@ -449,7 +441,7 @@ final class HdmiCecController {
allocated.add(address);
}
}
- mIoThreadLogger.debug("[P]:Allocated Address=" + allocated);
+ HdmiLogger.debug("[P]:Allocated Address=" + allocated);
if (callback != null) {
runOnServiceThread(new Runnable() {
@Override
@@ -551,7 +543,7 @@ final class HdmiCecController {
runOnIoThread(new Runnable() {
@Override
public void run() {
- mIoThreadLogger.debug("[S]:" + cecMessage);
+ HdmiLogger.debug("[S]:" + cecMessage);
byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
int i = 0;
int errorCode = Constants.SEND_RESULT_SUCCESS;
@@ -586,7 +578,7 @@ final class HdmiCecController {
private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
- mServiceThreadLogger.debug("[R]:" + command);
+ HdmiLogger.debug("[R]:" + command);
onReceiveCommand(command);
}
@@ -596,8 +588,7 @@ final class HdmiCecController {
@ServiceThreadOnly
private void handleHotplug(int port, boolean connected) {
assertRunOnServiceThread();
- mServiceThreadLogger.debug(
- "Hotplug event:[port:" + port + " , connected:" + connected + "]");
+ HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
mService.onHotplug(port, connected);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 85f5be2..b2300a6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -43,9 +43,6 @@ import java.util.List;
*/
abstract class HdmiCecFeatureAction {
private static final String TAG = "HdmiCecFeatureAction";
- // As all actions run in the same thread (service thread), it's fine to have single logger.
- // TODO: create global logger for each threads and use them.
- protected static final HdmiLogger DLOGGER = new HdmiLogger(TAG);
// Timer handler message used for timeout event
protected static final int MSG_TIMEOUT = 100;
@@ -264,10 +261,7 @@ abstract class HdmiCecFeatureAction {
}
protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
- getSourceAddress(), targetAddress, uiCommand));
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
- getSourceAddress(), targetAddress));
+ mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
}
protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index bc6a299..c00c5d0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -274,6 +274,12 @@ abstract class HdmiCecLocalDevice {
return handleRecordTvScreen(message);
case Constants.MESSAGE_TIMER_CLEARED_STATUS:
return handleTimerClearedStatus(message);
+ case Constants.MESSAGE_REPORT_POWER_STATUS:
+ return handleReportPowerStatus(message);
+ case Constants.MESSAGE_TIMER_STATUS:
+ return handleTimerStatus(message);
+ case Constants.MESSAGE_RECORD_STATUS:
+ return handleRecordStatus(message);
default:
return false;
}
@@ -541,6 +547,18 @@ abstract class HdmiCecLocalDevice {
return false;
}
+ protected boolean handleReportPowerStatus(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleTimerStatus(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleRecordStatus(HdmiCecMessage message) {
+ return false;
+ }
+
@ServiceThreadOnly
final void handleAddressAllocated(int logicalAddress, int reason) {
assertRunOnServiceThread();
@@ -810,6 +828,13 @@ abstract class HdmiCecLocalDevice {
Slog.w(TAG, "sendKeyEvent not implemented");
}
+ void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlPressed(
+ mAddress, targetAddress, cecKeycode));
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+ mAddress, targetAddress));
+ }
+
/**
* Dump internal status of HdmiCecLocalDevice object.
*/
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7ae2198..58ccbdb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -120,8 +120,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// other CEC devices since they might not have logical address.
private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>();
- private final HdmiLogger mSafeLogger = new HdmiLogger(TAG);
-
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_TV);
mPrevPortId = Constants.INVALID_PORT_ID;
@@ -476,6 +474,25 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return true;
}
+ @Override
+ protected boolean handleReportPowerStatus(HdmiCecMessage command) {
+ int newStatus = command.getParams()[0] & 0xFF;
+ updateDevicePowerStatus(command.getSource(), newStatus);
+ return true;
+ }
+
+ @Override
+ protected boolean handleTimerStatus(HdmiCecMessage message) {
+ // Do nothing.
+ return true;
+ }
+
+ @Override
+ protected boolean handleRecordStatus(HdmiCecMessage message) {
+ // Do nothing.
+ return true;
+ }
+
boolean updateCecSwitchInfo(int address, int type, int path) {
if (address == Constants.ADDR_UNREGISTERED
&& type == HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH) {
@@ -700,8 +717,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// # Seq 25
void setSystemAudioMode(boolean on, boolean updateSetting) {
- mSafeLogger.debug(String.format("System Audio Mode change[old:%b new:%b]",
- mSystemAudioActivated, on));
+ HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
if (updateSetting) {
mService.writeBooleanSetting(Global.HDMI_SYSTEM_AUDIO_ENABLED, on);
@@ -832,6 +848,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
AudioManager.STREAM_MUSIC);
mService.setAudioStatus(mute,
VolumeControlAction.scaleToCustomVolume(volume, maxVolume));
+ displayOsd(HdmiControlManager.OSD_MESSAGE_AVR_VOLUME_CHANGED,
+ mute ? HdmiControlManager.AVR_VOLUME_MUTED : volume);
}
}
@@ -855,12 +873,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
}
- // Remove existing volume action.
- removeAction(VolumeControlAction.class);
-
- HdmiDeviceInfo avr = getAvrDeviceInfo();
- addAndStartAction(VolumeControlAction.ofVolumeChange(this, avr.getLogicalAddress(),
- cecVolume, delta > 0));
+ List<VolumeControlAction> actions = getActions(VolumeControlAction.class);
+ if (actions.isEmpty()) {
+ addAndStartAction(new VolumeControlAction(this,
+ getAvrDeviceInfo().getLogicalAddress(), delta > 0));
+ } else {
+ actions.get(0).handleVolumeChange(delta > 0);
+ }
}
@ServiceThreadOnly
@@ -872,8 +891,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// Remove existing volume action.
removeAction(VolumeControlAction.class);
- HdmiDeviceInfo avr = getAvrDeviceInfo();
- addAndStartAction(VolumeControlAction.ofMute(this, avr.getLogicalAddress(), mute));
+ sendUserControlPressedAndReleased(getAvrDeviceInfo().getLogicalAddress(),
+ mute ? HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION :
+ HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
}
@Override
@@ -935,8 +955,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
assertRunOnServiceThread();
if (!isMessageForSystemAudio(message)) {
- mSafeLogger.warning("Invalid <Set System Audio Mode> message:" + message);
- return false;
+ HdmiLogger.warning("Invalid <Set System Audio Mode> message:" + message);
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+ return true;
}
SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message), null);
@@ -949,8 +970,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
assertRunOnServiceThread();
if (!isMessageForSystemAudio(message)) {
- mSafeLogger.warning("Invalid <System Audio Mode Status> message:" + message);
- return false;
+ HdmiLogger.warning("Invalid <System Audio Mode Status> message:" + message);
+ // Ignore this message.
+ return true;
}
setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message), true);
return true;
@@ -973,7 +995,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
int recorderAddress = message.getSource();
byte[] recordSource = mService.invokeRecordRequestListener(recorderAddress);
- startOneTouchRecord(recorderAddress, recordSource);
+ int reason = startOneTouchRecord(recorderAddress, recordSource);
+ if (reason != Constants.ABORT_NO_ERROR) {
+ mService.maySendFeatureAbortCommand(message, reason);
+ }
return true;
}
@@ -998,7 +1023,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
private boolean isMessageForSystemAudio(HdmiCecMessage message) {
- return message.getSource() == Constants.ADDR_AUDIO_SYSTEM
+ return mService.isControlEnabled()
+ && message.getSource() == Constants.ADDR_AUDIO_SYSTEM
&& (message.getDestination() == Constants.ADDR_TV
|| message.getDestination() == Constants.ADDR_BROADCAST)
&& getAvrDeviceInfo() != null;
@@ -1445,31 +1471,38 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mService.displayOsd(messageId);
}
+ @ServiceThreadOnly
+ void displayOsd(int messageId, int extra) {
+ assertRunOnServiceThread();
+ mService.displayOsd(messageId, extra);
+ }
+
// Seq #54 and #55
@ServiceThreadOnly
- void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
+ int startOneTouchRecord(int recorderAddress, byte[] recordSource) {
assertRunOnServiceThread();
if (!mService.isControlEnabled()) {
Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
announceOneTouchRecordResult(ONE_TOUCH_RECORD_CEC_DISABLED);
- return;
+ return Constants.ABORT_NOT_IN_CORRECT_MODE;
}
if (!checkRecorder(recorderAddress)) {
Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
announceOneTouchRecordResult(ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
- return;
+ return Constants.ABORT_NOT_IN_CORRECT_MODE;
}
if (!checkRecordSource(recordSource)) {
Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
announceOneTouchRecordResult(ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN);
- return;
+ return Constants.ABORT_UNABLE_TO_DETERMINE;
}
addAndStartAction(new OneTouchRecordAction(this, recorderAddress, recordSource));
Slog.i(TAG, "Start new [One Touch Record]-Target:" + recorderAddress + ", recordSource:"
+ Arrays.toString(recordSource));
+ return Constants.ABORT_NO_ERROR;
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 8b345cf..0b3d9fb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -51,7 +51,6 @@ public final class HdmiCecMessageValidator {
}
final SparseArray<ValidationInfo> mValidationInfo = new SparseArray<>();
- private final HdmiLogger mSpamSafeLogger = new HdmiLogger(TAG);
public HdmiCecMessageValidator(HdmiControlService service) {
mService = service;
@@ -183,32 +182,32 @@ public final class HdmiCecMessageValidator {
int opcode = message.getOpcode();
ValidationInfo info = mValidationInfo.get(opcode);
if (info == null) {
- mSpamSafeLogger.warning("No validation information for the message: " + message);
+ HdmiLogger.warning("No validation information for the message: " + message);
return true;
}
// Check the source field.
if (message.getSource() == Constants.ADDR_UNREGISTERED &&
(info.addressType & SRC_UNREGISTERED) == 0) {
- mSpamSafeLogger.warning("Unexpected source: " + message);
+ HdmiLogger.warning("Unexpected source: " + message);
return false;
}
// Check the destination field.
if (message.getDestination() == Constants.ADDR_BROADCAST) {
if ((info.addressType & DEST_BROADCAST) == 0) {
- mSpamSafeLogger.warning("Unexpected broadcast message: " + message);
+ HdmiLogger.warning("Unexpected broadcast message: " + message);
return false;
}
} else { // Direct addressing.
if ((info.addressType & DEST_DIRECT) == 0) {
- mSpamSafeLogger.warning("Unexpected direct message: " + message);
+ HdmiLogger.warning("Unexpected direct message: " + message);
return false;
}
}
// Check the parameter type.
if (!info.parameterValidator.isValid(message.getParams())) {
- mSpamSafeLogger.warning("Unexpected parameters: " + message);
+ HdmiLogger.warning("Unexpected parameters: " + message);
return false;
}
return true;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9314cf8..5cd7c01 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -166,8 +166,6 @@ public final class HdmiControlService extends SystemService {
// Type of logical devices hosted in the system. Stored in the unmodifiable list.
private final List<Integer> mLocalDevices;
- private final HdmiLogger mSpamSafeLogger = new HdmiLogger(TAG);
-
// List of records for hotplug event listener to handle the the caller killed in action.
@GuardedBy("mLock")
private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
@@ -258,7 +256,7 @@ public final class HdmiControlService extends SystemService {
private List<HdmiDeviceInfo> mMhlDevices;
@Nullable
- private HdmiMhlController mMhlController;
+ private HdmiMhlControllerStub mMhlController;
// 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.
@@ -307,7 +305,7 @@ public final class HdmiControlService extends SystemService {
Slog.i(TAG, "Device does not support HDMI-CEC.");
}
- mMhlController = HdmiMhlController.create(this);
+ mMhlController = HdmiMhlControllerStub.create(this);
if (!mMhlController.isReady()) {
Slog.i(TAG, "Device does not support MHL-control.");
}
@@ -656,7 +654,7 @@ public final class HdmiControlService extends SystemService {
if (mMessageValidator.isValid(command)) {
mCecController.sendCommand(command, callback);
} else {
- mSpamSafeLogger.error("Invalid message type:" + command);
+ HdmiLogger.error("Invalid message type:" + command);
if (callback != null) {
callback.onSendCompleted(Constants.SEND_RESULT_FAILURE);
}
@@ -705,7 +703,7 @@ public final class HdmiControlService extends SystemService {
}
if (message.getDestination() != Constants.ADDR_BROADCAST) {
- mSpamSafeLogger.warning("Unhandled cec command:" + message);
+ HdmiLogger.warning("Unhandled cec command:" + message);
}
return false;
}
@@ -794,7 +792,6 @@ public final class HdmiControlService extends SystemService {
// FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
// volume change notification back to hdmi control service.
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
- AudioManager.FLAG_SHOW_UI |
AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
}
}
@@ -2033,4 +2030,14 @@ public final class HdmiControlService extends SystemService {
getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
HdmiControlService.PERMISSION);
}
+
+ @ServiceThreadOnly
+ void displayOsd(int messageId, int extra) {
+ assertRunOnServiceThread();
+ Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
+ intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
+ intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRAM_PARAM1, extra);
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+ HdmiControlService.PERMISSION);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index c7add75..2562ffc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -26,44 +26,83 @@ import java.util.HashMap;
/**
* A logger that prevents spammy log. For the same log message, it logs once every 20seconds.
* This class is not thread-safe.
+ * <p>
+ * For convenience, use single character prefix for all messages.
+ * Here are common acronyms
+ * <ul>
+ * <li>[T]: Timout
+ * <li>[R]: Received message
+ * <li>[S]: Sent message
+ * <li>[P]: Device polling result
+ * </ul>
*/
final class HdmiLogger {
+ private static final String TAG = "HDMI";
// Logging duration for same error message.
private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000; // 20s
private static final boolean DEBUG = false;
+ private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>();
+
// Key (String): log message.
// Value (Pair(Long, Integer)): a pair of last log time millis and the number of logMessage.
// Cache for warning.
private final HashMap<String, Pair<Long, Integer>> mWarningTimingCache = new HashMap<>();
// Cache for error.
private final HashMap<String, Pair<Long, Integer>> mErrorTimingCache = new HashMap<>();
- private final String mTag;
- HdmiLogger(String tag) {
- mTag = "HDMI:" + tag;
+ private HdmiLogger() {
+ }
+
+ static final void warning(String logMessage, Object... objs) {
+ getLogger().warningInternal(toLogString(logMessage, objs));
}
- void warning(String logMessage) {
+ private void warningInternal(String logMessage) {
String log = updateLog(mWarningTimingCache, logMessage);
if (!log.isEmpty()) {
- Slog.w(mTag, log);
+ Slog.w(TAG, log);
}
}
- void error(String logMessage) {
+ static final void error(String logMessage, Object... objs) {
+ getLogger().errorInternal(toLogString(logMessage, objs));
+ }
+
+ private void errorInternal(String logMessage) {
String log = updateLog(mErrorTimingCache, logMessage);
if (!log.isEmpty()) {
- Slog.e(mTag, log);
+ Slog.e(TAG, log);
}
}
- void debug(String logMessage) {
+ static final void debug(String logMessage, Object... objs) {
+ getLogger().debugInternal(toLogString(logMessage, objs));
+ }
+
+ private void debugInternal(String logMessage) {
if (!DEBUG) {
return;
}
- Slog.d(mTag, logMessage);
+ Slog.d(TAG, logMessage);
+ }
+
+ private static final String toLogString(String logMessage, Object[] objs) {
+ if (objs.length > 0) {
+ return String.format(logMessage, objs);
+ } else {
+ return logMessage;
+ }
+ }
+
+ private static HdmiLogger getLogger() {
+ HdmiLogger logger = sLogger.get();
+ if (logger == null) {
+ logger = new HdmiLogger();
+ sLogger.set(logger);
+ }
+ return logger;
}
private static String updateLog(HashMap<String, Pair<Long, Integer>> cache, String logMessage) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java b/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java
new file mode 100644
index 0000000..c27cf18
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 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.hdmi;
+
+import android.hardware.hdmi.HdmiPortInfo;
+import android.util.SparseArray;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * A handler class for MHL control command. It converts user's command into MHL command and pass it
+ * to MHL HAL layer.
+ * <p>
+ * It can be created only by {@link HdmiMhlControllerStub#create}.
+ */
+final class HdmiMhlControllerStub {
+
+ private static final SparseArray<HdmiMhlLocalDevice> mLocalDevices = new SparseArray<>();
+ private static final HdmiPortInfo[] EMPTY_PORT_INFO = new HdmiPortInfo[0];
+ private static final int INVALID_MHL_VERSION = 0;
+ private static final int NO_SUPPORTED_FEATURES = 0;
+ private static final int INVALID_DEVICE_ROLES = 0;
+
+ // Private constructor. Use HdmiMhlControllerStub.create().
+ private HdmiMhlControllerStub(HdmiControlService service) {
+ }
+
+ // Returns true if MHL controller is initialized and ready to use.
+ boolean isReady() {
+ return false;
+ }
+
+ static HdmiMhlControllerStub create(HdmiControlService service) {
+ return new HdmiMhlControllerStub(service);
+ }
+
+ HdmiPortInfo[] getPortInfos() {
+ return EMPTY_PORT_INFO;
+ }
+
+ /**
+ * Return {@link HdmiMhlLocalDevice} matched with the given port id.
+ *
+ * @return null if has no matched port id
+ */
+ HdmiMhlLocalDevice getLocalDevice(int portId) {
+ return null;
+ }
+
+ /**
+ * Return {@link HdmiMhlLocalDevice} matched with the given device id.
+ *
+ * @return null if has no matched id
+ */
+ HdmiMhlLocalDevice getLocalDeviceById(int deviceId) {
+ return null;
+ }
+
+ SparseArray<HdmiMhlLocalDevice> getAllLocalDevices() {
+ return mLocalDevices;
+ }
+
+ /**
+ * Remove a {@link HdmiMhlLocalDevice} matched with the given port id.
+ *
+ * @return removed {@link HdmiMhlLocalDevice}. Return null if no matched port id.
+ */
+ HdmiMhlLocalDevice removeLocalDevice(int portId) {
+ return null;
+ }
+
+ /**
+ * Add a new {@link HdmiMhlLocalDevice}.
+ *
+ * @return old {@link HdmiMhlLocalDevice} having same port id
+ */
+ HdmiMhlLocalDevice addLocalDevice(HdmiMhlLocalDevice device) {
+ return null;
+ }
+
+ void clearAllLocalDevices() {
+ }
+
+ /**
+ * Send MHL MSC-Subcommand to the device connected to the given port.
+ */
+ void sendSubcommand(int portId, HdmiMhlSubcommand command) {
+ }
+
+ void sendSubcommand(final int portId, final HdmiMhlSubcommand command,
+ SendMessageCallback callback) {
+ }
+
+
+ void sendScratchpadCommand(int portId, int offset, int length, byte[] data) {
+ }
+
+ void setOption(int flag, int value) {
+ }
+
+ /**
+ * Get the MHL version supported by underlying hardware port of the given {@code portId}.
+ * MHL specification version 2.0 returns 0x20, 3.0 will return 0x30 respectively.
+ * The return value is stored in 'version'. Return INVALID_VERSION if MHL hardware layer
+ * is not ready.
+ */
+ int getMhlVersion(int portId) {
+ return INVALID_MHL_VERSION;
+ }
+
+ /**
+ * Get MHL version of a device which is connected to a port of the given {@code portId}.
+ * MHL specification version 2.0 returns 0x20, 3.0 will return 0x30 respectively.
+ * The return value is stored in 'version'.
+ */
+ int getPeerMhlVersion(int portId) {
+ return INVALID_MHL_VERSION;
+ }
+
+ /**
+ * Get the bit flags describing the features supported by the system. Refer to feature support
+ * flag register info in MHL specification.
+ */
+ int getSupportedFeatures(int portId) {
+ return NO_SUPPORTED_FEATURES;
+ }
+
+ /**
+ * Get the bit flags describing the roles which ECBUS device can play. Refer to the
+ * ECBUS_DEV_ROLES Register info MHL3.0 specification
+ */
+ int getEcbusDeviceRoles(int portId) {
+ return INVALID_DEVICE_ROLES;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index 03fbb95..1e29fd6 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -77,7 +77,7 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
// if no device exists for incoming message, hands it over to other actions.
return false;
}
- int newStatus = cmd.getParams()[0];
+ int newStatus = cmd.getParams()[0] & 0xFF;
updatePowerStatus(sourceAddress, newStatus, true);
return true;
}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 17c2d6c..3fb450f 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -85,6 +85,7 @@ abstract class RequestArcAction extends HdmiCecFeatureAction {
if (mState != state || state != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) {
return;
}
+ HdmiLogger.debug("[T]RequestArcAction.");
disableArcTransmission();
finish();
}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index f25363d..d9e1f24 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -35,15 +35,15 @@ final class RequestArcInitiationAction extends RequestArcAction {
@Override
boolean start() {
+ mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+
HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(
getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == Constants.SEND_RESULT_SUCCESS) {
- mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
- addTimer(mState, HdmiConfig.TIMEOUT_MS);
- } else {
+ if (error != Constants.SEND_RESULT_SUCCESS) {
// If failed to send <Request ARC Initiation>, start "Disabled"
// ARC transmission action.
disableArcTransmission();
diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
index 1491c72..f5a0115 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
@@ -35,15 +35,15 @@ final class RequestArcTerminationAction extends RequestArcAction {
@Override
boolean start() {
+ mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+
HdmiCecMessage command =
HdmiCecMessageBuilder.buildRequestArcTermination(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == Constants.SEND_RESULT_SUCCESS) {
- mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
- addTimer(mState, HdmiConfig.TIMEOUT_MS);
- } else {
+ if (error != Constants.SEND_RESULT_SUCCESS) {
// If failed to send <Request ARC Termination>, start "Disabled" ARC
// transmission action.
disableArcTransmission();
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 30519f3..bffa854 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -53,6 +53,18 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction {
@Override
boolean start() {
if (mEnabled) {
+ // Enable ARC status immediately after sending <Report Arc Initiated>.
+ // If AVR responds with <Feature Abort>, disable ARC status again.
+ // This is different from spec that says that turns ARC status to
+ // "Enabled" if <Report ARC Initiated> is acknowledged and no
+ // <Feature Abort> is received.
+ // But implemented this way to save the time having to wait for
+ // <Feature Abort>.
+ setArcStatus(true);
+ // If succeeds to send <Report ARC Initiated>, wait general timeout
+ // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
+ mState = STATE_WAITING_TIMEOUT;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
sendReportArcInitiated();
} else {
setArcStatus(false);
@@ -67,23 +79,11 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction {
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == Constants.SEND_RESULT_SUCCESS) {
- // Enable ARC status immediately after sending <Report Arc Initiated>.
- // If AVR responds with <Feature Abort>, disable ARC status again.
- // This is different from spec that says that turns ARC status to
- // "Enabled" if <Report ARC Initiated> is acknowledged and no
- // <Feature Abort> is received.
- // But implemented this way to save the time having to wait for
- // <Feature Abort>.
- setArcStatus(true);
- // If succeeds to send <Report ARC Initiated>, wait general timeout
- // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
- mState = STATE_WAITING_TIMEOUT;
- addTimer(mState, HdmiConfig.TIMEOUT_MS);
- } else {
+ if (error != Constants.SEND_RESULT_SUCCESS) {
// If fails to send <Report ARC Initiated>, disable ARC and
// send <Report ARC Terminated> directly.
setArcStatus(false);
+ HdmiLogger.debug("Failed to send <Report Arc Initiated>.");
finish();
}
}
@@ -112,6 +112,7 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction {
if (opcode == Constants.MESSAGE_FEATURE_ABORT) {
int originalOpcode = cmd.getParams()[0] & 0xFF;
if (originalOpcode == Constants.MESSAGE_REPORT_ARC_INITIATED) {
+ HdmiLogger.debug("Feature aborted for <Report Arc Initiated>");
setArcStatus(false);
finish();
return true;
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index d15ffb0..0871194 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -98,7 +98,7 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
@Override
public void onSendCompleted(int error) {
if (error != Constants.SEND_RESULT_SUCCESS) {
- DLOGGER.debug("Failed to send <System Audio Mode Request>:" + error);
+ HdmiLogger.debug("Failed to send <System Audio Mode Request>:" + error);
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
}
@@ -111,7 +111,7 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
private void handleSendSystemAudioModeRequestTimeout() {
if (!mTargetAudioStatus // Don't retry for Off case.
|| mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
- DLOGGER.debug("[T]:wait for <Set System Audio Mode>.");
+ HdmiLogger.debug("[T]:wait for <Set System Audio Mode>.");
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
return;
@@ -130,7 +130,7 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
&& (cmd.getParams()[0] & 0xFF)
== Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
- DLOGGER.debug("Failed to start system audio mode request.");
+ HdmiLogger.debug("Failed to start system audio mode request.");
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
return true;
@@ -145,7 +145,7 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
startAudioStatusAction();
return true;
} else {
- DLOGGER.debug("Unexpected system audio mode request:" + receivedStatus);
+ HdmiLogger.debug("Unexpected system audio mode request:" + receivedStatus);
// Unexpected response, consider the request is newly initiated by AVR.
// To return 'false' will initiate new SystemAudioActionFromAvr by the control
// service.
diff --git a/services/core/java/com/android/server/hdmi/VolumeControlAction.java b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
index ddc267a..4338fc7 100644
--- a/services/core/java/com/android/server/hdmi/VolumeControlAction.java
+++ b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
@@ -17,66 +17,35 @@
package com.android.server.hdmi;
import static com.android.server.hdmi.Constants.IRT_MS;
+import static com.android.server.hdmi.Constants.MESSAGE_FEATURE_ABORT;
+import static com.android.server.hdmi.Constants.MESSAGE_REPORT_AUDIO_STATUS;
+import static com.android.server.hdmi.Constants.MESSAGE_USER_CONTROL_PRESSED;
-import com.android.internal.util.Preconditions;
+import android.media.AudioManager;
/**
* Feature action that transmits volume change to Audio Receiver.
* <p>
- * This action is created when a user pressed volume up/down. However, Since Android only provides a
- * listener for delta of some volume change, we will set a target volume, and check reported volume
- * from Audio Receiver(AVR). If TV receives no &lt;Report Audio Status&gt; from AVR, this action
- * will be finished in {@link #IRT_MS} * {@link #VOLUME_CHANGE_TIMEOUT_MAX_COUNT} (ms).
+ * This action is created when a user pressed volume up/down. However, Android only provides a
+ * listener for delta of some volume change instead of individual key event. Also it's hard to know
+ * Audio Receiver's number of volume steps for a single volume control key. Because of this, it
+ * sends key-down event until IRT timeout happens, and it will send key-up event if no additional
+ * volume change happens; otherwise, it will send again key-down as press and hold feature does.
*/
final class VolumeControlAction extends HdmiCecFeatureAction {
private static final String TAG = "VolumeControlAction";
- private static final int VOLUME_MUTE = 101;
- private static final int VOLUME_RESTORE = 102;
+ // State that wait for next volume press.
+ private static final int STATE_WAIT_FOR_NEXT_VOLUME_PRESS = 1;
private static final int MAX_VOLUME = 100;
- private static final int MIN_VOLUME = 0;
- // State where to wait for <Report Audio Status>
- private static final int STATE_WAIT_FOR_REPORT_VOLUME_STATUS = 1;
-
- // Maximum count of time out used to finish volume action.
- private static final int VOLUME_CHANGE_TIMEOUT_MAX_COUNT = 2;
+ private static final int UNKNOWN_AVR_VOLUME = -1;
private final int mAvrAddress;
- private final int mTargetVolume;
- private final boolean mIsVolumeUp;
- private int mTimeoutCount;
-
- /**
- * Create a {@link VolumeControlAction} for mute/restore change
- *
- * @param source source device sending volume change
- * @param avrAddress address of audio receiver
- * @param mute whether to mute sound or not. {@code true} for mute on; {@code false} for mute
- * off, i.e restore volume
- * @return newly created {@link VolumeControlAction}
- */
- public static VolumeControlAction ofMute(HdmiCecLocalDevice source, int avrAddress,
- boolean mute) {
- return new VolumeControlAction(source, avrAddress, mute ? VOLUME_MUTE : VOLUME_RESTORE,
- false);
- }
-
- /**
- * Create a {@link VolumeControlAction} for volume up/down change
- *
- * @param source source device sending volume change
- * @param avrAddress address of audio receiver
- * @param targetVolume target volume to be set to AVR. It should be in range of [0-100]
- * @param isVolumeUp whether to volume up or not. {@code true} for volume up; {@code false} for
- * volume down
- * @return newly created {@link VolumeControlAction}
- */
- public static VolumeControlAction ofVolumeChange(HdmiCecLocalDevice source, int avrAddress,
- int targetVolume, boolean isVolumeUp) {
- Preconditions.checkArgumentInRange(targetVolume, MIN_VOLUME, MAX_VOLUME, "volume");
- return new VolumeControlAction(source, avrAddress, targetVolume, isVolumeUp);
- }
+ private boolean mIsVolumeUp;
+ private long mLastKeyUpdateTime;
+ private int mLastAvrVolume;
+ private boolean mSentKeyPressed;
/**
* Scale a custom volume value to cec volume scale.
@@ -94,123 +63,141 @@ final class VolumeControlAction extends HdmiCecFeatureAction {
*
* @param cecVolume volume value in cec volume scale. It should be in a range of [0-100]
* @param scale scale of custom volume (max volume)
- * @return a volume value scaled to custom volume range
+ * @return a volume scaled to custom volume range
*/
public static int scaleToCustomVolume(int cecVolume, int scale) {
return (cecVolume * scale) / MAX_VOLUME;
}
- private VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, int targetVolume,
- boolean isVolumeUp) {
+ VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, boolean isVolumeUp) {
super(source);
-
mAvrAddress = avrAddress;
- mTargetVolume = targetVolume;
mIsVolumeUp = isVolumeUp;
+ mLastAvrVolume = UNKNOWN_AVR_VOLUME;
+ mSentKeyPressed = false;
+
+ updateLastKeyUpdateTime();
+ }
+
+ private void updateLastKeyUpdateTime() {
+ mLastKeyUpdateTime = System.currentTimeMillis();
}
@Override
boolean start() {
- if (isForMute()) {
- sendMuteChange(mTargetVolume == VOLUME_MUTE);
- finish();
- return true;
- }
-
- startVolumeChange();
+ mState = STATE_WAIT_FOR_NEXT_VOLUME_PRESS;
+ sendVolumeKeyPressed();
+ resetTimer();
return true;
}
-
- private boolean isForMute() {
- return mTargetVolume == VOLUME_MUTE || mTargetVolume == VOLUME_RESTORE;
+ private void sendVolumeKeyPressed() {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mAvrAddress,
+ mIsVolumeUp ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
+ : HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN));
+ mSentKeyPressed = true;
}
- private void startVolumeChange() {
- mTimeoutCount = 0;
- sendVolumeChange(mIsVolumeUp);
- mState = STATE_WAIT_FOR_REPORT_VOLUME_STATUS;
- addTimer(mState, IRT_MS);
+ private void resetTimer() {
+ mActionTimer.clearTimerMessage();
+ addTimer(STATE_WAIT_FOR_NEXT_VOLUME_PRESS, IRT_MS);
}
- private void sendVolumeChange(boolean up) {
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mAvrAddress,
- up ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
- : HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN));
+ void handleVolumeChange(boolean isVolumeUp) {
+ if (mIsVolumeUp != isVolumeUp) {
+ HdmiLogger.debug("Volume Key Status Changed[old:%b new:%b]", mIsVolumeUp, isVolumeUp);
+ sendVolumeKeyReleased();
+ mIsVolumeUp = isVolumeUp;
+ }
+ updateLastKeyUpdateTime();
}
- private void sendMuteChange(boolean mute) {
- sendUserControlPressedAndReleased(mAvrAddress,
- mute ? HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION :
- HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
+ private void sendVolumeKeyReleased() {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+ getSourceAddress(), mAvrAddress));
+ mSentKeyPressed = false;
}
@Override
boolean processCommand(HdmiCecMessage cmd) {
- if (mState != STATE_WAIT_FOR_REPORT_VOLUME_STATUS) {
+ if (mState != STATE_WAIT_FOR_NEXT_VOLUME_PRESS || cmd.getSource() != mAvrAddress) {
return false;
}
switch (cmd.getOpcode()) {
- case Constants.MESSAGE_REPORT_AUDIO_STATUS:
- handleReportAudioStatus(cmd);
- return true;
- case Constants.MESSAGE_FEATURE_ABORT:
- int originalOpcode = cmd.getParams()[0] & 0xFF;
- if (originalOpcode == Constants.MESSAGE_USER_CONTROL_PRESSED
- || originalOpcode == Constants.MESSAGE_USER_CONTROL_RELEASED) {
- // TODO: handle feature abort.
- finish();
- return true;
- }
- default: // fall through
+ case MESSAGE_REPORT_AUDIO_STATUS:
+ return handleReportAudioStatus(cmd);
+ case MESSAGE_FEATURE_ABORT:
+ return handleFeatureAbort(cmd);
+ default:
return false;
}
}
- private void handleReportAudioStatus(HdmiCecMessage cmd) {
- byte[] params = cmd.getParams();
+ private boolean handleReportAudioStatus(HdmiCecMessage cmd) {
+ byte params[] = cmd.getParams();
+ boolean mute = (params[0] & 0x80) == 0x80;
int volume = params[0] & 0x7F;
- // Update volume with new value.
- // Note that it will affect system volume change.
- tv().setAudioStatus(false, volume);
+ mLastAvrVolume = volume;
+ if (shouldUpdateAudioVolume(mute)) {
+ HdmiLogger.debug("Force volume change[mute:%b, volume=%d]", mute, volume);
+ tv().setAudioStatus(mute, volume);
+ }
+ return true;
+ }
+
+ private boolean shouldUpdateAudioVolume(boolean mute) {
+ // Do nothing if in mute.
+ if (mute) {
+ return true;
+ }
+
+ // Update audio status if current volume position is edge of volume bar,
+ // i.e max or min volume.
+ AudioManager audioManager = tv().getService().getAudioManager();
+ int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
if (mIsVolumeUp) {
- if (mTargetVolume <= volume) {
- finishWithVolumeChangeRelease();
- return;
- }
+ int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ return currentVolume == maxVolume;
} else {
- if (mTargetVolume >= volume) {
- finishWithVolumeChangeRelease();
- return;
- }
+ return currentVolume == 0;
}
+ }
- // Clear action status and send another volume change command.
- clear();
- startVolumeChange();
+ private boolean handleFeatureAbort(HdmiCecMessage cmd) {
+ int originalOpcode = cmd.getParams()[0] & 0xFF;
+ // Since it sends <User Control Released> only when it finishes this action,
+ // it takes care of <User Control Pressed> only here.
+ if (originalOpcode == MESSAGE_USER_CONTROL_PRESSED) {
+ finish();
+ return true;
+ }
+ return false;
}
- private void finishWithVolumeChangeRelease() {
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
- getSourceAddress(), mAvrAddress));
- finish();
+ @Override
+ protected void clear() {
+ super.clear();
+ if (mSentKeyPressed) {
+ sendVolumeKeyReleased();
+ }
+ if (mLastAvrVolume != UNKNOWN_AVR_VOLUME) {
+ tv().setAudioStatus(false, mLastAvrVolume);
+ mLastAvrVolume = UNKNOWN_AVR_VOLUME;
+ }
}
@Override
void handleTimerEvent(int state) {
- if (mState != STATE_WAIT_FOR_REPORT_VOLUME_STATUS) {
+ if (state != STATE_WAIT_FOR_NEXT_VOLUME_PRESS) {
return;
}
- // If no report volume action after IRT * VOLUME_CHANGE_TIMEOUT_MAX_COUNT just stop volume
- // action.
- if (++mTimeoutCount == VOLUME_CHANGE_TIMEOUT_MAX_COUNT) {
- finishWithVolumeChangeRelease();
- return;
+ if (System.currentTimeMillis() - mLastKeyUpdateTime >= IRT_MS) {
+ finish();
+ } else {
+ sendVolumeKeyPressed();
+ resetTimer();
}
-
- sendVolumeChange(mIsVolumeUp);
- addTimer(mState, IRT_MS);
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 93dceff..9c567ac 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -241,6 +241,9 @@ public class InputManagerService extends IInputManager.Stub
/** Switch code: Headphone/Microphone Jack. When set, something is inserted. */
public static final int SW_JACK_PHYSICAL_INSERT = 0x07;
+ /** Switch code: Camera lens cover. When set the lens is covered. */
+ public static final int SW_CAMERA_LENS_COVER = 0x09;
+
public static final int SW_LID_BIT = 1 << SW_LID;
public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT;
@@ -249,6 +252,7 @@ public class InputManagerService extends IInputManager.Stub
public static final int SW_JACK_PHYSICAL_INSERT_BIT = 1 << SW_JACK_PHYSICAL_INSERT;
public static final int SW_JACK_BITS =
SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT;
+ public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
final boolean mUseDevInputEventForAudioJack;
@@ -693,7 +697,9 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mDataStore) {
for (int i = 0; i < numFullKeyboards; i++) {
final InputDevice inputDevice = mTempFullKeyboards.get(i);
- if (mDataStore.getCurrentKeyboardLayout(inputDevice.getDescriptor()) == null) {
+ final String layout =
+ getCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier());
+ if (layout == null) {
missingLayoutForExternalKeyboard = true;
if (i < numFullKeyboardsAdded) {
missingLayoutForExternalKeyboardAdded = true;
@@ -1380,6 +1386,11 @@ public class InputManagerService extends IInputManager.Stub
mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
}
+ if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
+ final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) == 0);
+ mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
+ }
+
if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
switchMask);
@@ -1420,8 +1431,8 @@ public class InputManagerService extends IInputManager.Stub
}
// Native callback.
- private int interceptWakeMotionBeforeQueueing(long whenNanos, int policyFlags) {
- return mWindowManagerCallbacks.interceptWakeMotionBeforeQueueing(
+ private int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
+ return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive(
whenNanos, policyFlags);
}
@@ -1575,6 +1586,8 @@ public class InputManagerService extends IInputManager.Stub
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
+ public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
+
public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
public long notifyANR(InputApplicationHandle inputApplicationHandle,
@@ -1582,7 +1595,7 @@ public class InputManagerService extends IInputManager.Stub
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
- public int interceptWakeMotionBeforeQueueing(long whenNanos, int policyFlags);
+ public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
public long interceptKeyBeforeDispatching(InputWindowHandle focus,
KeyEvent event, int policyFlags);
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 2ef9828..11818d8 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -331,6 +331,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private int mSuplServerPort;
private String mC2KServerHost;
private int mC2KServerPort;
+ private boolean mSuplEsEnabled = false;
private final Context mContext;
private final NtpTrustedTime mNtpTime;
@@ -453,11 +454,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
+ if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
if (action.equals(ALARM_WAKEUP)) {
- if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
startNavigating(false);
} else if (action.equals(ALARM_TIMEOUT)) {
- if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
hibernate();
} else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
checkSmsSuplInit(intent);
@@ -494,6 +494,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
Log.d(TAG, "SIM STATE is ready, SIM MCC/MNC is " + mccMnc);
synchronized (mLock) {
reloadGpsProperties(context, mProperties);
+ mNIHandler.setSuplEsEnablement(mSuplEsEnabled);
}
}
}
@@ -571,6 +572,16 @@ public class GpsLocationProvider implements LocationProviderInterface {
} catch (IOException ex) {
Log.w(TAG, "failed to dump properties contents");
}
+
+ // SUPL_ES configuration.
+ String suplESProperty = mProperties.getProperty("SUPL_ES");
+ if (suplESProperty != null) {
+ try {
+ mSuplEsEnabled = (Integer.parseInt(suplESProperty) == 1);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
+ }
+ }
}
private void loadPropertiesFromResource(Context context,
@@ -612,7 +623,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
mContext = context;
mNtpTime = NtpTrustedTime.getInstance(context);
mILocationManager = ilocationManager;
- mNIHandler = new GpsNetInitiatedHandler(context);
mLocation.setExtras(mLocationExtras);
@@ -639,6 +649,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
mProperties = new Properties();
reloadGpsProperties(mContext, mProperties);
+ // Create a GPS net-initiated handler.
+ mNIHandler = new GpsNetInitiatedHandler(context,
+ mNetInitiatedListener,
+ mSuplEsEnabled);
+
// construct handler, listen for events
mHandler = new ProviderHandler(looper);
listenForBroadcasts();
@@ -691,7 +706,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter = new IntentFilter();
intentFilter.addAction(SIM_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index b936130d..5097927 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -229,8 +229,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
* {@link AudioManager#ADJUST_SAME}.
*
* @param direction The direction to adjust volume in.
+ * @param flags Any of the flags from {@link AudioManager}.
+ * @param packageName The package that made the original volume request.
+ * @param uid The uid that made the original volume request.
+ * @param useSuggested True to use adjustSuggestedStreamVolume instead of
+ * adjustStreamVolume.
*/
- public void adjustVolume(int direction, int flags, String packageName, int uid) {
+ public void adjustVolume(int direction, int flags, String packageName, int uid,
+ boolean useSuggested) {
if (isPlaybackActive(false) || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
@@ -241,8 +247,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, packageName,
- uid);
+ if (useSuggested) {
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction, flags,
+ packageName, uid);
+ } else {
+ mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
+ packageName, uid);
+ }
} else {
if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
@@ -1020,7 +1031,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid);
+ MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid, false);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0c6d46c..a12dd1c 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -814,7 +814,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
} else {
session.adjustVolume(direction, flags, getContext().getPackageName(),
- UserHandle.myUserId());
+ UserHandle.myUserId(), true);
if (session.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE
&& mRvc != null) {
try {
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 189131c..05ad1fe 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -545,8 +545,9 @@ public class ConditionProviders extends ManagedServices {
setZenModeCondition(condition, "downtime");
}
// exit downtime
- if (!inDowntime && mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && mDowntime.isDowntimeCondition(mExitCondition)) {
+ if (!inDowntime && mDowntime.isDowntimeCondition(mExitCondition)
+ && (mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ || mode == Global.ZEN_MODE_NO_INTERRUPTIONS)) {
mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
}
}
diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
index 317ebef..b71bad8 100644
--- a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
+++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
@@ -43,6 +43,7 @@ import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;
+import java.util.TimeZone;
/** Built-in zen condition provider for managing downtime */
public class DowntimeConditionProvider extends ConditionProviderService {
@@ -275,6 +276,9 @@ public class DowntimeConditionProvider extends ConditionProviderService {
final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
if (DEBUG) Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
action, ts(schTime), ts(now), now - schTime));
+ } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+ if (DEBUG) Slog.d(TAG, "timezone changed to " + TimeZone.getDefault());
+ mCalendar.setTimeZone(TimeZone.getDefault());
} else {
if (DEBUG) Slog.d(TAG, action + " fired at " + now);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d0f4054..c09eff8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,6 +49,7 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioAttributes;
import android.media.AudioManager;
+import android.media.AudioSystem;
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
@@ -69,6 +70,7 @@ import android.service.notification.Condition;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
+import android.service.notification.IStatusBarNotificationHolder;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
@@ -1126,6 +1128,19 @@ public class NotificationManagerService extends SystemService {
return mRankingHelper.getPackagePriority(pkg, uid);
}
+ @Override
+ public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
+ checkCallerIsSystem();
+ mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
+ savePolicyFile();
+ }
+
+ @Override
+ public int getPackageVisibilityOverride(String pkg, int uid) {
+ checkCallerIsSystem();
+ return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
+ }
+
/**
* System-only API for getting a list of current (i.e. not cleared) notifications.
*
@@ -1457,7 +1472,14 @@ public class NotificationManagerService extends SystemService {
@Override
public boolean matchesCallFilter(Bundle extras) {
enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
- return mZenModeHelper.matchesCallFilter(extras,
+ return matchesCallFilterAsUser(extras, Binder.getCallingUid());
+ }
+
+ @Override
+ public boolean matchesCallFilterAsUser(Bundle extras, int userId) {
+ enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
+ UserHandle userHandle = new UserHandle(userId);
+ return mZenModeHelper.matchesCallFilter(userHandle, extras,
mRankingHelper.findExtractor(ValidateNotificationPeople.class));
}
};
@@ -1924,14 +1946,19 @@ public class NotificationManagerService extends SystemService {
}
private static AudioAttributes audioAttributesForNotification(Notification n) {
- if (n.audioAttributes != null
- && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
+ if (n.audioAttributes != null) {
return n.audioAttributes;
+ } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
+ // the stream type is valid, use it
+ return new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(n.audioStreamType)
+ .build();
+ } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
+ return Notification.AUDIO_ATTRIBUTES_DEFAULT;
+ } else {
+ Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
+ return Notification.AUDIO_ATTRIBUTES_DEFAULT;
}
- return new AudioAttributes.Builder()
- .setLegacyStreamType(n.audioStreamType)
- .setUsage(AudioAttributes.usageForLegacyStreamType(n.audioStreamType))
- .build();
}
void showNextToastLocked() {
@@ -2045,12 +2072,15 @@ public class NotificationManagerService extends SystemService {
}
int indexBefore = findNotificationRecordIndexLocked(record);
boolean interceptBefore = record.isIntercepted();
+ int visibilityBefore = record.getPackageVisibilityOverride();
recon.applyChangesLocked(record);
applyZenModeLocked(record);
mRankingHelper.sort(mNotificationList);
int indexAfter = findNotificationRecordIndexLocked(record);
boolean interceptAfter = record.isIntercepted();
- changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
+ int visibilityAfter = record.getPackageVisibilityOverride();
+ changed = indexBefore != indexAfter || interceptBefore != interceptAfter
+ || visibilityBefore != visibilityAfter;
if (interceptBefore && !interceptAfter) {
buzzBeepBlinkLocked(record);
}
@@ -2064,14 +2094,18 @@ public class NotificationManagerService extends SystemService {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
ArrayList<String> orderBefore = new ArrayList<String>(N);
+ int[] visibilities = new int[N];
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
+ visibilities[i] = r.getPackageVisibilityOverride();
mRankingHelper.extractSignals(r);
}
- mRankingHelper.sort(mNotificationList);
for (int i = 0; i < N; i++) {
- if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
+ mRankingHelper.sort(mNotificationList);
+ final NotificationRecord r = mNotificationList.get(i);
+ if (!orderBefore.get(i).equals(r.getKey())
+ || visibilities[i] != r.getPackageVisibilityOverride()) {
scheduleSendRankingUpdate();
return;
}
@@ -2601,6 +2635,7 @@ public class NotificationManagerService extends SystemService {
final int N = mNotificationList.size();
ArrayList<String> keys = new ArrayList<String>(N);
ArrayList<String> interceptedKeys = new ArrayList<String>(N);
+ Bundle visibilityOverrides = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
@@ -2610,14 +2645,28 @@ public class NotificationManagerService extends SystemService {
if (record.isIntercepted()) {
interceptedKeys.add(record.sbn.getKey());
}
+ if (record.getPackageVisibilityOverride()
+ != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
+ visibilityOverrides.putInt(record.sbn.getKey(),
+ record.getPackageVisibilityOverride());
+ }
+ // Find first min-prio notification for speedbump placement.
if (speedBumpIndex == -1 &&
+ // Intrusiveness trumps priority, hence ignore intrusives.
+ !record.isRecentlyIntrusive() &&
+ // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
+ // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
+ // (or lower as a safeguard) is sufficient to find the speedbump index.
+ // We'll have to revisit this when more package priority buckets are introduced.
+ record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
speedBumpIndex = keys.size() - 1;
}
}
String[] keysAr = keys.toArray(new String[keys.size()]);
String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
- return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex);
+ return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
+ speedBumpIndex);
}
private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
@@ -2811,8 +2860,9 @@ public class NotificationManagerService extends SystemService {
private void notifyPosted(final ManagedServiceInfo info,
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
final INotificationListener listener = (INotificationListener)info.service;
+ StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
- listener.onNotificationPosted(sbn, rankingUpdate);
+ listener.onNotificationPosted(sbnHolder, rankingUpdate);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
}
@@ -2824,8 +2874,9 @@ public class NotificationManagerService extends SystemService {
return;
}
final INotificationListener listener = (INotificationListener) info.service;
+ StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
- listener.onNotificationRemoved(sbn, rankingUpdate);
+ listener.onNotificationRemoved(sbnHolder, rankingUpdate);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
}
@@ -2913,4 +2964,25 @@ public class NotificationManagerService extends SystemService {
return zen ? "zen" : ('\'' + pkgFilter + '\'');
}
}
+
+ /**
+ * Wrapper for a StatusBarNotification object that allows transfer across a oneway
+ * binder without sending large amounts of data over a oneway transaction.
+ */
+ private static final class StatusBarNotificationHolder
+ extends IStatusBarNotificationHolder.Stub {
+ private StatusBarNotification mValue;
+
+ public StatusBarNotificationHolder(StatusBarNotification value) {
+ mValue = value;
+ }
+
+ /** Get the held value and clear it. This function should only be called once per holder */
+ @Override
+ public StatusBarNotification get() {
+ StatusBarNotification value = mValue;
+ mValue = null;
+ return value;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f2bd676..fd34aa5 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.media.AudioAttributes;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import com.android.internal.annotations.VisibleForTesting;
@@ -65,6 +66,7 @@ public final class NotificationRecord {
private int mAuthoritativeRank;
private String mGlobalSortKey;
+ private int mPackageVisibility;
@VisibleForTesting
public NotificationRecord(StatusBarNotification sbn, int score)
@@ -79,6 +81,7 @@ public final class NotificationRecord {
mContactAffinity = previous.mContactAffinity;
mRecentlyIntrusive = previous.mRecentlyIntrusive;
mPackagePriority = previous.mPackagePriority;
+ mPackageVisibility = previous.mPackageVisibility;
mIntercept = previous.mIntercept;
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
// Don't copy mGlobalSortKey, recompute it.
@@ -86,8 +89,10 @@ public final class NotificationRecord {
public Notification getNotification() { return sbn.getNotification(); }
public int getFlags() { return sbn.getNotification().flags; }
- public int getUserId() { return sbn.getUserId(); }
+ public UserHandle getUser() { return sbn.getUser(); }
public String getKey() { return sbn.getKey(); }
+ /** @deprecated Use {@link #getUser()} instead. */
+ public int getUserId() { return sbn.getUserId(); }
void dump(PrintWriter pw, String prefix, Context baseContext) {
final Notification notification = sbn.getNotification();
@@ -155,6 +160,7 @@ public final class NotificationRecord {
pw.println(prefix + " mContactAffinity=" + mContactAffinity);
pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive);
pw.println(prefix + " mPackagePriority=" + mPackagePriority);
+ pw.println(prefix + " mPackageVisibility=" + mPackageVisibility);
pw.println(prefix + " mIntercept=" + mIntercept);
pw.println(prefix + " mGlobalSortKey=" + mGlobalSortKey);
pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs);
@@ -216,6 +222,14 @@ public final class NotificationRecord {
return mPackagePriority;
}
+ public void setPackageVisibilityOverride(int packageVisibility) {
+ mPackageVisibility = packageVisibility;
+ }
+
+ public int getPackageVisibilityOverride() {
+ return mPackageVisibility;
+ }
+
public boolean setIntercepted(boolean intercept) {
mIntercept = intercept;
return mIntercept;
diff --git a/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java b/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java
new file mode 100644
index 0000000..f720f9f
--- /dev/null
+++ b/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java
@@ -0,0 +1,53 @@
+/*
+* Copyright (C) 2014 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.notification;
+
+import android.content.Context;
+import android.util.Slog;
+
+public class PackageVisibilityExtractor implements NotificationSignalExtractor {
+ private static final String TAG = "PackageVisibilityExtractor";
+ private static final boolean DBG = false;
+
+ private RankingConfig mConfig;
+
+ public void initialize(Context ctx) {
+ if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ }
+
+ public RankingReconsideration process(NotificationRecord record) {
+ if (record == null || record.getNotification() == null) {
+ if (DBG) Slog.d(TAG, "skipping empty notification");
+ return null;
+ }
+
+ if (mConfig == null) {
+ if (DBG) Slog.d(TAG, "missing config");
+ return null;
+ }
+
+ final int packageVisibility = mConfig.getPackageVisibilityOverride(
+ record.sbn.getPackageName(), record.sbn.getUid());
+ record.setPackageVisibilityOverride(packageVisibility);
+
+ return null;
+ }
+
+ @Override
+ public void setConfig(RankingConfig config) {
+ mConfig = config;
+ }
+}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 7d0bd59..aea137b 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -19,4 +19,8 @@ public interface RankingConfig {
int getPackagePriority(String packageName, int uid);
void setPackagePriority(String packageName, int uid, int priority);
+
+ int getPackageVisibilityOverride(String packageName, int uid);
+
+ void setPackageVisibilityOverride(String packageName, int uid, int visibility);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 435177b..aeddecb 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -20,8 +20,10 @@ import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -33,7 +35,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
public class RankingHelper implements RankingConfig {
@@ -49,6 +51,7 @@ public class RankingHelper implements RankingConfig {
private static final String ATT_NAME = "name";
private static final String ATT_UID = "uid";
private static final String ATT_PRIORITY = "priority";
+ private static final String ATT_VISIBILITY = "visibility";
private final NotificationSignalExtractor[] mSignalExtractors;
private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
@@ -56,6 +59,7 @@ public class RankingHelper implements RankingConfig {
// Package name to uid, to priority. Would be better as Table<String, Int, Int>
private final ArrayMap<String, SparseIntArray> mPackagePriorities;
+ private final ArrayMap<String, SparseIntArray> mPackageVisibilities;
private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp;
private final Context mContext;
@@ -65,6 +69,7 @@ public class RankingHelper implements RankingConfig {
mContext = context;
mRankingHandler = rankingHandler;
mPackagePriorities = new ArrayMap<String, SparseIntArray>();
+ mPackageVisibilities = new ArrayMap<String, SparseIntArray>();
final int N = extractorNames.length;
mSignalExtractors = new NotificationSignalExtractor[N];
@@ -132,15 +137,27 @@ public class RankingHelper implements RankingConfig {
if (TAG_PACKAGE.equals(tag)) {
int uid = safeInt(parser, ATT_UID, UserHandle.USER_ALL);
int priority = safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT);
+ int vis = safeInt(parser, ATT_VISIBILITY,
+ NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
String name = parser.getAttributeValue(null, ATT_NAME);
- if (!TextUtils.isEmpty(name) && priority != Notification.PRIORITY_DEFAULT) {
- SparseIntArray priorityByUid = mPackagePriorities.get(name);
- if (priorityByUid == null) {
- priorityByUid = new SparseIntArray();
- mPackagePriorities.put(name, priorityByUid);
+ if (!TextUtils.isEmpty(name)) {
+ if (priority != Notification.PRIORITY_DEFAULT) {
+ SparseIntArray priorityByUid = mPackagePriorities.get(name);
+ if (priorityByUid == null) {
+ priorityByUid = new SparseIntArray();
+ mPackagePriorities.put(name, priorityByUid);
+ }
+ priorityByUid.put(uid, priority);
+ }
+ if (vis != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
+ SparseIntArray visibilityByUid = mPackageVisibilities.get(name);
+ if (visibilityByUid == null) {
+ visibilityByUid = new SparseIntArray();
+ mPackageVisibilities.put(name, visibilityByUid);
+ }
+ visibilityByUid.put(uid, vis);
}
- priorityByUid.put(uid, priority);
}
}
}
@@ -152,18 +169,43 @@ public class RankingHelper implements RankingConfig {
out.startTag(null, TAG_RANKING);
out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
- final int N = mPackagePriorities.size();
- for (int i = 0; i < N; i ++) {
- String name = mPackagePriorities.keyAt(i);
- SparseIntArray priorityByUid = mPackagePriorities.get(name);
- final int M = priorityByUid.size();
- for (int j = 0; j < M; j++) {
- int uid = priorityByUid.keyAt(j);
- int priority = priorityByUid.get(uid);
+ final Set<String> packageNames = new ArraySet<>(mPackagePriorities.size()
+ + mPackageVisibilities.size());
+ packageNames.addAll(mPackagePriorities.keySet());
+ packageNames.addAll(mPackageVisibilities.keySet());
+ final Set<Integer> packageUids = new ArraySet<>();
+ for (String packageName : packageNames) {
+ packageUids.clear();
+ SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
+ SparseIntArray visibilityByUid = mPackageVisibilities.get(packageName);
+ if (priorityByUid != null) {
+ final int M = priorityByUid.size();
+ for (int j = 0; j < M; j++) {
+ packageUids.add(priorityByUid.keyAt(j));
+ }
+ }
+ if (visibilityByUid != null) {
+ final int M = visibilityByUid.size();
+ for (int j = 0; j < M; j++) {
+ packageUids.add(visibilityByUid.keyAt(j));
+ }
+ }
+ for (Integer uid : packageUids) {
out.startTag(null, TAG_PACKAGE);
- out.attribute(null, ATT_NAME, name);
+ out.attribute(null, ATT_NAME, packageName);
+ if (priorityByUid != null) {
+ final int priority = priorityByUid.get(uid);
+ if (priority != Notification.PRIORITY_DEFAULT) {
+ out.attribute(null, ATT_PRIORITY, Integer.toString(priority));
+ }
+ }
+ if (visibilityByUid != null) {
+ final int visibility = visibilityByUid.get(uid);
+ if (visibility != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
+ out.attribute(null, ATT_VISIBILITY, Integer.toString(visibility));
+ }
+ }
out.attribute(null, ATT_UID, Integer.toString(uid));
- out.attribute(null, ATT_PRIORITY, Integer.toString(priority));
out.endTag(null, TAG_PACKAGE);
}
}
@@ -293,6 +335,31 @@ public class RankingHelper implements RankingConfig {
updateConfig();
}
+ @Override
+ public int getPackageVisibilityOverride(String packageName, int uid) {
+ int visibility = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
+ SparseIntArray visibilityByUid = mPackageVisibilities.get(packageName);
+ if (visibilityByUid != null) {
+ visibility = visibilityByUid.get(uid,
+ NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
+ }
+ return visibility;
+ }
+
+ @Override
+ public void setPackageVisibilityOverride(String packageName, int uid, int visibility) {
+ if (visibility == getPackageVisibilityOverride(packageName, uid)) {
+ return;
+ }
+ SparseIntArray visibilityByUid = mPackageVisibilities.get(packageName);
+ if (visibilityByUid == null) {
+ visibilityByUid = new SparseIntArray();
+ mPackageVisibilities.put(packageName, visibilityByUid);
+ }
+ visibilityByUid.put(uid, visibility);
+ updateConfig();
+ }
+
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
if (filter == null) {
final int N = mSignalExtractors.length;
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index aa47858..f266916 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -18,18 +18,23 @@ package com.android.server.notification;
import android.app.Notification;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
import android.util.LruCache;
import android.util.Slog;
import java.util.ArrayList;
import java.util.LinkedList;
+import java.util.Map;
/**
* This {@link NotificationSignalExtractor} attempts to validate
@@ -65,21 +70,88 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
static final float STARRED_CONTACT = 1f;
protected boolean mEnabled;
- private Context mContext;
+ private Context mBaseContext;
// maps raw person handle to resolved person object
private LruCache<String, LookupResult> mPeopleCache;
+ private Map<Integer, Context> mUserToContextMap;
- private RankingReconsideration validatePeople(final NotificationRecord record) {
+ public void initialize(Context context) {
+ if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ mUserToContextMap = new ArrayMap<>();
+ mBaseContext = context;
+ mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
+ mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
+ mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
+ }
+
+ public RankingReconsideration process(NotificationRecord record) {
+ if (!mEnabled) {
+ if (INFO) Slog.i(TAG, "disabled");
+ return null;
+ }
+ if (record == null || record.getNotification() == null) {
+ if (INFO) Slog.i(TAG, "skipping empty notification");
+ return null;
+ }
+ if (record.getUserId() == UserHandle.USER_ALL) {
+ if (INFO) Slog.i(TAG, "skipping global notification");
+ return null;
+ }
+ Context context = getContextAsUser(record.getUser());
+ if (context == null) {
+ if (INFO) Slog.i(TAG, "skipping notification that lacks a context");
+ return null;
+ }
+ return validatePeople(context, record);
+ }
+
+ @Override
+ public void setConfig(RankingConfig config) {
+ // ignore: config has no relevant information yet.
+ }
+
+ public float getContactAffinity(UserHandle userHandle, Bundle extras) {
+ if (extras == null) return NONE;
+ final String key = Long.toString(System.nanoTime());
+ final float[] affinityOut = new float[1];
+ Context context = getContextAsUser(userHandle);
+ if (context == null) {
+ return NONE;
+ }
+ final PeopleRankingReconsideration prr = validatePeople(context, key, extras, affinityOut);
+ float affinity = affinityOut[0];
+ if (prr != null) {
+ prr.work();
+ affinity = Math.max(prr.getContactAffinity(), affinity);
+ }
+ return affinity;
+ }
+
+ private Context getContextAsUser(UserHandle userHandle) {
+ Context context = mUserToContextMap.get(userHandle.getIdentifier());
+ if (context == null) {
+ try {
+ context = mBaseContext.createPackageContextAsUser("android", 0, userHandle);
+ mUserToContextMap.put(userHandle.getIdentifier(), context);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "failed to create package context for lookups", e);
+ }
+ }
+ return context;
+ }
+
+ private RankingReconsideration validatePeople(Context context,
+ final NotificationRecord record) {
final String key = record.getKey();
final Bundle extras = record.getNotification().extras;
final float[] affinityOut = new float[1];
- final RankingReconsideration rr = validatePeople(key, extras, affinityOut);
+ final RankingReconsideration rr = validatePeople(context, key, extras, affinityOut);
record.setContactAffinity(affinityOut[0]);
return rr;
}
- private PeopleRankingReconsideration validatePeople(String key, Bundle extras,
+ private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
float[] affinityOut) {
float affinity = NONE;
if (extras == null) {
@@ -98,7 +170,8 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
if (TextUtils.isEmpty(handle)) continue;
synchronized (mPeopleCache) {
- LookupResult lookupResult = mPeopleCache.get(handle);
+ final String cacheKey = getCacheKey(context.getUserId(), handle);
+ LookupResult lookupResult = mPeopleCache.get(cacheKey);
if (lookupResult == null || lookupResult.isExpired()) {
pendingLookups.add(handle);
} else {
@@ -119,7 +192,11 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
}
if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
- return new PeopleRankingReconsideration(key, pendingLookups);
+ return new PeopleRankingReconsideration(context, key, pendingLookups);
+ }
+
+ private String getCacheKey(int userId, String handle) {
+ return Integer.toString(userId) + ":" + handle;
}
// VisibleForTesting
@@ -185,24 +262,24 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
return null;
}
- private LookupResult resolvePhoneContact(final String number) {
+ private LookupResult resolvePhoneContact(Context context, final String number) {
Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(number));
- return searchContacts(phoneUri);
+ return searchContacts(context, phoneUri);
}
- private LookupResult resolveEmailContact(final String email) {
+ private LookupResult resolveEmailContact(Context context, final String email) {
Uri numberUri = Uri.withAppendedPath(
ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
Uri.encode(email));
- return searchContacts(numberUri);
+ return searchContacts(context, numberUri);
}
- private LookupResult searchContacts(Uri lookupUri) {
+ private LookupResult searchContacts(Context context, Uri lookupUri) {
LookupResult lookupResult = new LookupResult();
Cursor c = null;
try {
- c = mContext.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
+ c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
if (c != null && c.getCount() > 0) {
c.moveToFirst();
lookupResult.readContact(c);
@@ -217,44 +294,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
return lookupResult;
}
- public void initialize(Context context) {
- if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
- mContext = context;
- mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
- mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
- }
-
- public RankingReconsideration process(NotificationRecord record) {
- if (!mEnabled) {
- if (INFO) Slog.i(TAG, "disabled");
- return null;
- }
- if (record == null || record.getNotification() == null) {
- if (INFO) Slog.i(TAG, "skipping empty notification");
- return null;
- }
- return validatePeople(record);
- }
-
- @Override
- public void setConfig(RankingConfig config) {
- // ignore: config has no relevant information yet.
- }
-
- public float getContactAffinity(Bundle extras) {
- if (extras == null) return NONE;
- final String key = Long.toString(System.nanoTime());
- final float[] affinityOut = new float[1];
- final PeopleRankingReconsideration prr = validatePeople(key, extras, affinityOut);
- float affinity = affinityOut[0];
- if (prr != null) {
- prr.work();
- affinity = Math.max(prr.getContactAffinity(), affinity);
- }
- return affinity;
- }
-
private static class LookupResult {
private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
public static final int INVALID_ID = -1;
@@ -317,11 +356,13 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
private class PeopleRankingReconsideration extends RankingReconsideration {
private final LinkedList<String> mPendingLookups;
+ private final Context mContext;
private float mContactAffinity = NONE;
- private PeopleRankingReconsideration(String key, LinkedList<String> pendingLookups) {
+ private PeopleRankingReconsideration(Context context, String key, LinkedList<String> pendingLookups) {
super(key);
+ mContext = context;
mPendingLookups = pendingLookups;
}
@@ -333,20 +374,21 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
final Uri uri = Uri.parse(handle);
if ("tel".equals(uri.getScheme())) {
if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
- lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
+ lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
} else if ("mailto".equals(uri.getScheme())) {
if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
- lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
+ lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
} else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
- lookupResult = searchContacts(uri);
+ lookupResult = searchContacts(mContext, uri);
} else {
lookupResult = new LookupResult(); // invalid person for the cache
Slog.w(TAG, "unsupported URI " + handle);
}
if (lookupResult != null) {
synchronized (mPeopleCache) {
- mPeopleCache.put(handle, lookupResult);
+ final String cacheKey = getCacheKey(mContext.getUserId(), handle);
+ mPeopleCache.put(cacheKey, lookupResult);
}
mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index fd35ede..41d7fa8 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -18,7 +18,6 @@ package com.android.server.notification;
import static android.media.AudioAttributes.USAGE_ALARM;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
-import static android.media.AudioAttributes.USAGE_UNKNOWN;
import android.app.AppOpsManager;
import android.app.Notification;
@@ -225,11 +224,6 @@ public class ZenModeHelper {
muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
- // restrict vibrations with no hints
- mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, USAGE_UNKNOWN,
- zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
-
// alarm restrictions
final boolean muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, USAGE_ALARM,
@@ -307,8 +301,8 @@ public class ZenModeHelper {
final int ringerMode = mAudioManager.getRingerMode();
int newZen = -1;
if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
- if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS) {
- newZen = Global.ZEN_MODE_NO_INTERRUPTIONS;
+ if (mZenMode == Global.ZEN_MODE_OFF) {
+ newZen = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
}
} else if ((ringerMode == AudioManager.RINGER_MODE_NORMAL
|| ringerMode == AudioManager.RINGER_MODE_VIBRATE)
@@ -373,13 +367,14 @@ public class ZenModeHelper {
return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record);
}
- public boolean matchesCallFilter(Bundle extras, ValidateNotificationPeople validator) {
+ public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
+ ValidateNotificationPeople validator) {
final int zen = mZenMode;
if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
if (!mConfig.allowCalls) return false; // no calls get through
if (validator != null) {
- final float contactAffinity = validator.getContactAffinity(extras);
+ final float contactAffinity = validator.getContactAffinity(userHandle, extras);
return audienceMatches(contactAffinity);
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 355f34f..a7eebf8 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -75,7 +75,7 @@ public class BackgroundDexOptService extends JobService {
schedule(BackgroundDexOptService.this);
return;
}
- pm.performDexOpt(pkg, null /* instruction set */, false);
+ pm.performDexOpt(pkg, null /* instruction set */, true);
}
// ran to completion, so we abandon our timeslice and do not reschedule
jobFinished(jobParams, false);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b7b1c23..dcc4f8d 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -199,8 +199,7 @@ public class LauncherAppsService extends SystemService {
mainIntent.setPackage(packageName);
long ident = Binder.clearCallingIdentity();
try {
- List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
- PackageManager.NO_CROSS_PROFILE, // We only want the apps for this user
+ List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */,
user.getIdentifier());
return apps;
} finally {
@@ -288,8 +287,7 @@ public class LauncherAppsService extends SystemService {
// as calling startActivityAsUser ignores the category and just
// resolves based on the component if present.
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
- PackageManager.NO_CROSS_PROFILE, // We only want the apps for this user
- user.getIdentifier());
+ 0 /* flags */, user.getIdentifier());
final int size = apps.size();
for (int i = 0; i < size; ++i) {
ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 3f7a607..496c136 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -42,23 +42,18 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
-import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageParser;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageParser.PackageLite;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
-import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
@@ -70,7 +65,6 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -126,6 +120,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private static final String ATTR_CREATED_MILLIS = "createdMillis";
private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
+ private static final String ATTR_PREPARED = "prepared";
private static final String ATTR_SEALED = "sealed";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
@@ -148,7 +143,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private final Context mContext;
private final PackageManagerService mPm;
private final AppOpsManager mAppOps;
- private final StorageManager mStorage;
private final File mStagingDir;
private final HandlerThread mInstallThread;
@@ -190,7 +184,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
mContext = context;
mPm = pm;
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- mStorage = StorageManager.from(mContext);
mStagingDir = stagingDir;
@@ -266,7 +259,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
try {
final int sessionId = allocateSessionIdLocked();
mLegacySessions.put(sessionId, true);
- return prepareInternalStageDir(sessionId);
+ final File stageDir = buildInternalStageDir(sessionId);
+ prepareInternalStageDir(stageDir);
+ return stageDir;
} catch (IllegalStateException e) {
throw new IOException(e);
}
@@ -345,6 +340,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
+ final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
final SessionParams params = new SessionParams(
@@ -362,7 +358,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
return new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
- createdMillis, stageDir, stageCid, sealed);
+ createdMillis, stageDir, stageCid, prepared, sealed);
}
private void writeSessionsLocked() {
@@ -410,6 +406,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
if (session.stageCid != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
}
+ writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
writeIntAttribute(out, ATTR_MODE, params.mode);
@@ -483,14 +480,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- // TODO: treat INHERIT_EXISTING as install for user
-
- // Figure out where we're going to be staging session data
- final boolean stageInternal;
-
- if (params.mode == SessionParams.MODE_FULL_INSTALL) {
- // Brand new install, use best resolved location. This also verifies
- // that target has enough free space for the install.
+ if (params.mode == SessionParams.MODE_FULL_INSTALL
+ || params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ // Resolve best location for install, based on combination of
+ // requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
final int resolved = PackageHelper.resolveInstallLocation(mContext,
@@ -498,46 +491,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
params.installFlags);
if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
- stageInternal = true;
+ params.setInstallFlagsInternal();
} else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
- stageInternal = false;
+ params.setInstallFlagsExternal();
} else {
throw new IOException("No storage with enough free space; res=" + resolved);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
- } else if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
- // Inheriting existing install, so stay on the same storage medium.
- final ApplicationInfo existingApp = mPm.getApplicationInfo(params.appPackageName, 0,
- userId);
- if (existingApp == null) {
- throw new IllegalStateException(
- "Missing existing app " + params.appPackageName);
- }
-
- final long existingSize;
- try {
- final PackageLite existingPkg = PackageParser.parsePackageLite(
- new File(existingApp.getCodePath()), 0);
- existingSize = PackageHelper.calculateInstalledSize(existingPkg, false,
- params.abiOverride);
- } catch (PackageParserException e) {
- throw new IllegalStateException(
- "Failed to calculate size of " + params.appPackageName);
- }
-
- if ((existingApp.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0) {
- // Internal we can link existing install into place, so we only
- // need enough space for the new data.
- checkInternalStorage(params.sizeBytes);
- stageInternal = true;
- } else {
- // External we're going to copy existing install into our
- // container, so we need footprint of both.
- checkExternalStorage(params.sizeBytes + existingSize);
- stageInternal = false;
- }
} else {
throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
@@ -563,15 +525,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
// We're staging to exactly one location
File stageDir = null;
String stageCid = null;
- if (stageInternal) {
- stageDir = prepareInternalStageDir(sessionId);
+ if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+ stageDir = buildInternalStageDir(sessionId);
} else {
- stageCid = prepareExternalStageCid(sessionId, params.sizeBytes);
+ stageCid = buildExternalStageCid(sessionId);
}
session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
- createdMillis, stageDir, stageCid, false);
+ createdMillis, stageDir, stageCid, false, false);
mSessions.put(sessionId, session);
}
@@ -581,42 +543,50 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
@Override
- public void abandonSession(int sessionId) {
+ public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
if (session == null || !isCallingUidOwner(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
- session.abandon();
+ session.params.appIcon = appIcon;
+ mInternalCallback.onSessionBadgingChanged(session);
}
}
- private void checkInternalStorage(long sizeBytes) throws IOException {
- if (sizeBytes <= 0) return;
-
- final File target = Environment.getDataDirectory();
- final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target);
-
- mPm.freeStorage(targetBytes);
- if (target.getUsableSpace() < targetBytes) {
- throw new IOException("Not enough internal space to write " + sizeBytes + " bytes");
+ @Override
+ public void updateSessionAppLabel(int sessionId, String appLabel) {
+ synchronized (mSessions) {
+ final PackageInstallerSession session = mSessions.get(sessionId);
+ if (session == null || !isCallingUidOwner(session)) {
+ throw new SecurityException("Caller has no access to session " + sessionId);
+ }
+ session.params.appLabel = appLabel;
+ mInternalCallback.onSessionBadgingChanged(session);
}
}
- private void checkExternalStorage(long sizeBytes) throws IOException {
- if (sizeBytes <= 0) return;
-
- final File target = new UserEnvironment(UserHandle.USER_OWNER)
- .getExternalStorageDirectory();
- final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target);
-
- if (target.getUsableSpace() < targetBytes) {
- throw new IOException("Not enough external space to write " + sizeBytes + " bytes");
+ @Override
+ public void abandonSession(int sessionId) {
+ synchronized (mSessions) {
+ final PackageInstallerSession session = mSessions.get(sessionId);
+ if (session == null || !isCallingUidOwner(session)) {
+ throw new SecurityException("Caller has no access to session " + sessionId);
+ }
+ session.abandon();
}
}
@Override
public IPackageInstallerSession openSession(int sessionId) {
+ try {
+ return openSessionInternal(sessionId);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
if (session == null || !isCallingUidOwner(session)) {
@@ -641,49 +611,43 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
throw new IllegalStateException("Failed to allocate session ID");
}
- private File prepareInternalStageDir(int sessionId) throws IOException {
- final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp");
+ private File buildInternalStageDir(int sessionId) {
+ return new File(mStagingDir, "vmdl" + sessionId + ".tmp");
+ }
- if (file.exists()) {
- throw new IOException("Session dir already exists: " + file);
+ static void prepareInternalStageDir(File stageDir) throws IOException {
+ if (stageDir.exists()) {
+ throw new IOException("Session dir already exists: " + stageDir);
}
try {
- Os.mkdir(file.getAbsolutePath(), 0755);
- Os.chmod(file.getAbsolutePath(), 0755);
+ Os.mkdir(stageDir.getAbsolutePath(), 0755);
+ Os.chmod(stageDir.getAbsolutePath(), 0755);
} catch (ErrnoException e) {
// This purposefully throws if directory already exists
- throw new IOException("Failed to prepare session dir", e);
+ throw new IOException("Failed to prepare session dir: " + stageDir, e);
}
- if (!SELinux.restorecon(file)) {
- throw new IOException("Failed to restorecon session dir");
+ if (!SELinux.restorecon(stageDir)) {
+ throw new IOException("Failed to restorecon session dir: " + stageDir);
}
-
- return file;
}
- private String prepareExternalStageCid(int sessionId, long sizeBytes) throws IOException {
- if (sizeBytes <= 0) {
- throw new IOException("Session must provide valid size for ASEC");
- }
+ private String buildExternalStageCid(int sessionId) {
+ return "smdl" + sessionId + ".tmp";
+ }
- final String cid = "smdl" + sessionId + ".tmp";
- if (PackageHelper.createSdDir(sizeBytes, cid, PackageManagerService.getEncryptKey(),
+ static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
+ if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
Process.SYSTEM_UID, true) == null) {
- throw new IOException("Failed to create ASEC");
+ throw new IOException("Failed to create session cid: " + stageCid);
}
-
- return cid;
}
@Override
public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
- if (!isCallingUidOwner(session)) {
- enforceCallerCanReadSessions();
- }
return session != null ? session.generateInfo() : null;
}
}
@@ -691,7 +655,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public List<SessionInfo> getAllSessions(int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions");
- enforceCallerCanReadSessions();
final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
@@ -755,8 +718,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public void registerCallback(IPackageInstallerCallback callback, int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerCallback");
- enforceCallerCanReadSessions();
-
mCallbacks.register(callback, userId);
}
@@ -787,21 +748,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- /**
- * We allow those with permission, or the current home app.
- */
- private void enforceCallerCanReadSessions() {
- final boolean hasPermission = (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.READ_INSTALL_SESSIONS)
- == PackageManager.PERMISSION_GRANTED);
- final boolean isHomeApp = mPm.checkCallerIsHomeApp();
- if (hasPermission || isHomeApp) {
- return;
- } else {
- throw new SecurityException("Caller must be current home app to read install sessions");
- }
- }
-
static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
private final Context mContext;
private final IntentSender mTarget;
@@ -893,9 +839,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private static class Callbacks extends Handler {
private static final int MSG_SESSION_CREATED = 1;
- private static final int MSG_SESSION_OPENED = 2;
- private static final int MSG_SESSION_PROGRESS_CHANGED = 3;
- private static final int MSG_SESSION_CLOSED = 4;
+ private static final int MSG_SESSION_BADGING_CHANGED = 2;
+ private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
+ private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
private static final int MSG_SESSION_FINISHED = 5;
private final RemoteCallbackList<IPackageInstallerCallback>
@@ -938,15 +884,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
case MSG_SESSION_CREATED:
callback.onSessionCreated(sessionId);
break;
- case MSG_SESSION_OPENED:
- callback.onSessionOpened(sessionId);
+ case MSG_SESSION_BADGING_CHANGED:
+ callback.onSessionBadgingChanged(sessionId);
+ break;
+ case MSG_SESSION_ACTIVE_CHANGED:
+ callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
break;
case MSG_SESSION_PROGRESS_CHANGED:
callback.onSessionProgressChanged(sessionId, (float) msg.obj);
break;
- case MSG_SESSION_CLOSED:
- callback.onSessionClosed(sessionId);
- break;
case MSG_SESSION_FINISHED:
callback.onSessionFinished(sessionId, (boolean) msg.obj);
break;
@@ -957,16 +903,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
}
- private void notifySessionOpened(int sessionId, int userId) {
- obtainMessage(MSG_SESSION_OPENED, sessionId, userId).sendToTarget();
+ private void notifySessionBadgingChanged(int sessionId, int userId) {
+ obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
}
- private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
- obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
+ private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
+ obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
}
- private void notifySessionClosed(int sessionId, int userId) {
- obtainMessage(MSG_SESSION_CLOSED, sessionId, userId).sendToTarget();
+ private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
+ obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
}
public void notifySessionFinished(int sessionId, int userId, boolean success) {
@@ -1006,16 +952,17 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
class InternalCallback {
- public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
- mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
+ public void onSessionBadgingChanged(PackageInstallerSession session) {
+ mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
+ writeSessionsAsync();
}
- public void onSessionOpened(PackageInstallerSession session) {
- mCallbacks.notifySessionOpened(session.sessionId, session.userId);
+ public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
+ mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
}
- public void onSessionClosed(PackageInstallerSession session) {
- mCallbacks.notifySessionClosed(session.sessionId, session.userId);
+ public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
+ mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
}
public void onSessionFinished(PackageInstallerSession session, boolean success) {
@@ -1027,6 +974,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
writeSessionsAsync();
}
+ public void onSessionPrepared(PackageInstallerSession session) {
+ // We prepared the destination to write into; we want to persist
+ // this, but it's not critical enough to block for.
+ writeSessionsAsync();
+ }
+
public void onSessionSealed(PackageInstallerSession session) {
// It's very important that we block until we've recorded the
// session as being sealed, since we never want to allow mutation
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5264fc4..06f550d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -17,15 +17,15 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
+import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
+import static com.android.server.pm.PackageInstallerService.prepareInternalStageDir;
import android.content.Context;
import android.content.Intent;
@@ -88,8 +88,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
- // TODO: treat INHERIT_EXISTING as installExistingPackage()
-
private final PackageInstallerService.InternalCallback mCallback;
private final Context mContext;
private final PackageManagerService mPm;
@@ -108,18 +106,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** Note that UID is not persisted; it's always derived at runtime. */
final int installerUid;
- private final AtomicInteger mOpenCount = new AtomicInteger();
+ private final AtomicInteger mActiveCount = new AtomicInteger();
private final Object mLock = new Object();
@GuardedBy("mLock")
private float mClientProgress = 0;
@GuardedBy("mLock")
+ private float mInternalProgress = 0;
+
+ @GuardedBy("mLock")
private float mProgress = 0;
@GuardedBy("mLock")
private float mReportedProgress = -1;
@GuardedBy("mLock")
+ private boolean mPrepared = false;
+ @GuardedBy("mLock")
private boolean mSealed = false;
@GuardedBy("mLock")
private boolean mPermissionsAccepted = false;
@@ -184,7 +187,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
String installerPackageName, SessionParams params, long createdMillis,
- File stageDir, String stageCid, boolean sealed) {
+ File stageDir, String stageCid, boolean prepared, boolean sealed) {
mCallback = callback;
mContext = context;
mPm = pm;
@@ -203,6 +206,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Exactly one of stageDir or stageCid stage must be set");
}
+ mPrepared = prepared;
mSealed = sealed;
// Always derived at runtime
@@ -214,8 +218,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} else {
mPermissionsAccepted = false;
}
-
- computeProgressLocked();
}
public SessionInfo generateInfo() {
@@ -227,7 +229,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mResolvedBaseFile.getAbsolutePath() : null;
info.progress = mProgress;
info.sealed = mSealed;
- info.open = mOpenCount.get() > 0;
+ info.active = mActiveCount.get() > 0;
info.mode = params.mode;
info.sizeBytes = params.sizeBytes;
@@ -238,14 +240,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return info;
}
+ public boolean isPrepared() {
+ synchronized (mLock) {
+ return mPrepared;
+ }
+ }
+
public boolean isSealed() {
synchronized (mLock) {
return mSealed;
}
}
- private void assertNotSealed(String cookie) {
+ private void assertPreparedAndNotSealed(String cookie) {
synchronized (mLock) {
+ if (!mPrepared) {
+ throw new IllegalStateException(cookie + " before prepared");
+ }
if (mSealed) {
throw new SecurityException(cookie + " not allowed after commit");
}
@@ -278,28 +289,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void setClientProgress(float progress) {
synchronized (mLock) {
+ // Always publish first staging movement
+ final boolean forcePublish = (mClientProgress == 0);
mClientProgress = progress;
- computeProgressLocked();
+ computeProgressLocked(forcePublish);
}
- maybePublishProgress();
}
@Override
public void addClientProgress(float progress) {
synchronized (mLock) {
- mClientProgress += progress;
- computeProgressLocked();
+ setClientProgress(mClientProgress + progress);
}
- maybePublishProgress();
}
- private void computeProgressLocked() {
- mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f);
- }
+ private void computeProgressLocked(boolean forcePublish) {
+ mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+ + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
- private void maybePublishProgress() {
// Only publish when meaningful change
- if (Math.abs(mProgress - mReportedProgress) > 0.01) {
+ if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
mReportedProgress = mProgress;
mCallback.onSessionProgressChanged(this, mProgress);
}
@@ -307,7 +316,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public String[] getNames() {
- assertNotSealed("getNames");
+ assertPreparedAndNotSealed("getNames");
try {
return resolveStageDir().list();
} catch (IOException e) {
@@ -331,7 +340,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// will block any attempted install transitions.
final FileBridge bridge;
synchronized (mLock) {
- assertNotSealed("openWrite");
+ assertPreparedAndNotSealed("openWrite");
bridge = new FileBridge();
mBridges.add(bridge);
@@ -385,7 +394,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private ParcelFileDescriptor openReadInternal(String name) throws IOException {
- assertNotSealed("openRead");
+ assertPreparedAndNotSealed("openRead");
try {
if (!FileUtils.isValidExtFilename(name)) {
@@ -405,6 +414,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public void commit(IntentSender statusReceiver) {
Preconditions.checkNotNull(statusReceiver);
+ synchronized (mLock) {
+ if (!mSealed) {
+ // Verify that all writers are hands-off
+ for (FileBridge bridge : mBridges) {
+ if (!bridge.isClosed()) {
+ throw new SecurityException("Files still open");
+ }
+ }
+
+ // Persist the fact that we've sealed ourselves to prevent
+ // mutations of any hard links we create.
+ mSealed = true;
+ mCallback.onSessionSealed(this);
+ }
+ }
+
+ // Client staging is fully done at this point
+ mClientProgress = 1f;
+ computeProgressLocked(true);
+
+ // This ongoing commit should keep session active, even though client
+ // will probably close their end.
+ mActiveCount.incrementAndGet();
+
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId);
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
@@ -412,22 +445,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private void commitLocked() throws PackageManagerException {
if (mDestroyed) {
- throw new PackageManagerException(INSTALL_FAILED_ALREADY_EXISTS, "Invalid session");
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
}
-
- // Verify that all writers are hands-off
if (!mSealed) {
- for (FileBridge bridge : mBridges) {
- if (!bridge.isClosed()) {
- throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
- "Files still open");
- }
- }
- mSealed = true;
-
- // Persist the fact that we've sealed ourselves to prevent mutations
- // of any hard links we create below.
- mCallback.onSessionSealed(this);
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
}
try {
@@ -456,6 +477,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mRemoteObserver.onUserActionRequired(intent);
} catch (RemoteException ignored) {
}
+
+ // Commit was keeping session marked as active until now; release
+ // that extra refcount so session appears idle.
+ close();
return;
}
@@ -485,7 +510,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
// TODO: surface more granular state from dexopt
- mCallback.onSessionProgressChanged(this, 0.9f);
+ mInternalProgress = 0.5f;
+ computeProgressLocked(true);
// Unpack native libraries
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
@@ -553,8 +579,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Failed to parse " + file + ": " + e);
+ throw PackageManagerException.from(e);
}
if (!stagedSplits.add(apk.splitName)) {
@@ -620,8 +645,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Failed to parse existing package " + app.getCodePath() + ": " + e);
+ throw PackageManagerException.from(e);
}
assertApkConsistent("Existing base", existingBase);
@@ -673,8 +697,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
} catch (PackageParserException e) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Failed to parse base package " + mResolvedBaseFile + ": " + e);
+ throw PackageManagerException.from(e);
}
final List<String> splitPaths = new ArrayList<>();
@@ -828,16 +851,36 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- public void open() {
- if (mOpenCount.getAndIncrement() == 0) {
- mCallback.onSessionOpened(this);
+ public void open() throws IOException {
+ if (mActiveCount.getAndIncrement() == 0) {
+ mCallback.onSessionActiveChanged(this, true);
+ }
+
+ synchronized (mLock) {
+ if (!mPrepared) {
+ if (stageDir != null) {
+ prepareInternalStageDir(stageDir);
+ } else if (stageCid != null) {
+ prepareExternalStageCid(stageCid, params.sizeBytes);
+
+ // TODO: deliver more granular progress for ASEC allocation
+ mInternalProgress = 0.25f;
+ computeProgressLocked(true);
+ } else {
+ throw new IllegalArgumentException(
+ "Exactly one of stageDir or stageCid stage must be set");
+ }
+
+ mPrepared = true;
+ mCallback.onSessionPrepared(this);
+ }
}
}
@Override
public void close() {
- if (mOpenCount.decrementAndGet() == 0) {
- mCallback.onSessionClosed(this);
+ if (mActiveCount.decrementAndGet() == 0) {
+ mCallback.onSessionActiveChanged(this, false);
}
}
@@ -866,6 +909,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
synchronized (mLock) {
mSealed = true;
mDestroyed = true;
+
+ // Force shut down all bridges
+ for (FileBridge bridge : mBridges) {
+ bridge.forceClose();
+ }
}
if (stageDir != null) {
FileUtils.deleteContents(stageDir);
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index 0cbdcdc..a41636e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import android.content.pm.PackageParser.PackageParserException;
+
/** {@hide} */
public class PackageManagerException extends Exception {
public final int error;
@@ -29,4 +31,9 @@ public class PackageManagerException extends Exception {
super(detailMessage, throwable);
this.error = error;
}
+
+ public static PackageManagerException from(PackageParserException e)
+ throws PackageManagerException {
+ throw new PackageManagerException(e.error, e.getMessage(), e.getCause());
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 228c120..a5e84f6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -312,6 +312,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final PackageHandler mHandler;
+ /**
+ * Messages for {@link #mHandler} that need to wait for system ready before
+ * being dispatched.
+ */
+ private ArrayList<Message> mPostSystemReadyMessages;
+
final int mSdkVersion = Build.VERSION.SDK_INT;
final Context mContext;
@@ -446,9 +452,9 @@ public class PackageManagerService extends IPackageManager.Stub {
/** Token for keys in mPendingVerification. */
private int mPendingVerificationToken = 0;
- boolean mSystemReady;
- boolean mSafeMode;
- boolean mHasSystemUidErrors;
+ volatile boolean mSystemReady;
+ volatile boolean mSafeMode;
+ volatile boolean mHasSystemUidErrors;
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity = new ActivityInfo();
@@ -3177,24 +3183,6 @@ public class PackageManagerService extends IPackageManager.Stub {
if (matches.get(i).getTargetUserId() == targetUserId) return true;
}
}
- ArrayList<String> packageNames = null;
- SparseArray<ArrayList<String>> fromSource =
- mSettings.mCrossProfilePackageInfo.get(sourceUserId);
- if (fromSource != null) {
- packageNames = fromSource.get(targetUserId);
- if (packageNames != null) {
- // We need the package name, so we try to resolve with the loosest flags possible
- List<ResolveInfo> resolveInfos = mActivities.queryIntent(intent, resolvedType,
- PackageManager.GET_UNINSTALLED_PACKAGES, targetUserId);
- int count = resolveInfos.size();
- for (int i = 0; i < count; i++) {
- ResolveInfo resolveInfo = resolveInfos.get(i);
- if (packageNames.contains(resolveInfo.activityInfo.packageName)) {
- return true;
- }
- }
- }
- }
return false;
}
@@ -3234,32 +3222,21 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final String pkgName = intent.getPackage();
- boolean queryCrossProfile = (flags & PackageManager.NO_CROSS_PROFILE) == 0;
if (pkgName == null) {
- ResolveInfo resolveInfo = null;
- if (queryCrossProfile) {
- // Check if the intent needs to be forwarded to another user for this package
- ArrayList<ResolveInfo> crossProfileResult =
- queryIntentActivitiesCrossProfilePackage(
- intent, resolvedType, flags, userId);
- if (!crossProfileResult.isEmpty()) {
- // Skip the current profile
- return crossProfileResult;
- }
- List<CrossProfileIntentFilter> matchingFilters =
- getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
- // Check for results that need to skip the current profile.
- resolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
- resolvedType, flags, userId);
- if (resolveInfo != null) {
- List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
- result.add(resolveInfo);
- return result;
- }
- // Check for cross profile results.
- resolveInfo = queryCrossProfileIntents(
- matchingFilters, intent, resolvedType, flags, userId);
+ List<CrossProfileIntentFilter> matchingFilters =
+ getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
+ // Check for results that need to skip the current profile.
+ ResolveInfo resolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
+ resolvedType, flags, userId);
+ if (resolveInfo != null) {
+ List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
+ result.add(resolveInfo);
+ return result;
}
+ // Check for cross profile results.
+ resolveInfo = queryCrossProfileIntents(
+ matchingFilters, intent, resolvedType, flags, userId);
+
// Check for results in the current profile.
List<ResolveInfo> result = mActivities.queryIntent(
intent, resolvedType, flags, userId);
@@ -3271,15 +3248,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- if (queryCrossProfile) {
- ArrayList<ResolveInfo> crossProfileResult =
- queryIntentActivitiesCrossProfilePackage(
- intent, resolvedType, flags, userId, pkg, pkgName);
- if (!crossProfileResult.isEmpty()) {
- // Skip the current profile
- return crossProfileResult;
- }
- }
return mActivities.queryIntentForPackage(intent, resolvedType, flags,
pkg.activities, userId);
}
@@ -3308,56 +3276,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
- private ArrayList<ResolveInfo> queryIntentActivitiesCrossProfilePackage(
- Intent intent, String resolvedType, int flags, int userId) {
- ArrayList<ResolveInfo> matchingResolveInfos = new ArrayList<ResolveInfo>();
- SparseArray<ArrayList<String>> sourceForwardingInfo =
- mSettings.mCrossProfilePackageInfo.get(userId);
- if (sourceForwardingInfo != null) {
- int NI = sourceForwardingInfo.size();
- for (int i = 0; i < NI; i++) {
- int targetUserId = sourceForwardingInfo.keyAt(i);
- ArrayList<String> packageNames = sourceForwardingInfo.valueAt(i);
- List<ResolveInfo> resolveInfos = mActivities.queryIntent(
- intent, resolvedType, flags, targetUserId);
- int NJ = resolveInfos.size();
- for (int j = 0; j < NJ; j++) {
- ResolveInfo resolveInfo = resolveInfos.get(j);
- if (packageNames.contains(resolveInfo.activityInfo.packageName)) {
- matchingResolveInfos.add(createForwardingResolveInfo(
- resolveInfo.filter, userId, targetUserId));
- }
- }
- }
- }
- return matchingResolveInfos;
- }
-
- private ArrayList<ResolveInfo> queryIntentActivitiesCrossProfilePackage(
- Intent intent, String resolvedType, int flags, int userId, PackageParser.Package pkg,
- String packageName) {
- ArrayList<ResolveInfo> matchingResolveInfos = new ArrayList<ResolveInfo>();
- SparseArray<ArrayList<String>> sourceForwardingInfo =
- mSettings.mCrossProfilePackageInfo.get(userId);
- if (sourceForwardingInfo != null) {
- int NI = sourceForwardingInfo.size();
- for (int i = 0; i < NI; i++) {
- int targetUserId = sourceForwardingInfo.keyAt(i);
- if (sourceForwardingInfo.valueAt(i).contains(packageName)) {
- List<ResolveInfo> resolveInfos = mActivities.queryIntentForPackage(
- intent, resolvedType, flags, pkg.activities, targetUserId);
- int NJ = resolveInfos.size();
- for (int j = 0; j < NJ; j++) {
- ResolveInfo resolveInfo = resolveInfos.get(j);
- matchingResolveInfos.add(createForwardingResolveInfo(
- resolveInfo.filter, userId, targetUserId));
- }
- }
- }
- }
- return matchingResolveInfos;
- }
-
// Return matching ResolveInfo if any for skip current profile intent filters.
private ResolveInfo queryCrossProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
@@ -3777,21 +3695,25 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
}
- if ((flags&PackageManager.GET_PERMISSIONS) == 0) {
- if (numMatch == permissions.length) {
- pi.requestedPermissions = permissions;
- } else {
- pi.requestedPermissions = new String[numMatch];
- numMatch = 0;
- for (int i=0; i<permissions.length; i++) {
- if (tmp[i]) {
- pi.requestedPermissions[numMatch] = permissions[i];
- numMatch++;
+ // The above might return null in cases of uninstalled apps or install-state
+ // skew across users/profiles.
+ if (pi != null) {
+ if ((flags&PackageManager.GET_PERMISSIONS) == 0) {
+ if (numMatch == permissions.length) {
+ pi.requestedPermissions = permissions;
+ } else {
+ pi.requestedPermissions = new String[numMatch];
+ numMatch = 0;
+ for (int i=0; i<permissions.length; i++) {
+ if (tmp[i]) {
+ pi.requestedPermissions[numMatch] = permissions[i];
+ numMatch++;
+ }
}
}
}
+ list.add(pi);
}
- list.add(pi);
}
@Override
@@ -4155,8 +4077,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
- throw new PackageManagerException(e.error, "Failed to collect certificates for "
- + pkg.packageName + ": " + e.getMessage());
+ throw PackageManagerException.from(e);
}
}
@@ -4181,8 +4102,7 @@ public class PackageManagerService extends IPackageManager.Stub {
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
- throw new PackageManagerException(e.error,
- "Failed to scan " + scanFile + ": " + e.getMessage());
+ throw PackageManagerException.from(e);
}
PackageSetting ps = null;
@@ -4512,7 +4432,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
- return performDexOpt(packageName, instructionSet, true);
+ return performDexOpt(packageName, instructionSet, false);
}
private static String getPrimaryInstructionSet(ApplicationInfo info) {
@@ -7685,16 +7605,18 @@ public class PackageManagerService extends IPackageManager.Stub {
}
void schedulePackageCleaning(String packageName, int userId, boolean andCode) {
- if (false) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.d(TAG, "Schedule cleaning " + packageName + " user=" + userId
- + " andCode=" + andCode, here);
+ final Message msg = mHandler.obtainMessage(START_CLEANING_PACKAGE,
+ userId, andCode ? 1 : 0, packageName);
+ if (mSystemReady) {
+ msg.sendToTarget();
+ } else {
+ if (mPostSystemReadyMessages == null) {
+ mPostSystemReadyMessages = new ArrayList<>();
+ }
+ mPostSystemReadyMessages.add(msg);
}
- mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,
- userId, andCode ? 1 : 0, packageName));
}
-
+
void startCleaningPackages() {
// reader
synchronized (mPackages) {
@@ -10245,8 +10167,18 @@ public class PackageManagerService extends IPackageManager.Stub {
if (bp != null) {
// If the defining package is signed with our cert, it's okay. This
// also includes the "updating the same package" case, of course.
- if (compareSignatures(bp.packageSetting.signatures.mSignatures,
- pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
+ // "updating same package" could also involve key-rotation.
+ final boolean sigsOk;
+ if (!bp.sourcePackage.equals(pkg.packageName)
+ || !(bp.packageSetting instanceof PackageSetting)
+ || !bp.packageSetting.keySetData.isUsingUpgradeKeySets()
+ || ((PackageSetting) bp.packageSetting).sharedUser != null) {
+ sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
+ pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
+ } else {
+ sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
+ }
+ if (!sigsOk) {
// If the owning package is the system itself, we log but allow
// install to proceed; we fail the install on all other permission
// redefinitions.
@@ -11642,24 +11574,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
- public void addCrossProfileIntentsForPackage(String packageName,
- int sourceUserId, int targetUserId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- mSettings.addCrossProfilePackage(packageName, sourceUserId, targetUserId);
- mSettings.writePackageRestrictionsLPr(sourceUserId);
- }
-
- @Override
- public void removeCrossProfileIntentsForPackage(String packageName,
- int sourceUserId, int targetUserId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- mSettings.removeCrossProfilePackage(packageName, sourceUserId, targetUserId);
- mSettings.writePackageRestrictionsLPr(sourceUserId);
- }
-
- @Override
public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage,
int ownerUserId) {
mContext.enforceCallingOrSelfPermission(
@@ -11728,47 +11642,6 @@ public class PackageManagerService extends IPackageManager.Stub {
preferred.activityInfo.name);
}
- /**
- * Check if calling UID is the current home app. This handles both the case
- * where the user has selected a specific home app, and where there is only
- * one home app.
- */
- public boolean checkCallerIsHomeApp() {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_HOME);
-
- final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getCallingUserId();
- final List<ResolveInfo> allHomes = queryIntentActivities(intent, null, 0, callingUserId);
- final ResolveInfo preferredHome = findPreferredActivity(intent, null, 0, allHomes, 0, true,
- false, false, callingUserId);
-
- if (preferredHome != null) {
- if (callingUid == preferredHome.activityInfo.applicationInfo.uid) {
- return true;
- }
- } else {
- for (ResolveInfo info : allHomes) {
- if (callingUid == info.activityInfo.applicationInfo.uid) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Enforce that calling UID is the current home app. This handles both the
- * case where the user has selected a specific home app, and where there is
- * only one home app.
- */
- public void enforceCallerIsHomeApp() {
- if (!checkCallerIsHomeApp()) {
- throw new SecurityException("Caller is not currently selected home app");
- }
- }
-
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
@@ -12028,6 +11901,14 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
sUserManager.systemReady();
+
+ // Kick off any messages waiting for system ready
+ if (mPostSystemReadyMessages != null) {
+ for (Message msg : mPostSystemReadyMessages) {
+ msg.sendToTarget();
+ }
+ mPostSystemReadyMessages = null;
+ }
}
@Override
@@ -13025,6 +12906,9 @@ public class PackageManagerService extends IPackageManager.Stub {
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
+ if (ps.pkg == null) {
+ continue;
+ }
final String packageName = ps.pkg.packageName;
// Skip over if system app
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c346f71..a74e3f1 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -36,7 +36,6 @@ import android.os.FileUtils;
import android.os.PatternMatcher;
import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManager;
import android.util.LogPrinter;
import com.android.internal.util.FastXmlSerializer;
@@ -138,10 +137,6 @@ final class Settings {
"persistent-preferred-activities";
static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
"crossProfile-intent-filters";
- private static final String TAG_CROSS_PROFILE_PACKAGE_INFO = "cross-profile-package-info";
- private static final String CROSS_PROFILE_PACKAGE_INFO_ATTR_TARGET_USER_ID = "target-user-id";
- private static final String CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME = "package-name";
- private static final String CROSS_PROFILE_PACKAGE_INFO_ATTR_PACKAGE_NAME = "value";
private static final String ATTR_NAME = "name";
private static final String ATTR_USER = "user";
@@ -249,23 +244,15 @@ final class Settings {
*/
private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>();
- private final Context mContext;
-
private final File mSystemDir;
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
- // A mapping of (sourceUserId, targetUserId, packageNames) for forwarding the intents of a
- // package.
- final SparseArray<SparseArray<ArrayList<String>>>
- mCrossProfilePackageInfo = new SparseArray<SparseArray<ArrayList<String>>>();
-
Settings(Context context) {
this(context, Environment.getDataDirectory());
}
Settings(Context context, File dataDir) {
- mContext = context;
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
@@ -282,47 +269,6 @@ final class Settings {
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
- public void addCrossProfilePackage(
- String packageName, int sourceUserId, int targetUserId) {
- synchronized(mCrossProfilePackageInfo) {
- SparseArray<ArrayList<String>> sourceForwardingInfo =
- mCrossProfilePackageInfo.get(sourceUserId);
- if (sourceForwardingInfo == null) {
- sourceForwardingInfo = new SparseArray<ArrayList<String>>();
- mCrossProfilePackageInfo.put(sourceUserId, sourceForwardingInfo);
- }
- ArrayList<String> packageNames = sourceForwardingInfo.get(targetUserId);
- if (packageNames == null) {
- packageNames = new ArrayList<String>();
- sourceForwardingInfo.put(targetUserId, packageNames);
- }
- if (!packageNames.contains(packageName)) {
- packageNames.add(packageName);
- }
- }
- }
-
- public void removeCrossProfilePackage(
- String packageName, int sourceUserId, int targetUserId) {
- synchronized(mCrossProfilePackageInfo) {
- SparseArray<ArrayList<String>> sourceForwardingInfo =
- mCrossProfilePackageInfo.get(sourceUserId);
- if (sourceForwardingInfo == null) {
- return;
- }
- ArrayList<String> packageNames = sourceForwardingInfo.get(targetUserId);
- if (packageNames != null && packageNames.contains(packageName)) {
- packageNames.remove(packageName);
- if (packageNames.isEmpty()) {
- sourceForwardingInfo.remove(targetUserId);
- if (sourceForwardingInfo.size() == 0) {
- mCrossProfilePackageInfo.remove(sourceUserId);
- }
- }
- }
- }
- }
-
PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbi, String secondaryCpuAbi,
@@ -602,11 +548,8 @@ final class Settings {
// original default value is true), or we are being
// asked to install for all users, or this is the
// user we are installing for.
- // In this context all users (USER_ALL) implies an adb install,
- // so we additionally check whether that is allowed for this user.
final boolean installed = installUser == null
- || (installUser.getIdentifier() == UserHandle.USER_ALL
- && (!isUnknownSourcesDisallowed(user.id)))
+ || installUser.getIdentifier() == UserHandle.USER_ALL
|| installUser.getIdentifier() == user.id;
p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
installed,
@@ -673,10 +616,7 @@ final class Settings {
List<UserInfo> users = getAllUsers();
if (users != null) {
for (UserInfo user : users) {
- // Installing for USER_ALL implies an adb install, so we
- // additionally check whether that is allowed for this user.
- if ((installUser.getIdentifier() == UserHandle.USER_ALL
- && (!isUnknownSourcesDisallowed(user.id)))
+ if (installUser.getIdentifier() == UserHandle.USER_ALL
|| installUser.getIdentifier() == user.id) {
boolean installed = p.getInstalled(user.id);
if (!installed) {
@@ -691,12 +631,6 @@ final class Settings {
return p;
}
- boolean isUnknownSourcesDisallowed(int userId) {
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- return um.getUserRestrictions(new UserHandle(userId)).getBoolean(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, false);
- }
-
void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) {
p.pkg = pkg;
// pkg.mSetEnabled = p.getEnabled(userId);
@@ -1080,68 +1014,6 @@ final class Settings {
}
}
- private void readCrossProfilePackageInfoLPw(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- String tagName = parser.getName();
- if (tagName.equals(TAG_ITEM)) {
- String targetUserIdString = parser.getAttributeValue(null,
- CROSS_PROFILE_PACKAGE_INFO_ATTR_TARGET_USER_ID);
- if (targetUserIdString == null) {
- String msg = "Missing element under " + TAG +": "
- + CROSS_PROFILE_PACKAGE_INFO_ATTR_TARGET_USER_ID + " at " +
- parser.getPositionDescription();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- continue;
- }
- int targetUserId = Integer.parseInt(targetUserIdString);
- readCrossProfilePackageInfoForTargetLPw(parser, userId, targetUserId);
- } else {
- String msg = "Unknown element under " + TAG_CROSS_PROFILE_PACKAGE_INFO + ": " +
- parser.getName();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }
-
- private void readCrossProfilePackageInfoForTargetLPw(
- XmlPullParser parser, int sourceUserId, int targetUserId)
- throws XmlPullParserException, IOException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- String tagName = parser.getName();
- if (tagName.equals(CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME)) {
- String packageName = parser.getAttributeValue(
- null, CROSS_PROFILE_PACKAGE_INFO_ATTR_PACKAGE_NAME);
- if (packageName == null) {
- String msg = "Missing element under " + TAG +": "
- + CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME + " at " +
- parser.getPositionDescription();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- continue;
- }
- addCrossProfilePackage(packageName, sourceUserId, targetUserId);
- } else {
- String msg = "Unknown element under " + TAG_ITEM + ": " +
- parser.getName();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }
-
void readPackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1283,8 +1155,6 @@ final class Settings {
readPersistentPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
readCrossProfileIntentFiltersLPw(parser, userId);
- } else if (tagName.equals(TAG_CROSS_PROFILE_PACKAGE_INFO)){
- readCrossProfilePackageInfoLPw(parser, userId);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -1376,32 +1246,6 @@ final class Settings {
serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
}
- void writeCrossProfilePackageInfoLPr(XmlSerializer serializer, int userId)
- throws IllegalArgumentException, IllegalStateException, IOException {
- SparseArray<ArrayList<String>> sourceForwardingInfo = mCrossProfilePackageInfo.get(userId);
- if (sourceForwardingInfo == null) {
- return;
- }
- serializer.startTag(null, TAG_CROSS_PROFILE_PACKAGE_INFO);
- int NI = sourceForwardingInfo.size();
- for (int i = 0; i < NI; i++) {
- int targetUserId = sourceForwardingInfo.keyAt(i);
- ArrayList<String> packageNames = sourceForwardingInfo.valueAt(i);
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, CROSS_PROFILE_PACKAGE_INFO_ATTR_TARGET_USER_ID,
- Integer.toString(targetUserId));
- int NJ = packageNames.size();
- for (int j = 0; j < NJ; j++) {
- serializer.startTag(null, CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME);
- serializer.attribute(null, CROSS_PROFILE_PACKAGE_INFO_ATTR_PACKAGE_NAME,
- packageNames.get(j));
- serializer.endTag(null, CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME);
- }
- serializer.endTag(null, TAG_ITEM);
- }
- serializer.endTag(null, TAG_CROSS_PROFILE_PACKAGE_INFO);
- }
-
void writePackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Writing package restrictions for user=" + userId);
@@ -1506,8 +1350,6 @@ final class Settings {
writeCrossProfileIntentFiltersLPr(serializer, userId);
- writeCrossProfilePackageInfoLPr(serializer, userId);
-
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
serializer.endDocument();
@@ -3192,7 +3034,6 @@ final class Settings {
file = getUserPackagesStateBackupFile(userId);
file.delete();
removeCrossProfileIntentFiltersLPw(userId);
- removeCrossProfilePackagesLPw(userId);
}
void removeCrossProfileIntentFiltersLPw(int userId) {
@@ -3223,27 +3064,6 @@ final class Settings {
}
}
- public void removeCrossProfilePackagesLPw(int userId) {
- synchronized(mCrossProfilePackageInfo) {
- // userId is the source user
- if (mCrossProfilePackageInfo.get(userId) != null) {
- mCrossProfilePackageInfo.remove(userId);
- writePackageRestrictionsLPr(userId);
- }
- // userId is the target user
- int count = mCrossProfilePackageInfo.size();
- for (int i = 0; i < count; i++) {
- int sourceUserId = mCrossProfilePackageInfo.keyAt(i);
- SparseArray<ArrayList<String>> sourceForwardingInfo =
- mCrossProfilePackageInfo.valueAt(i);
- if (sourceForwardingInfo.get(userId) != null) {
- sourceForwardingInfo.remove(userId);
- writePackageRestrictionsLPr(sourceUserId);
- }
- }
- }
- }
-
// This should be called (at least) whenever an application is removed
private void setFirstAvailableUid(int uid) {
if (uid > mFirstAvailableUid) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4a2cece..d032d29 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -21,7 +21,6 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.ActivityThread;
import android.app.IStopUserCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -92,6 +91,7 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ATTR_SERIAL_NO = "serialNumber";
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
+ private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove";
private static final String ATTR_USER_VERSION = "version";
private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId";
private static final String TAG_GUEST_RESTRICTIONS = "guestRestrictions";
@@ -228,7 +228,7 @@ public class UserManagerService extends IUserManager.Stub {
ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
for (int i = 0; i < mUsers.size(); i++) {
UserInfo ui = mUsers.valueAt(i);
- if (ui.partial && i != 0) {
+ if ((ui.partial || ui.guestToRemove) && i != 0) {
partials.add(ui);
}
}
@@ -759,6 +759,9 @@ public class UserManagerService extends IUserManager.Stub {
if (userInfo.partial) {
serializer.attribute(null, ATTR_PARTIAL, "true");
}
+ if (userInfo.guestToRemove) {
+ serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true");
+ }
if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
serializer.attribute(null, ATTR_PROFILE_GROUP_ID,
Integer.toString(userInfo.profileGroupId));
@@ -806,7 +809,7 @@ public class UserManagerService extends IUserManager.Stub {
serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
- serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
+ serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
writeRestrictionsLocked(serializer, mGuestRestrictions);
serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
for (int i = 0; i < mUsers.size(); i++) {
@@ -855,6 +858,8 @@ public class UserManagerService extends IUserManager.Stub {
writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_SMS);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_BEAM);
serializer.endTag(null, TAG_RESTRICTIONS);
}
@@ -871,6 +876,7 @@ public class UserManagerService extends IUserManager.Stub {
int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
long lastAttemptTime = 0L;
boolean partial = false;
+ boolean guestToRemove = false;
Bundle restrictions = new Bundle();
FileInputStream fis = null;
@@ -918,6 +924,10 @@ public class UserManagerService extends IUserManager.Stub {
if ("true".equals(valueString)) {
partial = true;
}
+ valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE);
+ if ("true".equals(valueString)) {
+ guestToRemove = true;
+ }
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -942,6 +952,7 @@ public class UserManagerService extends IUserManager.Stub {
userInfo.creationTime = creationTime;
userInfo.lastLoggedInTime = lastLoggedInTime;
userInfo.partial = partial;
+ userInfo.guestToRemove = guestToRemove;
userInfo.profileGroupId = profileGroupId;
mUserRestrictions.append(id, restrictions);
if (salt != 0L) {
@@ -999,6 +1010,8 @@ public class UserManagerService extends IUserManager.Stub {
readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
readBoolean(parser, restrictions, UserManager.DISALLOW_SMS);
readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_BEAM);
}
private void readBoolean(XmlPullParser parser, Bundle restrictions,
@@ -1119,7 +1132,7 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
// If we're adding a guest and there already exists one, bail.
- if (isGuest && numberOfUsersOfTypeLocked(UserInfo.FLAG_GUEST, true) > 0) {
+ if (isGuest && findCurrentGuestUserLocked() != null) {
return null;
}
// Limit number of managed profiles that can be created
@@ -1180,6 +1193,23 @@ public class UserManagerService extends IUserManager.Stub {
}
/**
+ * Find the current guest user. If the Guest user is partial,
+ * then do not include it in the results as it is about to die.
+ * This is different than {@link #numberOfUsersOfTypeLocked(int, boolean)} due to
+ * the special handling of Guests being removed.
+ */
+ private UserInfo findCurrentGuestUserLocked() {
+ final int size = mUsers.size();
+ for (int i = 0; i < size; i++) {
+ final UserInfo user = mUsers.valueAt(i);
+ if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) {
+ return user;
+ }
+ }
+ return null;
+ }
+
+ /**
* Mark this guest user for deletion to allow us to create another guest
* and switch to that user before actually removing this guest.
* @param userHandle the userid of the current guest
@@ -1204,14 +1234,15 @@ public class UserManagerService extends IUserManager.Stub {
if (!user.isGuest()) {
return false;
}
- // Set this to a partially created user, so that the user will be purged
- // on next startup, in case the runtime stops now before stopping and
- // removing the user completely.
- user.partial = true;
+ // We set this to a guest user that is to be removed. This is a temporary state
+ // where we are allowed to add new Guest users, even if this one is still not
+ // removed. This user will still show up in getUserInfo() calls.
+ // If we don't get around to removing this Guest user, it will be purged on next
+ // startup.
+ user.guestToRemove = true;
// Mark it as disabled, so that it isn't returned any more when
// profiles are queried.
user.flags |= UserInfo.FLAG_DISABLED;
- user.flags &= ~UserInfo.FLAG_GUEST;
writeUserLocked(user);
}
} finally {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 5f97a00..7808800 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -91,8 +91,6 @@ public final class PowerManagerService extends com.android.server.SystemService
private static final int MSG_SANDMAN = 2;
// Message: Sent when the screen on blocker is released.
private static final int MSG_SCREEN_ON_BLOCKER_RELEASED = 3;
- // Message: Sent to poll whether the boot animation has terminated.
- private static final int MSG_CHECK_IF_BOOT_ANIMATION_FINISHED = 4;
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
@@ -154,12 +152,6 @@ public final class PowerManagerService extends com.android.server.SystemService
// provider populates the actual default value (R.integer.def_screen_off_timeout).
private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000;
- // The name of the boot animation service in init.rc.
- private static final String BOOT_ANIMATION_SERVICE = "bootanim";
-
- // Poll interval in milliseconds for watching boot animation finished.
- private static final int BOOT_ANIMATION_POLL_INTERVAL = 200;
-
// Power hints defined in hardware/libhardware/include/hardware/power.h.
private static final int POWER_HINT_INTERACTION = 2;
private static final int POWER_HINT_LOW_POWER = 5;
@@ -478,14 +470,15 @@ public final class PowerManagerService extends com.android.server.SystemService
@Override
public void onBootPhase(int phase) {
- if (phase == PHASE_BOOT_COMPLETED) {
- // This is our early signal that the system thinks it has finished booting.
- // However, the boot animation may still be running for a few more seconds
- // since it is ultimately in charge of when it terminates.
- // Defer transitioning into the boot completed state until the animation exits.
- // We do this so that the screen does not start to dim prematurely before
- // the user has actually had a chance to interact with the device.
- startWatchingForBootAnimationFinished();
+ synchronized (mLock) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ final long now = SystemClock.uptimeMillis();
+ mBootCompleted = true;
+ mDirty |= DIRTY_BOOT_COMPLETED;
+ userActivityNoUpdateLocked(
+ now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+ updatePowerStateLocked();
+ }
}
}
@@ -2076,38 +2069,6 @@ public final class PowerManagerService extends com.android.server.SystemService
updatePowerStateLocked();
}
- private void startWatchingForBootAnimationFinished() {
- mHandler.sendEmptyMessage(MSG_CHECK_IF_BOOT_ANIMATION_FINISHED);
- }
-
- private void checkIfBootAnimationFinished() {
- if (DEBUG) {
- Slog.d(TAG, "Check if boot animation finished...");
- }
-
- if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) {
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_IF_BOOT_ANIMATION_FINISHED,
- BOOT_ANIMATION_POLL_INTERVAL);
- return;
- }
-
- synchronized (mLock) {
- if (!mBootCompleted) {
- Slog.i(TAG, "Boot animation finished.");
- handleBootCompletedLocked();
- }
- }
- }
-
- private void handleBootCompletedLocked() {
- final long now = SystemClock.uptimeMillis();
- mBootCompleted = true;
- mDirty |= DIRTY_BOOT_COMPLETED;
- userActivityNoUpdateLocked(
- now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
- updatePowerStateLocked();
- }
-
private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
final String reason, boolean wait) {
if (mHandler == null || !mSystemReady) {
@@ -2528,9 +2489,6 @@ public final class PowerManagerService extends com.android.server.SystemService
case MSG_SCREEN_ON_BLOCKER_RELEASED:
handleScreenOnBlockerReleased();
break;
- case MSG_CHECK_IF_BOOT_ANIMATION_FINISHED:
- checkIfBootAnimationFinished();
- break;
}
}
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index c8b5b3e..fefbe0a 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -33,6 +33,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
@@ -48,6 +49,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -97,6 +99,7 @@ public class TrustManagerService extends SystemService {
private final SparseBooleanArray mUserHasAuthenticatedSinceBoot = new SparseBooleanArray();
/* package */ final TrustArchive mArchive = new TrustArchive();
private final Context mContext;
+ private final LockPatternUtils mLockPatternUtils;
private UserManager mUserManager;
@@ -104,6 +107,7 @@ public class TrustManagerService extends SystemService {
super(context);
mContext = context;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mLockPatternUtils = new LockPatternUtils(context);
}
@Override
@@ -116,6 +120,7 @@ public class TrustManagerService extends SystemService {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY && !isSafeMode()) {
mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
mReceiver.register(mContext);
+ maybeEnableFactoryTrustAgents(mLockPatternUtils, UserHandle.USER_OWNER);
refreshAgentList(UserHandle.USER_ALL);
}
}
@@ -159,6 +164,11 @@ public class TrustManagerService extends SystemService {
void refreshAgentList(int userId) {
if (DEBUG) Slog.d(TAG, "refreshAgentList()");
+ if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_OWNER) {
+ Log.e(TAG, "refreshAgentList(userId=" + userId + "): Invalid user handle,"
+ + " must be USER_ALL or a specific user.", new Throwable("here"));
+ userId = UserHandle.USER_ALL;
+ }
PackageManager pm = mContext.getPackageManager();
List<UserInfo> userInfos;
@@ -168,12 +178,13 @@ public class TrustManagerService extends SystemService {
userInfos = new ArrayList<>();
userInfos.add(mUserManager.getUserInfo(userId));
}
- LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+ LockPatternUtils lockPatternUtils = mLockPatternUtils;
ArraySet<AgentInfo> obsoleteAgents = new ArraySet<>();
obsoleteAgents.addAll(mActiveAgents);
for (UserInfo userInfo : userInfos) {
+ if (!userInfo.supportsSwitchTo()) continue;
if (lockPatternUtils.getKeyguardStoredPasswordQuality(userInfo.id)
== DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) continue;
if (!mUserHasAuthenticatedSinceBoot.get(userInfo.id)) continue;
@@ -186,22 +197,11 @@ public class TrustManagerService extends SystemService {
if (enabledAgents == null) {
continue;
}
- List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
- PackageManager.GET_META_DATA, userInfo.id);
+ List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userInfo.id);
for (ResolveInfo resolveInfo : resolveInfos) {
- if (resolveInfo.serviceInfo == null) continue;
-
- String packageName = resolveInfo.serviceInfo.packageName;
- if (pm.checkPermission(PERMISSION_PROVIDE_AGENT, packageName)
- != PackageManager.PERMISSION_GRANTED) {
- Log.w(TAG, "Skipping agent because package " + packageName
- + " does not have permission " + PERMISSION_PROVIDE_AGENT + ".");
- continue;
- }
-
ComponentName name = getComponentName(resolveInfo);
- if (!enabledAgents.contains(name)) continue;
+ if (!enabledAgents.contains(name)) continue;
if (disableTrustAgents) {
List<String> features =
dpm.getTrustAgentFeaturesEnabled(null /* admin */, name);
@@ -228,11 +228,13 @@ public class TrustManagerService extends SystemService {
boolean trustMayHaveChanged = false;
for (int i = 0; i < obsoleteAgents.size(); i++) {
AgentInfo info = obsoleteAgents.valueAt(i);
- if (info.agent.isManagingTrust()) {
- trustMayHaveChanged = true;
+ if (userId == UserHandle.USER_ALL || userId == info.userId) {
+ if (info.agent.isManagingTrust()) {
+ trustMayHaveChanged = true;
+ }
+ info.agent.unbind();
+ mActiveAgents.remove(info);
}
- info.agent.unbind();
- mActiveAgents.remove(info);
}
if (trustMayHaveChanged) {
@@ -342,6 +344,54 @@ public class TrustManagerService extends SystemService {
return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
}
+ private void maybeEnableFactoryTrustAgents(LockPatternUtils utils, int userId) {
+ if (0 != Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TRUST_AGENTS_INITIALIZED, 0, userId)) {
+ return;
+ }
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userId);
+ ArraySet<ComponentName> discoveredAgents = new ArraySet<>();
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ ComponentName componentName = getComponentName(resolveInfo);
+ int applicationInfoFlags = resolveInfo.serviceInfo.applicationInfo.flags;
+ if ((applicationInfoFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Log.i(TAG, "Leaving agent " + componentName + " disabled because package "
+ + "is not a system package.");
+ continue;
+ }
+ discoveredAgents.add(componentName);
+ }
+
+ List<ComponentName> previouslyEnabledAgents = utils.getEnabledTrustAgents(userId);
+ if (previouslyEnabledAgents != null) {
+ discoveredAgents.addAll(previouslyEnabledAgents);
+ }
+ utils.setEnabledTrustAgents(discoveredAgents, userId);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, userId);
+ }
+
+ private List<ResolveInfo> resolveAllowedTrustAgents(PackageManager pm, int userId) {
+ List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
+ 0 /* flags */, userId);
+ ArrayList<ResolveInfo> allowedAgents = new ArrayList<>(resolveInfos.size());
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ if (resolveInfo.serviceInfo == null) continue;
+ if (resolveInfo.serviceInfo.applicationInfo == null) continue;
+ String packageName = resolveInfo.serviceInfo.packageName;
+ if (pm.checkPermission(PERMISSION_PROVIDE_AGENT, packageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ ComponentName name = getComponentName(resolveInfo);
+ Log.w(TAG, "Skipping agent " + name + " because package does not have"
+ + " permission " + PERMISSION_PROVIDE_AGENT + ".");
+ continue;
+ }
+ allowedAgents.add(resolveInfo);
+ }
+ return allowedAgents;
+ }
+
// Agent dispatch and aggregation
private boolean aggregateIsTrusted(int userId) {
@@ -414,6 +464,7 @@ public class TrustManagerService extends SystemService {
}
}
mTrustListeners.add(listener);
+ updateTrustAll();
}
private void removeListener(ITrustListener listener) {
@@ -616,12 +667,19 @@ public class TrustManagerService extends SystemService {
@Override
public void onReceive(Context context, Intent intent) {
- if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
- intent.getAction())) {
+ String action = intent.getAction();
+ if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
refreshAgentList(getSendingUserId());
updateDevicePolicyFeatures();
- } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
+ } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
updateUserHasAuthenticated(getSendingUserId());
+ } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -100);
+ if (userId > 0) {
+ maybeEnableFactoryTrustAgents(mLockPatternUtils, userId);
+ } else {
+ Log.wtf(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
+ }
}
}
@@ -629,6 +687,7 @@ public class TrustManagerService extends SystemService {
IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_USER_ADDED);
context.registerReceiverAsUser(this,
UserHandle.ALL,
filter,
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 3380f71..adae84f 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -85,6 +85,7 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -139,7 +140,7 @@ public final class TvInputManagerService extends SystemService {
registerBroadcastReceivers();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mLock) {
- buildTvInputListLocked(mCurrentUserId);
+ buildTvInputListLocked(mCurrentUserId, null);
buildTvContentRatingSystemListLocked(mCurrentUserId);
}
}
@@ -148,19 +149,64 @@ public final class TvInputManagerService extends SystemService {
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
+ private void buildTvInputList(String[] packages) {
+ synchronized (mLock) {
+ buildTvInputListLocked(getChangingUserId(), packages);
+ buildTvContentRatingSystemListLocked(getChangingUserId());
+ }
+ }
+
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
+ // This callback is invoked when the TV input is reinstalled.
+ // In this case, isReplacing() always returns true.
+ buildTvInputList(new String[] { packageName });
+ }
+
+ @Override
+ public void onPackagesAvailable(String[] packages) {
+ if (DEBUG) {
+ Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
+ }
+ // This callback is invoked when the media on which some packages exist become
+ // available.
+ if (isReplacing()) {
+ buildTvInputList(packages);
+ }
+ }
+
+ @Override
+ public void onPackagesUnavailable(String[] packages) {
+ // This callback is invoked when the media on which some packages exist become
+ // unavailable.
+ if (DEBUG) {
+ Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
+ + ")");
+ }
+ if (isReplacing()) {
+ buildTvInputList(packages);
+ }
+ }
+
@Override
public void onSomePackagesChanged() {
+ // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
+ // the TV inputs.
if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
- synchronized (mLock) {
- buildTvInputListLocked(mCurrentUserId);
- buildTvContentRatingSystemListLocked(mCurrentUserId);
+ if (isReplacing()) {
+ if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
+ // When the package is updated, buildTvInputListLocked is called in other
+ // methods instead.
+ return;
}
+ buildTvInputList(null);
}
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
- UserState userState = getUserStateLocked(mCurrentUserId);
+ UserState userState = getUserStateLocked(getChangingUserId());
if (!userState.packageSet.contains(packageName)) {
// Not a TV input package.
return;
@@ -218,13 +264,11 @@ public final class TvInputManagerService extends SystemService {
component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
}
- private void buildTvInputListLocked(int userId) {
+ private void buildTvInputListLocked(int userId, String[] updatedPackages) {
UserState userState = getUserStateLocked(userId);
userState.packageSet.clear();
- if (DEBUG) {
- Slog.d(TAG, "buildTvInputList");
- }
+ if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServices(
new Intent(TvInputService.SERVICE_INTERFACE),
@@ -278,6 +322,15 @@ public final class TvInputManagerService extends SystemService {
for (String inputId : inputMap.keySet()) {
if (!userState.inputMap.containsKey(inputId)) {
notifyInputAddedLocked(userState, inputId);
+ } else if (updatedPackages != null) {
+ // Notify the package updates
+ TvInputState inputState = inputMap.get(inputId);
+ for (String updatedPackage : updatedPackages) {
+ if (inputState.info.getComponent().getPackageName().equals(updatedPackage)) {
+ notifyInputUpdatedLocked(userState, inputId);
+ break;
+ }
+ }
}
}
@@ -337,7 +390,7 @@ public final class TvInputManagerService extends SystemService {
userState = new UserState(mContext, userId);
}
mUserStates.put(userId, userState);
- buildTvInputListLocked(userId);
+ buildTvInputListLocked(userId, null);
buildTvContentRatingSystemListLocked(userId);
}
}
@@ -649,6 +702,19 @@ public final class TvInputManagerService extends SystemService {
}
}
+ private void notifyInputUpdatedLocked(UserState userState, String inputId) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")");
+ }
+ for (ITvInputManagerCallback callback : userState.callbackSet) {
+ try {
+ callback.onInputUpdated(inputId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "failed to report updated input to callback", e);
+ }
+ }
+ }
+
private void notifyInputStateChangedLocked(UserState userState, String inputId,
int state, ITvInputManagerCallback targetCallback) {
if (DEBUG) {
@@ -1813,7 +1879,7 @@ public final class TvInputManagerService extends SystemService {
private void addTvInputLocked(TvInputInfo inputInfo) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
serviceState.inputList.add(inputInfo);
- buildTvInputListLocked(mUserId);
+ buildTvInputListLocked(mUserId, null);
}
@Override
@@ -1851,7 +1917,7 @@ public final class TvInputManagerService extends SystemService {
}
}
if (removed) {
- buildTvInputListLocked(mUserId);
+ buildTvInputListLocked(mUserId, null);
mTvInputHardwareManager.removeTvInput(inputId);
} else {
Slog.e(TAG, "failed to remove input " + inputId);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3eb2d5f..e1ade63 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -42,6 +42,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -85,6 +86,7 @@ import org.xmlpull.v1.XmlSerializer;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
+import com.android.internal.R;
public class WallpaperManagerService extends IWallpaperManager.Stub {
static final String TAG = "WallpaperManagerService";
@@ -99,12 +101,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
static final long MIN_WALLPAPER_CRASH_TIME = 10000;
static final String WALLPAPER = "wallpaper";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
- /**
- * Name of the component used to display bitmap wallpapers from either the gallery or
- * built-in wallpapers.
- */
- static final ComponentName IMAGE_WALLPAPER = new ComponentName("com.android.systemui",
- "com.android.systemui.ImageWallpaper");
/**
* Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
@@ -146,7 +142,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
if (event == CLOSE_WRITE) {
mWallpaper.imageWallpaperPending = false;
}
- bindWallpaperComponentLocked(IMAGE_WALLPAPER, true,
+ bindWallpaperComponentLocked(mImageWallpaper, true,
false, mWallpaper, null);
saveSettingsLocked(mWallpaper);
}
@@ -161,6 +157,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
final MyPackageMonitor mMonitor;
WallpaperData mLastWallpaper;
+ /**
+ * Name of the component used to display bitmap wallpapers from either the gallery or
+ * built-in wallpapers.
+ */
+ final ComponentName mImageWallpaper;
+
SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
int mCurrentUserId;
@@ -205,6 +207,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
int width = -1;
int height = -1;
+ final Rect padding = new Rect(0, 0, 0, 0);
+
WallpaperData(int userId) {
this.userId = userId;
wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
@@ -221,6 +225,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
IRemoteCallback mReply;
boolean mDimensionsChanged = false;
+ boolean mPaddingChanged = false;
public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
mInfo = info;
@@ -282,6 +287,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
mDimensionsChanged = false;
}
+ if (mPaddingChanged) {
+ try {
+ mEngine.setDisplayPadding(mWallpaper.padding);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to set wallpaper padding", e);
+ }
+ mPaddingChanged = false;
+ }
}
}
@@ -447,6 +460,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
public WallpaperManagerService(Context context) {
if (DEBUG) Slog.v(TAG, "WallpaperService startup");
mContext = context;
+ mImageWallpaper = ComponentName.unflattenFromString(
+ context.getResources().getString(R.string.image_wallpaper_component));
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mIPackageManager = AppGlobals.getPackageManager();
@@ -599,33 +614,35 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
f.delete();
}
final long ident = Binder.clearCallingIdentity();
- RuntimeException e = null;
try {
- wallpaper.imageWallpaperPending = false;
- if (userId != mCurrentUserId) return;
- if (bindWallpaperComponentLocked(defaultFailed
- ? IMAGE_WALLPAPER
- : null, true, false, wallpaper, reply)) {
- return;
+ RuntimeException e = null;
+ try {
+ wallpaper.imageWallpaperPending = false;
+ if (userId != mCurrentUserId) return;
+ if (bindWallpaperComponentLocked(defaultFailed
+ ? mImageWallpaper
+ : null, true, false, wallpaper, reply)) {
+ return;
+ }
+ } catch (IllegalArgumentException e1) {
+ e = e1;
+ }
+
+ // This can happen if the default wallpaper component doesn't
+ // exist. This should be a system configuration problem, but
+ // let's not let it crash the system and just live with no
+ // wallpaper.
+ Slog.e(TAG, "Default wallpaper component not found!", e);
+ clearWallpaperComponentLocked(wallpaper);
+ if (reply != null) {
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e1) {
+ }
}
- } catch (IllegalArgumentException e1) {
- e = e1;
} finally {
Binder.restoreCallingIdentity(ident);
}
-
- // This can happen if the default wallpaper component doesn't
- // exist. This should be a system configuration problem, but
- // let's not let it crash the system and just live with no
- // wallpaper.
- Slog.e(TAG, "Default wallpaper component not found!", e);
- clearWallpaperComponentLocked(wallpaper);
- if (reply != null) {
- try {
- reply.sendResult(null);
- } catch (RemoteException e1) {
- }
- }
}
public boolean hasNamedWallpaper(String name) {
@@ -714,6 +731,40 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ public void setDisplayPadding(Rect padding) {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
+ synchronized (mLock) {
+ int userId = UserHandle.getCallingUserId();
+ WallpaperData wallpaper = mWallpaperMap.get(userId);
+ if (wallpaper == null) {
+ throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+ }
+ if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
+ throw new IllegalArgumentException("padding must be positive: " + padding);
+ }
+
+ if (!padding.equals(wallpaper.padding)) {
+ wallpaper.padding.set(padding);
+ saveSettingsLocked(wallpaper);
+ if (mCurrentUserId != userId) return; // Don't change the properties now
+ if (wallpaper.connection != null) {
+ if (wallpaper.connection.mEngine != null) {
+ try {
+ wallpaper.connection.mEngine.setDisplayPadding(padding);
+ } catch (RemoteException e) {
+ }
+ notifyCallbacksLocked(wallpaper);
+ } else if (wallpaper.connection.mService != null) {
+ // We've attached to the service but the engine hasn't attached back to us
+ // yet. This means it will be created with the previous dimensions, so we
+ // need to update it to the new dimensions once it attaches.
+ wallpaper.connection.mPaddingChanged = true;
+ }
+ }
+ }
+ }
+ }
+
public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
Bundle outParams) {
synchronized (mLock) {
@@ -848,7 +899,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
if (componentName == null) {
// Fall back to static image wallpaper
- componentName = IMAGE_WALLPAPER;
+ componentName = mImageWallpaper;
//clearWallpaperComponentLocked();
//return;
if (DEBUG) Slog.v(TAG, "Using image wallpaper");
@@ -876,7 +927,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
WallpaperInfo wi = null;
Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
- if (componentName != null && !componentName.equals(IMAGE_WALLPAPER)) {
+ if (componentName != null && !componentName.equals(mImageWallpaper)) {
// Make sure the selected service is actually a wallpaper service.
List<ResolveInfo> ris =
mIPackageManager.queryIntentServices(intent,
@@ -1001,7 +1052,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
try {
conn.mService.attach(conn, conn.mToken,
WindowManager.LayoutParams.TYPE_WALLPAPER, false,
- wallpaper.width, wallpaper.height);
+ wallpaper.width, wallpaper.height, wallpaper.padding);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
if (!wallpaper.wallpaperUpdating) {
@@ -1050,9 +1101,21 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
out.startTag(null, "wp");
out.attribute(null, "width", Integer.toString(wallpaper.width));
out.attribute(null, "height", Integer.toString(wallpaper.height));
+ if (wallpaper.padding.left != 0) {
+ out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+ }
+ if (wallpaper.padding.top != 0) {
+ out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+ }
+ if (wallpaper.padding.right != 0) {
+ out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
+ }
+ if (wallpaper.padding.bottom != 0) {
+ out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
+ }
out.attribute(null, "name", wallpaper.name);
if (wallpaper.wallpaperComponent != null
- && !wallpaper.wallpaperComponent.equals(IMAGE_WALLPAPER)) {
+ && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
out.attribute(null, "component",
wallpaper.wallpaperComponent.flattenToShortString());
}
@@ -1086,6 +1149,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
+ String value = parser.getAttributeValue(null, name);
+ if (value == null) {
+ return defValue;
+ }
+ return Integer.parseInt(value);
+ }
+
private void loadSettingsLocked(int userId) {
if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
@@ -1116,6 +1187,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
wallpaper.height = Integer.parseInt(parser
.getAttributeValue(null, "height"));
+ wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
+ wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
+ wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
+ wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
wallpaper.name = parser.getAttributeValue(null, "name");
String comp = parser.getAttributeValue(null, "component");
wallpaper.nextWallpaperComponent = comp != null
@@ -1124,7 +1199,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
if (wallpaper.nextWallpaperComponent == null
|| "android".equals(wallpaper.nextWallpaperComponent
.getPackageName())) {
- wallpaper.nextWallpaperComponent = IMAGE_WALLPAPER;
+ wallpaper.nextWallpaperComponent = mImageWallpaper;
}
if (DEBUG) {
@@ -1162,6 +1237,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
if (!success) {
wallpaper.width = -1;
wallpaper.height = -1;
+ wallpaper.padding.set(0, 0, 0, 0);
wallpaper.name = "";
}
@@ -1196,7 +1272,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
loadSettingsLocked(0);
wallpaper = mWallpaperMap.get(0);
if (wallpaper.nextWallpaperComponent != null
- && !wallpaper.nextWallpaperComponent.equals(IMAGE_WALLPAPER)) {
+ && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
wallpaper, null)) {
// No such live wallpaper or other failure; fall back to the default
@@ -1325,13 +1401,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
WallpaperData wallpaper = mWallpaperMap.valueAt(i);
pw.println(" User " + wallpaper.userId + ":");
pw.print(" mWidth=");
- pw.print(wallpaper.width);
- pw.print(" mHeight=");
- pw.println(wallpaper.height);
- pw.print(" mName=");
- pw.println(wallpaper.name);
- pw.print(" mWallpaperComponent=");
- pw.println(wallpaper.wallpaperComponent);
+ pw.print(wallpaper.width);
+ pw.print(" mHeight=");
+ pw.println(wallpaper.height);
+ pw.print(" mPadding="); pw.println(wallpaper.padding);
+ pw.print(" mName="); pw.println(wallpaper.name);
+ pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
if (wallpaper.connection != null) {
WallpaperConnection conn = wallpaper.connection;
pw.print(" Wallpaper connection ");
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index aabb8f7..bfc7659 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -569,9 +569,9 @@ public class AppTransition implements Dump {
int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
Rect contentInsets, boolean isFullScreen) {
Animation a;
- final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ final int thumbWidthI = mNextAppTransitionStartWidth;
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final int thumbHeightI = mNextAppTransitionStartHeight;
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
// Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
@@ -993,7 +993,7 @@ public class AppTransition implements Dump {
}
void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
- IRemoteCallback startedCallback, boolean scaleUp) {
+ int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
if (isTransitionSet()) {
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1002,6 +1002,8 @@ public class AppTransition implements Dump {
mNextAppTransitionScaleUp = scaleUp;
mNextAppTransitionStartX = startX;
mNextAppTransitionStartY = startY;
+ mNextAppTransitionStartWidth = targetWidth;
+ mNextAppTransitionStartHeight = targetHeight;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
} else {
@@ -1138,6 +1140,10 @@ public class AppTransition implements Dump {
pw.print(mNextAppTransitionStartX);
pw.print(" mNextAppTransitionStartY=");
pw.println(mNextAppTransitionStartY);
+ pw.print(" mNextAppTransitionStartWidth=");
+ pw.print(mNextAppTransitionStartWidth);
+ pw.print(" mNextAppTransitionStartHeight=");
+ pw.println(mNextAppTransitionStartHeight);
pw.print(" mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp);
break;
}
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 64b852b..a7d41fa 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -63,8 +63,14 @@ class CircularDisplayMask {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl(session, "CircularDisplayMask",
- mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ if (WindowManagerService.DEBUG_SURFACE_TRACE) {
+ ctrl = new WindowStateAnimator.SurfaceTrace(session, "CircularDisplayMask",
+ mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT,
+ SurfaceControl.HIDDEN);
+ } else {
+ ctrl = new SurfaceControl(session, "CircularDisplayMask", mScreenSize.x,
+ mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ }
ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(zOrder);
ctrl.setPosition(0, 0);
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
new file mode 100644
index 0000000..62f2b48
--- /dev/null
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 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.wm;
+
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+class EmulatorDisplayOverlay {
+ private static final String TAG = "EmulatorDisplayOverlay";
+
+ // Display dimensions
+ private Point mScreenSize;
+
+ private final SurfaceControl mSurfaceControl;
+ private final Surface mSurface = new Surface();
+ private int mLastDW;
+ private int mLastDH;
+ private boolean mDrawNeeded;
+ private Drawable mOverlay;
+ private int mRotation;
+ private boolean mVisible;
+
+ public EmulatorDisplayOverlay(Context context, Display display, SurfaceSession session,
+ int zOrder) {
+ mScreenSize = new Point();
+ display.getSize(mScreenSize);
+
+ SurfaceControl ctrl = null;
+ try {
+ if (WindowManagerService.DEBUG_SURFACE_TRACE) {
+ ctrl = new WindowStateAnimator.SurfaceTrace(session, "EmulatorDisplayOverlay",
+ mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT,
+ SurfaceControl.HIDDEN);
+ } else {
+ ctrl = new SurfaceControl(session, "EmulatorDisplayOverlay", mScreenSize.x,
+ mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ }
+ ctrl.setLayerStack(display.getLayerStack());
+ ctrl.setLayer(zOrder);
+ ctrl.setPosition(0, 0);
+ ctrl.show();
+ mSurface.copyFrom(ctrl);
+ } catch (OutOfResourcesException e) {
+ }
+ mSurfaceControl = ctrl;
+ mDrawNeeded = true;
+ mOverlay = context.getDrawable(
+ com.android.internal.R.drawable.emulator_circular_window_overlay);
+ }
+
+ private void drawIfNeeded() {
+ if (!mDrawNeeded || !mVisible) {
+ return;
+ }
+ mDrawNeeded = false;
+
+ Rect dirty = new Rect(0, 0, mScreenSize.x, mScreenSize.y);
+ Canvas c = null;
+ try {
+ c = mSurface.lockCanvas(dirty);
+ } catch (IllegalArgumentException e) {
+ } catch (OutOfResourcesException e) {
+ }
+ if (c == null) {
+ return;
+ }
+ c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
+ mSurfaceControl.setPosition(0, 0);
+ mOverlay.setBounds(0, 0, mScreenSize.x, mScreenSize.y);
+ mOverlay.draw(c);
+ mSurface.unlockCanvasAndPost(c);
+ }
+
+ // Note: caller responsible for being inside
+ // Surface.openTransaction() / closeTransaction()
+ public void setVisibility(boolean on) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ mVisible = on;
+ drawIfNeeded();
+ if (on) {
+ mSurfaceControl.show();
+ } else {
+ mSurfaceControl.hide();
+ }
+ }
+
+ void positionSurface(int dw, int dh, int rotation) {
+ if (mLastDW == dw && mLastDH == dh && mRotation == rotation) {
+ return;
+ }
+ mLastDW = dw;
+ mLastDH = dh;
+ mDrawNeeded = true;
+ mRotation = rotation;
+ drawIfNeeded();
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f02c0e6..46aefb6 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -352,6 +352,12 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
}
+ /* Notifies that the camera lens cover state has changed. */
+ @Override
+ public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
+ mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
+ }
+
/* Provides an opportunity for the window manager policy to intercept early key
* processing as soon as the key has been read from the device. */
@Override
@@ -359,12 +365,13 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}
- /* Provides an opportunity for the window manager policy to intercept early
- * motion event processing when the screen is off since these events are normally
+ /* Provides an opportunity for the window manager policy to intercept early motion event
+ * processing when the device is in a non-interactive state since these events are normally
* dropped. */
@Override
- public int interceptWakeMotionBeforeQueueing(long whenNanos, int policyFlags) {
- return mService.mPolicy.interceptWakeMotionBeforeQueueing(whenNanos, policyFlags);
+ public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
+ return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
+ whenNanos, policyFlags);
}
/* Provides an opportunity for the window manager policy to process a key before
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index f2703ad..d737e7f 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -415,6 +415,18 @@ final class Session extends IWindowSession.Stub
mService.wallpaperOffsetsComplete(window);
}
+ public void setWallpaperDisplayOffset(IBinder window, int x, int y) {
+ synchronized(mService.mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mService.setWindowWallpaperDisplayOffsetLocked(
+ mService.windowForClientLocked(this, window, true), x, y);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, Bundle extras, boolean sync) {
synchronized(mService.mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 08343d8..54af851 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -20,6 +20,8 @@ import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import android.app.AppOpsManager;
+import android.os.Build;
+import android.os.SystemService;
import android.util.ArraySet;
import android.util.TimeUtils;
import android.view.IWindowId;
@@ -271,6 +273,12 @@ public class WindowManagerService extends IWindowManager.Stub
// Default input dispatching timeout in nanoseconds.
static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
+ // Poll interval in milliseconds for watching boot animation finished.
+ private static final int BOOT_ANIMATION_POLL_INTERVAL = 200;
+
+ // The name of the boot animation service in init.rc.
+ private static final String BOOT_ANIMATION_SERVICE = "bootanim";
+
/** Minimum value for attachStack and resizeStack weight value */
public static final float STACK_WEIGHT_MIN = 0.2f;
@@ -295,6 +303,8 @@ public class WindowManagerService extends IWindowManager.Stub
private static final int SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
+
final private KeyguardDisableHandler mKeyguardDisableHandler;
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -441,6 +451,7 @@ public class WindowManagerService extends IWindowManager.Stub
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
CircularDisplayMask mCircularDisplayMask;
+ EmulatorDisplayOverlay mEmulatorDisplayOverlay;
FocusedStackFrame mFocusedStackFrame;
int mFocusedStackLayer;
@@ -454,6 +465,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mSystemBooted = false;
boolean mForceDisplayEnabled = false;
boolean mShowingBootMessages = false;
+ boolean mBootAnimationStopped = false;
String mLastANRState;
@@ -572,6 +584,8 @@ public class WindowManagerService extends IWindowManager.Stub
float mLastWallpaperY = -1;
float mLastWallpaperXStep = -1;
float mLastWallpaperYStep = -1;
+ int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
+ int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
// This is set when we are waiting for a wallpaper to tell us it is done
// changing its scroll position.
WindowState mWaitingOnWallpaper;
@@ -879,6 +893,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
showCircularDisplayMaskIfNeeded();
+ showEmulatorDisplayOverlayIfNeeded();
}
public InputMonitor getInputMonitor() {
@@ -1892,6 +1907,12 @@ public class WindowManagerService extends IWindowManager.Stub
mLastWallpaperY = mWallpaperTarget.mWallpaperY;
mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
}
+ if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
+ }
+ if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
+ }
}
// Start stepping backwards from here, ensuring that our wallpaper windows
@@ -2030,6 +2051,9 @@ public class WindowManagerService extends IWindowManager.Stub
float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw;
int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0;
+ if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ offset += mLastWallpaperDisplayOffsetX;
+ }
changed = wallpaperWin.mXOffset != offset;
if (changed) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper "
@@ -2046,6 +2070,9 @@ public class WindowManagerService extends IWindowManager.Stub
float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh;
offset = availh > 0 ? -(int)(availh*wpy+.5f) : 0;
+ if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ offset += mLastWallpaperDisplayOffsetY;
+ }
if (wallpaperWin.mYOffset != offset) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper "
+ wallpaperWin + " y: " + offset);
@@ -2130,6 +2157,16 @@ public class WindowManagerService extends IWindowManager.Stub
} else if (changingTarget.mWallpaperY >= 0) {
mLastWallpaperY = changingTarget.mWallpaperY;
}
+ if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
+ } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX;
+ }
+ if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY;
+ } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
+ }
}
int curTokenIndex = mWallpaperTokens.size();
@@ -2826,6 +2863,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ public void setWindowWallpaperDisplayOffsetLocked(WindowState window, int x, int y) {
+ if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) {
+ window.mWallpaperDisplayOffsetX = x;
+ window.mWallpaperDisplayOffsetY = y;
+ updateWallpaperOffsetLocked(window, true);
+ }
+ }
+
public Bundle sendWindowWallpaperCommandLocked(WindowState window,
String action, int x, int y, int z, Bundle extras, boolean sync) {
if (window == mWallpaperTarget || window == mLowerWallpaperTarget
@@ -4031,10 +4076,11 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX,
- int startY, IRemoteCallback startedCallback, boolean scaleUp) {
+ int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+ boolean scaleUp) {
synchronized(mWindowMap) {
mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX,
- startY, startedCallback, scaleUp);
+ startY, targetWidth, targetHeight, startedCallback, scaleUp);
}
}
@@ -4303,20 +4349,28 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void setAppFullscreen(IBinder token, boolean toOpaque) {
- AppWindowToken atoken = findAppWindowToken(token);
- if (atoken != null) {
- atoken.appFullscreen = toOpaque;
- setWindowOpaque(token, toOpaque);
- requestTraversal();
+ synchronized (mWindowMap) {
+ AppWindowToken atoken = findAppWindowToken(token);
+ if (atoken != null) {
+ atoken.appFullscreen = toOpaque;
+ setWindowOpaqueLocked(token, toOpaque);
+ requestTraversalLocked();
+ }
}
}
public void setWindowOpaque(IBinder token, boolean isOpaque) {
+ synchronized (mWindowMap) {
+ setWindowOpaqueLocked(token, isOpaque);
+ }
+ }
+
+ public void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
AppWindowToken wtoken = findAppWindowToken(token);
if (wtoken != null) {
WindowState win = wtoken.findMainWindow();
if (win != null) {
- win.mWinAnimator.setOpaque(isOpaque);
+ win.mWinAnimator.setOpaqueLocked(isOpaque);
}
}
}
@@ -5432,6 +5486,23 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ // Called by window manager policy. Not exposed externally.
+ @Override
+ public int getCameraLensCoverState() {
+ int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY,
+ InputManagerService.SW_CAMERA_LENS_COVER);
+ if (sw > 0) {
+ // Switch state: AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL.
+ return CAMERA_LENS_COVERED;
+ } else if (sw == 0) {
+ // Switch state: AKEY_STATE_UP.
+ return CAMERA_LENS_UNCOVERED;
+ } else {
+ // Switch state: AKEY_STATE_UNKNOWN.
+ return CAMERA_LENS_COVER_ABSENT;
+ }
+ }
+
// Called by window manager policy. Not exposed externally.
@Override
public void switchKeyboardLayout(int deviceId, int direction) {
@@ -5544,17 +5615,70 @@ public class WindowManagerService extends IWindowManager.Stub
performEnableScreen();
}
+ private boolean checkWaitingForWindowsLocked() {
+
+ boolean haveBootMsg = false;
+ boolean haveApp = false;
+ // if the wallpaper service is disabled on the device, we're never going to have
+ // wallpaper, don't bother waiting for it
+ boolean haveWallpaper = false;
+ boolean wallpaperEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableWallpaperService)
+ && !mOnlyCore;
+ boolean haveKeyguard = true;
+ // TODO(multidisplay): Expand to all displays?
+ final WindowList windows = getDefaultWindowListLocked();
+ final int N = windows.size();
+ for (int i=0; i<N; i++) {
+ WindowState w = windows.get(i);
+ if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+ return true;
+ }
+ if (w.isDrawnLw()) {
+ if (w.mAttrs.type == TYPE_BOOT_PROGRESS) {
+ haveBootMsg = true;
+ } else if (w.mAttrs.type == TYPE_APPLICATION) {
+ haveApp = true;
+ } else if (w.mAttrs.type == TYPE_WALLPAPER) {
+ haveWallpaper = true;
+ } else if (w.mAttrs.type == TYPE_STATUS_BAR) {
+ haveKeyguard = mPolicy.isKeyguardDrawnLw();
+ }
+ }
+ }
+
+ if (DEBUG_SCREEN_ON || DEBUG_BOOT) {
+ Slog.i(TAG, "******** booted=" + mSystemBooted + " msg=" + mShowingBootMessages
+ + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp
+ + " haveWall=" + haveWallpaper + " wallEnabled=" + wallpaperEnabled
+ + " haveKeyguard=" + haveKeyguard);
+ }
+
+ // If we are turning on the screen to show the boot message,
+ // don't do it until the boot message is actually displayed.
+ if (!mSystemBooted && !haveBootMsg) {
+ return true;
+ }
+
+ // If we are turning on the screen after the boot is completed
+ // normally, don't do so until we have the application and
+ // wallpaper.
+ if (mSystemBooted && ((!haveApp && !haveKeyguard) ||
+ (wallpaperEnabled && !haveWallpaper))) {
+ return true;
+ }
+
+ return false;
+ }
+
public void performEnableScreen() {
synchronized(mWindowMap) {
- if (DEBUG_BOOT) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
- + " mForceDisplayEnabled=" + mForceDisplayEnabled
- + " mShowingBootMessages=" + mShowingBootMessages
- + " mSystemBooted=" + mSystemBooted
- + " mOnlyCore=" + mOnlyCore, here);
- }
+ if (DEBUG_BOOT) Slog.i(TAG, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
+ + " mForceDisplayEnabled=" + mForceDisplayEnabled
+ + " mShowingBootMessages=" + mShowingBootMessages
+ + " mSystemBooted=" + mSystemBooted
+ + " mOnlyCore=" + mOnlyCore,
+ new RuntimeException("here").fillInStackTrace());
if (mDisplayEnabled) {
return;
}
@@ -5562,94 +5686,64 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (!mForceDisplayEnabled) {
- // Don't enable the screen until all existing windows
- // have been drawn.
- boolean haveBootMsg = false;
- boolean haveApp = false;
- // if the wallpaper service is disabled on the device, we're never going to have
- // wallpaper, don't bother waiting for it
- boolean haveWallpaper = false;
- boolean wallpaperEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableWallpaperService)
- && !mOnlyCore;
- boolean haveKeyguard = true;
- // TODO(multidisplay): Expand to all displays?
- final WindowList windows = getDefaultWindowListLocked();
- final int N = windows.size();
- for (int i=0; i<N; i++) {
- WindowState w = windows.get(i);
- if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
- return;
- }
- if (w.isDrawnLw()) {
- if (w.mAttrs.type == TYPE_BOOT_PROGRESS) {
- haveBootMsg = true;
- } else if (w.mAttrs.type == TYPE_APPLICATION) {
- haveApp = true;
- } else if (w.mAttrs.type == TYPE_WALLPAPER) {
- haveWallpaper = true;
- } else if (w.mAttrs.type == TYPE_STATUS_BAR) {
- haveKeyguard = mPolicy.isKeyguardDrawnLw();
- }
- }
- }
-
- if (DEBUG_SCREEN_ON || DEBUG_BOOT) {
- Slog.i(TAG, "******** booted=" + mSystemBooted + " msg=" + mShowingBootMessages
- + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp
- + " haveWall=" + haveWallpaper + " wallEnabled=" + wallpaperEnabled
- + " haveKeyguard=" + haveKeyguard);
- }
+ // Don't enable the screen until all existing windows have been drawn.
+ if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {
+ return;
+ }
- // If we are turning on the screen to show the boot message,
- // don't do it until the boot message is actually displayed.
- if (!mSystemBooted && !haveBootMsg) {
- return;
+ if (!mBootAnimationStopped) {
+ // Do this one time.
+ try {
+ IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+ if (surfaceFlinger != null) {
+ //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
+ data, null, 0);
+ data.recycle();
+ }
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");
}
+ mBootAnimationStopped = true;
+ }
- // If we are turning on the screen after the boot is completed
- // normally, don't do so until we have the application and
- // wallpaper.
- if (mSystemBooted && ((!haveApp && !haveKeyguard) ||
- (wallpaperEnabled && !haveWallpaper))) {
- return;
- }
+ if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
+ if (DEBUG_BOOT) Slog.i(TAG, "performEnableScreen: Waiting for anim complete");
+ return;
}
mDisplayEnabled = true;
if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG, "******************** ENABLING SCREEN!");
- if (false) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new FastPrintWriter(sw, false, 1024);
- this.dump(null, pw, null);
- pw.flush();
- Slog.i(TAG, sw.toString());
- }
- try {
- IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
- if (surfaceFlinger != null) {
- //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
- data, null, 0);
- data.recycle();
- }
- } catch (RemoteException ex) {
- Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");
- }
// Enable input dispatch.
mInputMonitor.setEventDispatchingLw(mEventDispatchingEnabled);
}
+ try {
+ mActivityManager.bootAnimationComplete();
+ } catch (RemoteException e) {
+ }
+
mPolicy.enableScreenAfterBoot();
// Make sure the last requested orientation has been applied.
updateRotationUnchecked(false, false);
}
+ private boolean checkBootAnimationCompleteLocked() {
+ if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) {
+ mH.removeMessages(H.CHECK_IF_BOOT_ANIMATION_FINISHED);
+ mH.sendEmptyMessageDelayed(H.CHECK_IF_BOOT_ANIMATION_FINISHED,
+ BOOT_ANIMATION_POLL_INTERVAL);
+ if (DEBUG_BOOT) Slog.i(TAG, "checkBootAnimationComplete: Waiting for anim complete");
+ return false;
+ }
+ if (DEBUG_BOOT) Slog.i(TAG, "checkBootAnimationComplete: Animation complete!");
+ return true;
+ }
+
public void showBootMessage(final CharSequence msg, final boolean always) {
boolean first = false;
synchronized(mWindowMap) {
@@ -5709,7 +5803,16 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.bool.config_windowIsRound)
&& mContext.getResources().getBoolean(
com.android.internal.R.bool.config_windowShowCircularMask)) {
- mH.sendMessage(mH.obtainMessage(H.SHOW_DISPLAY_MASK));
+ mH.sendMessage(mH.obtainMessage(H.SHOW_CIRCULAR_DISPLAY_MASK));
+ }
+ }
+
+ public void showEmulatorDisplayOverlayIfNeeded() {
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_windowEnableCircularEmulatorDisplayOverlay)
+ && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false)
+ && Build.HARDWARE.contains("goldfish")) {
+ mH.sendMessage(mH.obtainMessage(H.SHOW_EMULATOR_DISPLAY_OVERLAY));
}
}
@@ -5717,12 +5820,12 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION showDisplayMask");
+ ">>> OPEN TRANSACTION showCircularMask");
SurfaceControl.openTransaction();
try {
// TODO(multi-display): support multiple displays
if (mCircularDisplayMask == null) {
- int screenOffset = (int) mContext.getResources().getDimensionPixelSize(
+ int screenOffset = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.circular_display_mask_offset);
mCircularDisplayMask = new CircularDisplayMask(
@@ -5736,7 +5839,32 @@ public class WindowManagerService extends IWindowManager.Stub
} finally {
SurfaceControl.closeTransaction();
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION showDisplayMask");
+ "<<< CLOSE TRANSACTION showCircularMask");
+ }
+ }
+ }
+
+ public void showEmulatorDisplayOverlay() {
+ synchronized(mWindowMap) {
+
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION showEmulatorDisplayOverlay");
+ SurfaceControl.openTransaction();
+ try {
+ if (mEmulatorDisplayOverlay == null) {
+ mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
+ mContext,
+ getDefaultDisplayContentLocked().getDisplay(),
+ mFxSession,
+ mPolicy.windowTypeToLayerLw(
+ WindowManager.LayoutParams.TYPE_POINTER)
+ * TYPE_LAYER_MULTIPLIER + 10);
+ }
+ mEmulatorDisplayOverlay.setVisibility(true);
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION showEmulatorDisplayOverlay");
}
}
}
@@ -5834,7 +5962,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public Bitmap screenshotApplications(IBinder appToken, int displayId, int width,
int height, boolean force565) {
- if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
+ if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
"screenshotApplications()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
@@ -5854,7 +5982,7 @@ public class WindowManagerService extends IWindowManager.Stub
return null;
}
- Bitmap rawss = null;
+ Bitmap bm = null;
int maxLayer = 0;
final Rect frame = new Rect();
@@ -5995,10 +6123,8 @@ public class WindowManagerService extends IWindowManager.Stub
// The screenshot API does not apply the current screen rotation.
rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
+
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
- final int tmp = width;
- width = height;
- height = tmp;
rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
}
@@ -6024,9 +6150,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG,
"Taking screenshot while rotating");
- rawss = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
- inRotation);
- if (rawss == null) {
+ bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
+ inRotation, rot);
+ if (bm == null) {
Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+ ") to layer " + maxLayer);
return null;
@@ -6036,17 +6162,6 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- Bitmap bm = Bitmap.createBitmap(width, height, force565 ?
- Config.RGB_565 : rawss.getConfig());
- if (DEBUG_SCREENSHOT) {
- bm.eraseColor(0xFF000000);
- }
- Matrix matrix = new Matrix();
- ScreenRotationAnimation.createRotationMatrix(rot, width, height, matrix);
- Canvas canvas = new Canvas(bm);
- canvas.drawBitmap(rawss, matrix, null);
- canvas.setBitmap(null);
-
if (DEBUG_SCREENSHOT) {
// TEST IF IT's ALL BLACK
int[] buffer = new int[bm.getWidth() * bm.getHeight()];
@@ -6067,9 +6182,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- rawss.recycle();
-
- return bm;
+ // Copy the screenshot bitmap to another buffer so that the gralloc backed
+ // bitmap will not have a long lifetime. Gralloc memory can be pinned or
+ // duplicated and might have a higher cost than a skia backed buffer.
+ Bitmap ret = bm.copy(bm.getConfig(),true);
+ bm.recycle();
+ return ret;
}
/**
@@ -7367,7 +7485,10 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int NEW_ANIMATOR_SCALE = 34;
- public static final int SHOW_DISPLAY_MASK = 35;
+ public static final int SHOW_CIRCULAR_DISPLAY_MASK = 35;
+ public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36;
+
+ public static final int CHECK_IF_BOOT_ANIMATION_FINISHED = 37;
@Override
public void handleMessage(Message msg) {
@@ -7763,11 +7884,16 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case SHOW_DISPLAY_MASK: {
+ case SHOW_CIRCULAR_DISPLAY_MASK: {
showCircularMask();
break;
}
+ case SHOW_EMULATOR_DISPLAY_OVERLAY: {
+ showEmulatorDisplayOverlay();
+ break;
+ }
+
case DO_ANIMATION_CALLBACK: {
try {
((IRemoteCallback)msg.obj).sendResult(null);
@@ -7848,6 +7974,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
break;
+ case CHECK_IF_BOOT_ANIMATION_FINISHED: {
+ final boolean bootAnimationComplete;
+ synchronized (mWindowMap) {
+ if (DEBUG_BOOT) Slog.i(TAG, "CHECK_IF_BOOT_ANIMATION_FINISHED:");
+ bootAnimationComplete = checkBootAnimationCompleteLocked();
+ }
+ if (bootAnimationComplete) {
+ performEnableScreen();
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -9337,6 +9474,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (mCircularDisplayMask != null) {
mCircularDisplayMask.positionSurface(defaultDw, defaultDh, mRotation);
}
+ if (mEmulatorDisplayOverlay != null) {
+ mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh, mRotation);
+ }
boolean focusDisplayed = false;
@@ -10863,6 +11003,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" mLastWallpaperX="); pw.print(mLastWallpaperX);
pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
+ if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE
+ || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ pw.print(" mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
+ pw.print(" mLastWallpaperDisplayOffsetY=");
+ pw.println(mLastWallpaperDisplayOffsetY);
+ }
if (mInputMethodAnimLayerAdjustment != 0 ||
mWallpaperAnimLayerAdjustment != 0) {
pw.print(" mInputMethodAnimLayerAdjustment=");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e74de38..0baa2be 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -247,6 +247,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
float mWallpaperXStep = -1;
float mWallpaperYStep = -1;
+ // If a window showing a wallpaper: a raw pixel offset to forcibly apply
+ // to its window; if a wallpaper window: not used.
+ int mWallpaperDisplayOffsetX = Integer.MIN_VALUE;
+ int mWallpaperDisplayOffsetY = Integer.MIN_VALUE;
+
// Wallpaper windows: pixels offset based on above variables.
int mXOffset;
int mYOffset;
@@ -1584,6 +1589,13 @@ final class WindowState implements WindowManagerPolicy.WindowState {
pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep);
pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
}
+ if (mWallpaperDisplayOffsetX != Integer.MIN_VALUE
+ || mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ pw.print(prefix); pw.print("mWallpaperDisplayOffsetX=");
+ pw.print(mWallpaperDisplayOffsetX);
+ pw.print(" mWallpaperDisplayOffsetY=");
+ pw.println(mWallpaperDisplayOffsetY);
+ }
}
String makeInputChannelName() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3d4be12..dd611ce 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1526,8 +1526,9 @@ class WindowStateAnimator {
}
void setWallpaperOffset(RectF shownFrame) {
- final int left = (int) shownFrame.left;
- final int top = (int) shownFrame.top;
+ final LayoutParams attrs = mWin.getAttrs();
+ final int left = ((int) shownFrame.left) - attrs.surfaceInsets.left;
+ final int top = ((int) shownFrame.top) - attrs.surfaceInsets.top;
if (mSurfaceX != left || mSurfaceY != top) {
mSurfaceX = left;
mSurfaceY = top;
@@ -1556,11 +1557,11 @@ class WindowStateAnimator {
}
}
- void setOpaque(boolean isOpaque) {
+ void setOpaqueLocked(boolean isOpaque) {
if (mSurfaceControl == null) {
return;
}
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaque");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaqueLocked");
SurfaceControl.openTransaction();
try {
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "isOpaque=" + isOpaque,
@@ -1568,7 +1569,7 @@ class WindowStateAnimator {
mSurfaceControl.setOpaque(isOpaque);
} finally {
SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaque");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
}
}
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 1f377c7..d81cdd9 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -11,7 +11,6 @@ LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
$(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
$(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \
- $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiMhlController.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index bc866d3..65a28c0 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -21,7 +21,6 @@
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
-#include "utils/Vector.h"
#include <usbhost/usbhost.h>
@@ -68,8 +67,6 @@ static int usb_device_added(const char *devname, void* client_data) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject thiz = (jobject)client_data;
- Vector<int> interfaceValues;
- Vector<int> endpointValues;
const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
char *manufacturer = usb_device_get_manufacturer_name(device);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 8ed74be..cddca92 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -71,7 +71,7 @@ static struct {
jmethodID notifyANR;
jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
- jmethodID interceptWakeMotionBeforeQueueing;
+ jmethodID interceptMotionBeforeQueueingNonInteractive;
jmethodID interceptKeyBeforeDispatching;
jmethodID dispatchUnhandledKey;
jmethodID checkInjectEventsPermission;
@@ -854,7 +854,9 @@ void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
} else {
- policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ if (mInteractive) {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ }
}
}
@@ -870,20 +872,22 @@ void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& p
if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
if (policyFlags & POLICY_FLAG_INTERACTIVE) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
- } else if (policyFlags & POLICY_FLAG_WAKE) {
+ } else {
JNIEnv* env = jniEnv();
jint wmActions = env->CallIntMethod(mServiceObj,
- gServiceClassInfo.interceptWakeMotionBeforeQueueing,
+ gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive,
when, policyFlags);
if (checkAndClearExceptionFromCallback(env,
- "interceptWakeMotionBeforeQueueing")) {
+ "interceptMotionBeforeQueueingNonInteractive")) {
wmActions = 0;
}
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
}
} else {
- policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ if (mInteractive) {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ }
}
}
@@ -1441,8 +1445,8 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeQueueing, clazz,
"interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I");
- GET_METHOD_ID(gServiceClassInfo.interceptWakeMotionBeforeQueueing, clazz,
- "interceptWakeMotionBeforeQueueing", "(JI)I");
+ GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz,
+ "interceptMotionBeforeQueueingNonInteractive", "(JI)I");
GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz,
"interceptKeyBeforeDispatching",
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 39b70a8..7b2e408 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -38,7 +38,6 @@ int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
-int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
int register_android_server_PersistentDataBlockService(JNIEnv* env);
int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
@@ -76,7 +75,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
register_android_server_hdmi_HdmiCecController(env);
- register_android_server_hdmi_HdmiMhlController(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
register_android_server_fingerprint_FingerprintService(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8800ba9..5ad9825 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3001,10 +3001,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
- synchronized (this) {
- DevicePolicyData policy = getUserData(userHandle);
- long ident = Binder.clearCallingIdentity();
- try {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ boolean wipeData = false;
+ int identifier = 0;
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
policy.mFailedPasswordAttempts++;
saveSettingsLocked(userHandle);
if (mHasFeature) {
@@ -3016,15 +3018,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Wipe the user/profile associated with the policy that was violated. This
// is not necessarily calling user: if the policy that fired was from a
// managed profile rather than the main user profile, we wipe former only.
- wipeDeviceOrUserLocked(0, strictestAdmin.getUserHandle().getIdentifier());
+ wipeData = true;
+ identifier = strictestAdmin.getUserHandle().getIdentifier();
}
sendAdminCommandToSelfAndProfilesLocked(
DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
+ if (wipeData) {
+ // Call without holding lock.
+ wipeDeviceOrUserLocked(0, identifier);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -3659,11 +3666,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
- if (mUserManager.getUserInfo(userHandle) == null) {
+ UserInfo info = mUserManager.getUserInfo(userHandle);
+ if (info == null) {
// User doesn't exist.
throw new IllegalArgumentException(
"Attempted to set profile owner for invalid userId: " + userHandle);
}
+ if (info.isGuest()) {
+ throw new IllegalStateException("Cannot set a profile owner on a guest");
+ }
if (who == null
|| !DeviceOwner.isInstalledForUser(who.getPackageName(), userHandle)) {
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 64242ba..6785cb8 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -627,26 +627,40 @@ public final class PrintManagerService extends SystemService {
return userState;
}
- private void handleUserStarted(int userId) {
- UserState userState;
- synchronized (mLock) {
- userState = getOrCreateUserStateLocked(userId);
- userState.updateIfNeededLocked();
- }
- // This is the first time we switch to this user after boot, so
- // now is the time to remove obsolete print jobs since they
- // are from the last boot and no application would query them.
- userState.removeObsoletePrintJobs();
+ private void handleUserStarted(final int userId) {
+ // This code will touch the remote print spooler which
+ // must be called off the main thread, so post the work.
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ UserState userState;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(userId);
+ userState.updateIfNeededLocked();
+ }
+ // This is the first time we switch to this user after boot, so
+ // now is the time to remove obsolete print jobs since they
+ // are from the last boot and no application would query them.
+ userState.removeObsoletePrintJobs();
+ }
+ });
}
- private void handleUserStopped(int userId) {
- synchronized (mLock) {
- UserState userState = mUserStates.get(userId);
- if (userState != null) {
- userState.destroyLocked();
- mUserStates.remove(userId);
+ private void handleUserStopped(final int userId) {
+ // This code will touch the remote print spooler which
+ // must be called off the main thread, so post the work.
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ UserState userState = mUserStates.get(userId);
+ if (userState != null) {
+ userState.destroyLocked();
+ mUserStates.remove(userId);
+ }
+ }
}
- }
+ });
}
private int resolveCallingProfileParentLocked(int userId) {
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
index fb29b6a..218f899 100644
--- a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -139,7 +139,7 @@ public final class RestrictionsManagerService extends SystemService {
}
@Override
- public Intent getLocalApprovalIntent() throws RemoteException {
+ public Intent createLocalApprovalIntent() throws RemoteException {
if (DEBUG) {
Log.i(LOG_TAG, "requestPermission");
}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index dc036e2..5f639ab 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,9 +15,11 @@
*/
package com.android.server.usage;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
+import android.content.res.Configuration;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -25,7 +27,9 @@ class IntervalStats {
public long beginTime;
public long endTime;
public long lastTimeSaved;
- public final ArrayMap<String, UsageStats> stats = new ArrayMap<>();
+ public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
+ public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
+ public Configuration activeConfiguration;
public TimeSparseArray<UsageEvents.Event> events;
// A string cache. This is important as when we're parsing XML files, we don't want to
@@ -34,18 +38,49 @@ class IntervalStats {
// strings that had identical copies in the cache.
private final ArraySet<String> mStringCache = new ArraySet<>();
+ /**
+ * Gets the UsageStats object for the given package, or creates one and adds it internally.
+ */
UsageStats getOrCreateUsageStats(String packageName) {
- UsageStats usageStats = stats.get(packageName);
+ UsageStats usageStats = packageStats.get(packageName);
if (usageStats == null) {
usageStats = new UsageStats();
- usageStats.mPackageName = packageName;
+ usageStats.mPackageName = getCachedStringRef(packageName);
usageStats.mBeginTimeStamp = beginTime;
usageStats.mEndTimeStamp = endTime;
- stats.put(packageName, usageStats);
+ packageStats.put(usageStats.mPackageName, usageStats);
}
return usageStats;
}
+ /**
+ * Gets the ConfigurationStats object for the given configuration, or creates one and adds it
+ * internally.
+ */
+ ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
+ ConfigurationStats configStats = configurations.get(config);
+ if (configStats == null) {
+ configStats = new ConfigurationStats();
+ configStats.mBeginTimeStamp = beginTime;
+ configStats.mEndTimeStamp = endTime;
+ configStats.mConfiguration = config;
+ configurations.put(config, configStats);
+ }
+ return configStats;
+ }
+
+ /**
+ * Builds a UsageEvents.Event, but does not add it internally.
+ */
+ UsageEvents.Event buildEvent(String packageName, String className) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ event.mPackage = getCachedStringRef(packageName);
+ if (className != null) {
+ event.mClass = getCachedStringRef(className);
+ }
+ return event;
+ }
+
void update(String packageName, long timeStamp, int eventType) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
@@ -61,6 +96,28 @@ class IntervalStats {
usageStats.mLastEvent = eventType;
usageStats.mLastTimeUsed = timeStamp;
usageStats.mEndTimeStamp = timeStamp;
+
+ if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+ usageStats.mLaunchCount += 1;
+ }
+
+ endTime = timeStamp;
+ }
+
+ void updateConfigurationStats(Configuration config, long timeStamp) {
+ if (activeConfiguration != null) {
+ ConfigurationStats activeStats = configurations.get(activeConfiguration);
+ activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive;
+ activeStats.mLastTimeActive = timeStamp - 1;
+ }
+
+ if (config != null) {
+ ConfigurationStats configStats = getOrCreateConfigurationStats(config);
+ configStats.mLastTimeActive = timeStamp;
+ configStats.mActivationCount += 1;
+ activeConfiguration = configStats.mConfiguration;
+ }
+
endTime = timeStamp;
}
@@ -72,13 +129,4 @@ class IntervalStats {
}
return mStringCache.valueAt(index);
}
-
- UsageEvents.Event buildEvent(String packageName, String className) {
- UsageEvents.Event event = new UsageEvents.Event();
- event.mPackage = getCachedStringRef(packageName);
- if (className != null) {
- event.mClass = getCachedStringRef(className);
- }
- return event;
- }
}
diff --git a/services/usage/java/com/android/server/usage/UnixCalendar.java b/services/usage/java/com/android/server/usage/UnixCalendar.java
new file mode 100644
index 0000000..ce06a91
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UnixCalendar.java
@@ -0,0 +1,99 @@
+/**
+ * Copyright (C) 2014 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.usage;
+
+import android.app.usage.UsageStatsManager;
+
+/**
+ * A handy calendar object that knows nothing of Locale's or TimeZones. This simplifies
+ * interval book-keeping. It is *NOT* meant to be used as a user-facing calendar, as it has
+ * no concept of Locale or TimeZone.
+ */
+public class UnixCalendar {
+ private static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+ private static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
+ private static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
+ private static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+ private long mTime;
+
+ public UnixCalendar(long time) {
+ mTime = time;
+ }
+
+ public void truncateToDay() {
+ mTime -= mTime % DAY_IN_MILLIS;
+ }
+
+ public void truncateToWeek() {
+ mTime -= mTime % WEEK_IN_MILLIS;
+ }
+
+ public void truncateToMonth() {
+ mTime -= mTime % MONTH_IN_MILLIS;
+ }
+
+ public void truncateToYear() {
+ mTime -= mTime % YEAR_IN_MILLIS;
+ }
+
+ public void addDays(int val) {
+ mTime += val * DAY_IN_MILLIS;
+ }
+
+ public void addWeeks(int val) {
+ mTime += val * WEEK_IN_MILLIS;
+ }
+
+ public void addMonths(int val) {
+ mTime += val * MONTH_IN_MILLIS;
+ }
+
+ public void addYears(int val) {
+ mTime += val * YEAR_IN_MILLIS;
+ }
+
+ public void setTimeInMillis(long time) {
+ mTime = time;
+ }
+
+ public long getTimeInMillis() {
+ return mTime;
+ }
+
+ public static void truncateTo(UnixCalendar calendar, int intervalType) {
+ switch (intervalType) {
+ case UsageStatsManager.INTERVAL_YEARLY:
+ calendar.truncateToYear();
+ break;
+
+ case UsageStatsManager.INTERVAL_MONTHLY:
+ calendar.truncateToMonth();
+ break;
+
+ case UsageStatsManager.INTERVAL_WEEKLY:
+ calendar.truncateToWeek();
+ break;
+
+ case UsageStatsManager.INTERVAL_DAILY:
+ calendar.truncateToDay();
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Can't truncate date to interval " +
+ intervalType);
+ }
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index e6ce0fe..62a7ec0 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,29 +17,34 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
-import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.util.AtomicFile;
import android.util.Slog;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Calendar;
import java.util.List;
/**
* Provides an interface to query for UsageStat data from an XML database.
*/
class UsageStatsDatabase {
+ private static final int CURRENT_VERSION = 2;
+
private static final String TAG = "UsageStatsDatabase";
private static final boolean DEBUG = UsageStatsService.DEBUG;
private final Object mLock = new Object();
private final File[] mIntervalDirs;
private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
- private final Calendar mCal;
+ private final UnixCalendar mCal;
+ private final File mVersionFile;
public UsageStatsDatabase(File dir) {
mIntervalDirs = new File[] {
@@ -48,8 +53,9 @@ class UsageStatsDatabase {
new File(dir, "monthly"),
new File(dir, "yearly"),
};
+ mVersionFile = new File(dir, "version");
mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
- mCal = Calendar.getInstance();
+ mCal = new UnixCalendar(0);
}
/**
@@ -65,6 +71,8 @@ class UsageStatsDatabase {
}
}
+ checkVersionLocked();
+
final FilenameFilter backupFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
@@ -82,7 +90,45 @@ class UsageStatsDatabase {
}
for (File f : files) {
- mSortedStatFiles[i].put(Long.parseLong(f.getName()), new AtomicFile(f));
+ final AtomicFile af = new AtomicFile(f);
+ mSortedStatFiles[i].put(UsageStatsXml.parseBeginTime(af), af);
+ }
+ }
+ }
+ }
+ }
+
+ private void checkVersionLocked() {
+ int version;
+ try (BufferedReader reader = new BufferedReader(new FileReader(mVersionFile))) {
+ version = Integer.parseInt(reader.readLine());
+ } catch (NumberFormatException | IOException e) {
+ version = 0;
+ }
+
+ if (version != CURRENT_VERSION) {
+ Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION);
+ doUpgradeLocked(version);
+
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) {
+ writer.write(Integer.toString(CURRENT_VERSION));
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write new version");
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void doUpgradeLocked(int thisVersion) {
+ if (thisVersion < 2) {
+ // Delete all files if we are version 0. This is a pre-release version,
+ // so this is fine.
+ Slog.i(TAG, "Deleting all usage stats files");
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ File[] files = mIntervalDirs[i].listFiles();
+ if (files != null) {
+ for (File f : files) {
+ f.delete();
}
}
}
@@ -133,33 +179,77 @@ class UsageStatsDatabase {
}
/**
- * Find all {@link UsageStats} for the given range and interval type.
+ * Figures out what to extract from the given IntervalStats object.
*/
- public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
+ interface StatCombiner<T> {
+
+ /**
+ * Implementations should extract interesting from <code>stats</code> and add it
+ * to the <code>accumulatedResult</code> list.
+ *
+ * If the <code>stats</code> object is mutable, <code>mutable</code> will be true,
+ * which means you should make a copy of the data before adding it to the
+ * <code>accumulatedResult</code> list.
+ *
+ * @param stats The {@link IntervalStats} object selected.
+ * @param mutable Whether or not the data inside the stats object is mutable.
+ * @param accumulatedResult The list to which to add extracted data.
+ */
+ void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
+ }
+
+ /**
+ * Find all {@link IntervalStats} for the given range and interval type.
+ */
+ public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime,
+ StatCombiner<T> combiner) {
synchronized (mLock) {
if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
throw new IllegalArgumentException("Bad interval type " + intervalType);
}
- if (endTime < beginTime) {
+ final TimeSparseArray<AtomicFile> intervalStats = mSortedStatFiles[intervalType];
+
+ if (endTime <= beginTime) {
+ if (DEBUG) {
+ Slog.d(TAG, "endTime(" + endTime + ") <= beginTime(" + beginTime + ")");
+ }
return null;
}
- final int startIndex = mSortedStatFiles[intervalType].closestIndexOnOrBefore(beginTime);
+ int startIndex = intervalStats.closestIndexOnOrBefore(beginTime);
if (startIndex < 0) {
- return null;
+ // All the stats available have timestamps after beginTime, which means they all
+ // match.
+ startIndex = 0;
}
- int endIndex = mSortedStatFiles[intervalType].closestIndexOnOrAfter(endTime);
+ int endIndex = intervalStats.closestIndexOnOrBefore(endTime);
if (endIndex < 0) {
- endIndex = mSortedStatFiles[intervalType].size() - 1;
+ // All the stats start after this range ends, so nothing matches.
+ if (DEBUG) {
+ Slog.d(TAG, "No results for this range. All stats start after.");
+ }
+ return null;
+ }
+
+ if (intervalStats.keyAt(endIndex) == endTime) {
+ // The endTime is exclusive, so if we matched exactly take the one before.
+ endIndex--;
+ if (endIndex < 0) {
+ // All the stats start after this range ends, so nothing matches.
+ if (DEBUG) {
+ Slog.d(TAG, "No results for this range. All stats start after.");
+ }
+ return null;
+ }
}
try {
IntervalStats stats = new IntervalStats();
- ArrayList<UsageStats> results = new ArrayList<>();
+ ArrayList<T> results = new ArrayList<>();
for (int i = startIndex; i <= endIndex; i++) {
- final AtomicFile f = mSortedStatFiles[intervalType].valueAt(i);
+ final AtomicFile f = intervalStats.valueAt(i);
if (DEBUG) {
Slog.d(TAG, "Reading stat file " + f.getBaseFile().getAbsolutePath());
@@ -167,7 +257,7 @@ class UsageStatsDatabase {
UsageStatsXml.read(f, stats);
if (beginTime < stats.endTime) {
- results.addAll(stats.stats.values());
+ combiner.combine(stats, false, results);
}
}
return results;
@@ -209,19 +299,23 @@ class UsageStatsDatabase {
public void prune() {
synchronized (mLock) {
long timeNow = System.currentTimeMillis();
+ mCal.setTimeInMillis(timeNow);
+ mCal.addYears(-3);
+ pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_YEARLY],
+ mCal.getTimeInMillis());
mCal.setTimeInMillis(timeNow);
- mCal.add(Calendar.MONTH, -6);
+ mCal.addMonths(-6);
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_MONTHLY],
mCal.getTimeInMillis());
mCal.setTimeInMillis(timeNow);
- mCal.add(Calendar.WEEK_OF_YEAR, -4);
+ mCal.addWeeks(-4);
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_WEEKLY],
mCal.getTimeInMillis());
mCal.setTimeInMillis(timeNow);
- mCal.add(Calendar.DAY_OF_YEAR, -7);
+ mCal.addDays(-7);
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_DAILY],
mCal.getTimeInMillis());
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0e8b427..2dcdcc4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,6 +18,7 @@ package com.android.server.usage;
import android.Manifest;
import android.app.AppOpsManager;
+import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
@@ -30,11 +31,14 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
@@ -59,9 +63,7 @@ public class UsageStatsService extends SystemService implements
static final boolean DEBUG = false;
private static final long TEN_SECONDS = 10 * 1000;
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
- private static final long TWO_MINUTES = 2 * 60 * 1000;
private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES;
- private static final long END_TIME_DELAY = DEBUG ? 0 : TWO_MINUTES;
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
@@ -75,6 +77,8 @@ public class UsageStatsService extends SystemService implements
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
private File mUsageStatsDir;
+ long mRealTimeSnapshot;
+ long mSystemTimeSnapshot;
public UsageStatsService(Context context) {
super(context);
@@ -101,6 +105,9 @@ public class UsageStatsService extends SystemService implements
cleanUpRemovedUsersLocked();
}
+ mRealTimeSnapshot = SystemClock.elapsedRealtime();
+ mSystemTimeSnapshot = System.currentTimeMillis();
+
publishLocalService(UsageStatsManagerInternal.class, new LocalService());
publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
}
@@ -175,7 +182,7 @@ public class UsageStatsService extends SystemService implements
}
/**
- * Called by the Bunder stub
+ * Called by the Binder stub
*/
void shutdown() {
synchronized (mLock) {
@@ -218,8 +225,7 @@ public class UsageStatsService extends SystemService implements
* Called by the Binder stub.
*/
List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime) {
- final long timeNow = System.currentTimeMillis();
- if (beginTime > timeNow) {
+ if (!validRange(beginTime, endTime)) {
return null;
}
@@ -232,15 +238,23 @@ public class UsageStatsService extends SystemService implements
/**
* Called by the Binder stub.
*/
- UsageEvents queryEvents(int userId, long beginTime, long endTime) {
- final long timeNow = System.currentTimeMillis();
+ List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime,
+ long endTime) {
+ if (!validRange(beginTime, endTime)) {
+ return null;
+ }
- // Adjust the endTime so that we don't query for the latest events.
- // This is to prevent apps from making decision based on what app launched them,
- // etc.
- endTime = Math.min(endTime, timeNow - END_TIME_DELAY);
+ synchronized (mLock) {
+ UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId);
+ return service.queryConfigurationStats(bucketType, beginTime, endTime);
+ }
+ }
- if (beginTime > endTime) {
+ /**
+ * Called by the Binder stub.
+ */
+ UsageEvents queryEvents(int userId, long beginTime, long endTime) {
+ if (!validRange(beginTime, endTime)) {
return null;
}
@@ -250,6 +264,11 @@ public class UsageStatsService extends SystemService implements
}
}
+ private static boolean validRange(long beginTime, long endTime) {
+ final long timeNow = System.currentTimeMillis();
+ return beginTime <= timeNow && beginTime < endTime;
+ }
+
private void flushToDiskLocked() {
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
@@ -323,6 +342,28 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType,
+ long beginTime, long endTime, String callingPackage) throws RemoteException {
+ if (!hasPermission(callingPackage)) {
+ return null;
+ }
+
+ final int userId = UserHandle.getCallingUserId();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final List<ConfigurationStats> results =
+ UsageStatsService.this.queryConfigurationStats(userId, bucketType,
+ beginTime, endTime);
+ if (results != null) {
+ return new ParceledListSlice<>(results);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return null;
+ }
+
+ @Override
public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
if (!hasPermission(callingPackage)) {
return null;
@@ -345,9 +386,16 @@ public class UsageStatsService extends SystemService implements
*/
private class LocalService extends UsageStatsManagerInternal {
+ /**
+ * The system may have its time change, so at least make sure the events
+ * are monotonic in order.
+ */
+ private long computeMonotonicSystemTime(long realTime) {
+ return (realTime - mRealTimeSnapshot) + mSystemTimeSnapshot;
+ }
+
@Override
- public void reportEvent(ComponentName component, int userId,
- long timeStamp, int eventType) {
+ public void reportEvent(ComponentName component, int userId, int eventType) {
if (component == null) {
Slog.w(TAG, "Event reported without a component name");
return;
@@ -356,12 +404,27 @@ public class UsageStatsService extends SystemService implements
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = component.getPackageName();
event.mClass = component.getClassName();
- event.mTimeStamp = timeStamp;
+ event.mTimeStamp = computeMonotonicSystemTime(SystemClock.elapsedRealtime());
event.mEventType = eventType;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@Override
+ public void reportConfigurationChange(Configuration config, int userId) {
+ if (config == null) {
+ Slog.w(TAG, "Configuration event reported with a null config");
+ return;
+ }
+
+ UsageEvents.Event event = new UsageEvents.Event();
+ event.mPackage = "android";
+ event.mTimeStamp = computeMonotonicSystemTime(SystemClock.elapsedRealtime());
+ event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
+ event.mConfiguration = new Configuration(config);
+ mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+ }
+
+ @Override
public void prepareShutdown() {
// This method *WILL* do IO work, but we must block until it is finished or else
// we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
diff --git a/services/usage/java/com/android/server/usage/UsageStatsUtils.java b/services/usage/java/com/android/server/usage/UsageStatsUtils.java
deleted file mode 100644
index dd5f3b9..0000000
--- a/services/usage/java/com/android/server/usage/UsageStatsUtils.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (C) 2014 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.usage;
-
-import android.app.usage.UsageStatsManager;
-
-import java.util.Calendar;
-
-/**
- * A collection of utility methods used by the UsageStatsService and accompanying classes.
- */
-final class UsageStatsUtils {
- private UsageStatsUtils() {}
-
- /**
- * Truncates the date to the given UsageStats bucket. For example, if the bucket is
- * {@link UsageStatsManager#INTERVAL_YEARLY}, the date is truncated to the 1st day of the year,
- * with the time set to 00:00:00.
- *
- * @param bucket The UsageStats bucket to truncate to.
- * @param cal The date to truncate.
- */
- public static void truncateDateTo(int bucket, Calendar cal) {
- cal.set(Calendar.HOUR_OF_DAY, 0);
- cal.set(Calendar.MINUTE, 0);
- cal.set(Calendar.SECOND, 0);
- cal.set(Calendar.MILLISECOND, 0);
-
- switch (bucket) {
- case UsageStatsManager.INTERVAL_YEARLY:
- cal.set(Calendar.DAY_OF_YEAR, 0);
- break;
-
- case UsageStatsManager.INTERVAL_MONTHLY:
- cal.set(Calendar.DAY_OF_MONTH, 0);
- break;
-
- case UsageStatsManager.INTERVAL_WEEKLY:
- cal.set(Calendar.DAY_OF_WEEK, 0);
- break;
-
- case UsageStatsManager.INTERVAL_DAILY:
- break;
-
- default:
- throw new UnsupportedOperationException("Can't truncate date to bucket " + bucket);
- }
- }
-}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXml.java b/services/usage/java/com/android/server/usage/UsageStatsXml.java
index 48881d0..9ce6d63 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXml.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXml.java
@@ -37,10 +37,15 @@ public class UsageStatsXml {
private static final String USAGESTATS_TAG = "usagestats";
private static final String VERSION_ATTR = "version";
+ public static long parseBeginTime(AtomicFile file) {
+ return Long.parseLong(file.getBaseFile().getName());
+ }
+
public static void read(AtomicFile file, IntervalStats statsOut) throws IOException {
try {
FileInputStream in = file.openRead();
try {
+ statsOut.beginTime = parseBeginTime(file);
read(in, statsOut);
statsOut.lastTimeSaved = file.getLastModifiedTime();
} finally {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 374429a..ef95a7b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -15,16 +15,17 @@
*/
package com.android.server.usage;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
-import android.content.ComponentName;
+import android.content.res.Configuration;
import java.io.IOException;
import java.net.ProtocolException;
@@ -33,103 +34,144 @@ import java.net.ProtocolException;
* UsageStats reader/writer for version 1 of the XML format.
*/
final class UsageStatsXmlV1 {
- private static final String BEGIN_TIME_ATTR = "beginTime";
- private static final String END_TIME_ATTR = "endTime";
+ private static final String PACKAGES_TAG = "packages";
private static final String PACKAGE_TAG = "package";
- private static final String NAME_ATTR = "name";
+
+ private static final String CONFIGURATIONS_TAG = "configurations";
+ private static final String CONFIG_TAG = "config";
+
+ private static final String EVENT_LOG_TAG = "event-log";
+ private static final String EVENT_TAG = "event";
+
+ // Attributes
private static final String PACKAGE_ATTR = "package";
private static final String CLASS_ATTR = "class";
- private static final String TOTAL_TIME_ACTIVE_ATTR = "totalTimeActive";
- private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive";
+ private static final String COUNT_ATTR = "count";
+ private static final String ACTIVE_ATTR = "active";
private static final String LAST_EVENT_ATTR = "lastEvent";
- private static final String EVENT_LOG_TAG = "event-log";
private static final String TYPE_ATTR = "type";
+
+ // Time attributes stored as an offset of the beginTime.
+ private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String END_TIME_ATTR = "endTime";
private static final String TIME_ATTR = "time";
- private static UsageStats readNextUsageStats(XmlPullParser parser)
+ private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- XmlUtils.nextElement(parser);
+ final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
+ if (pkg == null) {
+ throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
}
- if (parser.getEventType() != XmlPullParser.START_TAG ||
- !parser.getName().equals(PACKAGE_TAG)) {
- return null;
- }
+ final UsageStats stats = statsOut.getOrCreateUsageStats(pkg);
- final String name = parser.getAttributeValue(null, NAME_ATTR);
- if (name == null) {
- throw new ProtocolException("no " + NAME_ATTR + " attribute present");
- }
+ // Apply the offset to the beginTime to find the absolute time.
+ stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_ACTIVE_ATTR);
- UsageStats stats = new UsageStats();
- stats.mPackageName = name;
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
- stats.mLastTimeUsed = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
- XmlUtils.skipCurrentTag(parser);
- return stats;
}
- private static UsageEvents.Event readNextEvent(XmlPullParser parser, IntervalStats statsOut)
+ private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- XmlUtils.nextElement(parser);
- }
+ final Configuration config = new Configuration();
+ Configuration.readXmlAttrs(parser, config);
- if (parser.getEventType() != XmlPullParser.START_TAG ||
- !parser.getName().equals(EVENT_LOG_TAG)) {
- return null;
+ final ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
+
+ // Apply the offset to the beginTime to find the absolute time.
+ configStats.mLastTimeActive = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_ACTIVE_ATTR);
+
+ configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
+ configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR);
+ if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) {
+ statsOut.activeConfiguration = configStats.mConfiguration;
}
+ }
- String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
- String className;
+ private static void loadEvent(XmlPullParser parser, IntervalStats statsOut)
+ throws XmlPullParserException, IOException {
+ final String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
if (packageName == null) {
- // Try getting the component name if it exists.
- final String componentName = XmlUtils.readStringAttribute(parser, NAME_ATTR);
- if (componentName == null) {
- throw new ProtocolException("no " + NAME_ATTR + " or " + PACKAGE_ATTR +
- " attribute present");
- }
- ComponentName component = ComponentName.unflattenFromString(componentName);
- if (component == null) {
- throw new ProtocolException("ComponentName " + componentName + " is invalid");
- }
-
- packageName = component.getPackageName();
- className = component.getClassName();
- } else {
- className = XmlUtils.readStringAttribute(parser, CLASS_ATTR);
+ throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
}
- UsageEvents.Event event = statsOut.buildEvent(packageName, className);
+ final String className = XmlUtils.readStringAttribute(parser, CLASS_ATTR);
+
+ final UsageEvents.Event event = statsOut.buildEvent(packageName, className);
+
+ // Apply the offset to the beginTime to find the absolute time of this event.
+ event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR);
+
event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
- event.mTimeStamp = XmlUtils.readLongAttribute(parser, TIME_ATTR);
- XmlUtils.skipCurrentTag(parser);
- return event;
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ event.mConfiguration = new Configuration();
+ Configuration.readXmlAttrs(parser, event.mConfiguration);
+ }
+
+ if (statsOut.events == null) {
+ statsOut.events = new TimeSparseArray<>();
+ }
+ statsOut.events.put(event.mTimeStamp, event);
+ }
+
+ private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
+ final UsageStats usageStats) throws IOException {
+ xml.startTag(null, PACKAGE_TAG);
+
+ // Write the time offset.
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
+ usageStats.mLastTimeUsed - stats.beginTime);
+
+ XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
+ XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
+
+ xml.endTag(null, PACKAGE_TAG);
}
- private static void writeUsageStats(FastXmlSerializer serializer, UsageStats stats)
- throws IOException {
- serializer.startTag(null, PACKAGE_TAG);
- serializer.attribute(null, NAME_ATTR, stats.mPackageName);
- serializer.attribute(null, TOTAL_TIME_ACTIVE_ATTR,
- Long.toString(stats.mTotalTimeInForeground));
- serializer.attribute(null, LAST_TIME_ACTIVE_ATTR, Long.toString(stats.mLastTimeUsed));
- serializer.attribute(null, LAST_EVENT_ATTR, Integer.toString(stats.mLastEvent));
- serializer.endTag(null, PACKAGE_TAG);
+ private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats,
+ final ConfigurationStats configStats, boolean isActive) throws IOException {
+ xml.startTag(null, CONFIG_TAG);
+
+ // Write the time offset.
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
+ configStats.mLastTimeActive - stats.beginTime);
+
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, configStats.mTotalTimeActive);
+ XmlUtils.writeIntAttribute(xml, COUNT_ATTR, configStats.mActivationCount);
+ if (isActive) {
+ XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
+ }
+
+ // Now write the attributes representing the configuration object.
+ Configuration.writeXmlAttrs(xml, configStats.mConfiguration);
+
+ xml.endTag(null, CONFIG_TAG);
}
- private static void writeEvent(FastXmlSerializer serializer, UsageEvents.Event event)
- throws IOException {
- serializer.startTag(null, EVENT_LOG_TAG);
- serializer.attribute(null, PACKAGE_ATTR, event.mPackage);
+ private static void writeEvent(XmlSerializer xml, final IntervalStats stats,
+ final UsageEvents.Event event) throws IOException {
+ xml.startTag(null, EVENT_TAG);
+
+ // Store the time offset.
+ XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp - stats.beginTime);
+
+ XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
if (event.mClass != null) {
- serializer.attribute(null, CLASS_ATTR, event.mClass);
+ XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
+ }
+ XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
+
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE
+ && event.mConfiguration != null) {
+ Configuration.writeXmlAttrs(xml, event.mConfiguration);
}
- serializer.attribute(null, TYPE_ATTR, Integer.toString(event.getEventType()));
- serializer.attribute(null, TIME_ATTR, Long.toString(event.getTimeStamp()));
- serializer.endTag(null, EVENT_LOG_TAG);
+
+ xml.endTag(null, EVENT_TAG);
}
/**
@@ -141,55 +183,74 @@ final class UsageStatsXmlV1 {
*/
public static void read(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- statsOut.stats.clear();
+ statsOut.packageStats.clear();
+ statsOut.configurations.clear();
+ statsOut.activeConfiguration = null;
if (statsOut.events != null) {
statsOut.events.clear();
}
- statsOut.beginTime = XmlUtils.readLongAttribute(parser, BEGIN_TIME_ATTR);
statsOut.endTime = XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
- XmlUtils.nextElement(parser);
- UsageStats pkgStats;
- while ((pkgStats = readNextUsageStats(parser)) != null) {
- pkgStats.mBeginTimeStamp = statsOut.beginTime;
- pkgStats.mEndTimeStamp = statsOut.endTime;
- statsOut.stats.put(pkgStats.mPackageName, pkgStats);
- }
+ int eventCode;
+ int outerDepth = parser.getDepth();
+ while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (eventCode != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (eventCode != XmlPullParser.START_TAG) {
+ continue;
+ }
- UsageEvents.Event event;
- while ((event = readNextEvent(parser, statsOut)) != null) {
- if (statsOut.events == null) {
- statsOut.events = new TimeSparseArray<>();
+ final String tag = parser.getName();
+ switch (tag) {
+ case PACKAGE_TAG:
+ loadUsageStats(parser, statsOut);
+ break;
+
+ case CONFIG_TAG:
+ loadConfigStats(parser, statsOut);
+ break;
+
+ case EVENT_TAG:
+ loadEvent(parser, statsOut);
+ break;
}
- statsOut.events.put(event.getTimeStamp(), event);
}
}
/**
- * Writes the stats object to an XML file. The {@link FastXmlSerializer}
+ * Writes the stats object to an XML file. The {@link XmlSerializer}
* has already written the <code><usagestats></code> tag, but attributes may still
* be added.
*
- * @param serializer The serializer to which to write the stats data.
+ * @param xml The serializer to which to write the packageStats data.
* @param stats The stats object to write to the XML file.
*/
- public static void write(FastXmlSerializer serializer, IntervalStats stats) throws IOException {
- serializer.attribute(null, BEGIN_TIME_ATTR, Long.toString(stats.beginTime));
- serializer.attribute(null, END_TIME_ATTR, Long.toString(stats.endTime));
+ public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
+ XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
- final int statsCount = stats.stats.size();
+ xml.startTag(null, PACKAGES_TAG);
+ final int statsCount = stats.packageStats.size();
for (int i = 0; i < statsCount; i++) {
- writeUsageStats(serializer, stats.stats.valueAt(i));
+ writeUsageStats(xml, stats, stats.packageStats.valueAt(i));
}
+ xml.endTag(null, PACKAGES_TAG);
- if (stats.events != null) {
- final int eventCount = stats.events.size();
- for (int i = 0; i < eventCount; i++) {
- writeEvent(serializer, stats.events.valueAt(i));
- }
+
+ xml.startTag(null, CONFIGURATIONS_TAG);
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+ writeConfigStats(xml, stats, stats.configurations.valueAt(i), active);
+ }
+ xml.endTag(null, CONFIGURATIONS_TAG);
+
+ xml.startTag(null, EVENT_LOG_TAG);
+ final int eventCount = stats.events != null ? stats.events.size() : 0;
+ for (int i = 0; i < eventCount; i++) {
+ writeEvent(xml, stats, stats.events.valueAt(i));
}
+ xml.endTag(null, EVENT_LOG_TAG);
}
private UsageStatsXmlV1() {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 6951590..2769666 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,19 +16,22 @@
package com.android.server.usage;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
+import android.content.res.Configuration;
import android.util.ArraySet;
import android.util.Slog;
+import com.android.server.usage.UsageStatsDatabase.StatCombiner;
+
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.List;
/**
@@ -43,7 +46,7 @@ class UserUsageStatsService {
private final UsageStatsDatabase mDatabase;
private final IntervalStats[] mCurrentStats;
private boolean mStatsChanged = false;
- private final Calendar mDailyExpiryDate;
+ private final UnixCalendar mDailyExpiryDate;
private final StatsUpdatedListener mListener;
private final String mLogPrefix;
@@ -52,7 +55,7 @@ class UserUsageStatsService {
}
UserUsageStatsService(int userId, File usageStatsDir, StatsUpdatedListener listener) {
- mDailyExpiryDate = Calendar.getInstance();
+ mDailyExpiryDate = new UnixCalendar(0);
mDatabase = new UsageStatsDatabase(usageStatsDir);
mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
mListener = listener;
@@ -62,6 +65,7 @@ class UserUsageStatsService {
void init() {
mDatabase.init();
+ final long timeNow = System.currentTimeMillis();
int nullCount = 0;
for (int i = 0; i < mCurrentStats.length; i++) {
mCurrentStats[i] = mDatabase.getLatestUsageStats(i);
@@ -69,6 +73,11 @@ class UserUsageStatsService {
// Find out how many intervals we don't have data for.
// Ideally it should be all or none.
nullCount++;
+ } else if (mCurrentStats[i].beginTime > timeNow) {
+ Slog.e(TAG, mLogPrefix + "Interval " + i + " has stat in the future " +
+ mCurrentStats[i].beginTime);
+ mCurrentStats[i] = null;
+ nullCount++;
}
}
@@ -90,17 +99,18 @@ class UserUsageStatsService {
// that is reported.
mDailyExpiryDate.setTimeInMillis(
mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
- mDailyExpiryDate.add(Calendar.DAY_OF_YEAR, 1);
- UsageStatsUtils.truncateDateTo(UsageStatsManager.INTERVAL_DAILY, mDailyExpiryDate);
- Slog.i(TAG, mLogPrefix + "Rollover scheduled for "
- + sDateFormat.format(mDailyExpiryDate.getTime()));
+ mDailyExpiryDate.addDays(1);
+ mDailyExpiryDate.truncateToDay();
+ Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
+ sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) +
+ "(" + mDailyExpiryDate.getTimeInMillis() + ")");
}
// Now close off any events that were open at the time this was saved.
for (IntervalStats stat : mCurrentStats) {
- final int pkgCount = stat.stats.size();
+ final int pkgCount = stat.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
- UsageStats pkgStats = stat.stats.valueAt(i);
+ UsageStats pkgStats = stat.packageStats.valueAt(i);
if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
stat.update(pkgStats.mPackageName, stat.lastTimeSaved,
@@ -108,121 +118,202 @@ class UserUsageStatsService {
notifyStatsChanged();
}
}
+
+ stat.updateConfigurationStats(null, stat.lastTimeSaved);
}
}
void reportEvent(UsageEvents.Event event) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
- + "[" + event.getTimeStamp() + "]: "
- + eventToString(event.getEventType()));
+ + "[" + event.mTimeStamp + "]: "
+ + eventToString(event.mEventType));
}
- if (event.getTimeStamp() >= mDailyExpiryDate.getTimeInMillis()) {
+ if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
rolloverStats();
}
- if (mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events == null) {
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events = new TimeSparseArray<>();
+ final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
+
+ final Configuration newFullConfig = event.mConfiguration;
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
+ currentDailyStats.activeConfiguration != null) {
+ // Make the event configuration a delta.
+ event.mConfiguration = Configuration.generateDelta(
+ currentDailyStats.activeConfiguration, newFullConfig);
+ }
+
+ // Add the event to the daily list.
+ if (currentDailyStats.events == null) {
+ currentDailyStats.events = new TimeSparseArray<>();
}
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events.put(event.getTimeStamp(), event);
+ currentDailyStats.events.put(event.mTimeStamp, event);
for (IntervalStats stats : mCurrentStats) {
- stats.update(event.mPackage, event.getTimeStamp(),
- event.getEventType());
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
+ } else {
+ stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ }
}
notifyStatsChanged();
}
- List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
- if (bucketType == UsageStatsManager.INTERVAL_BEST) {
- bucketType = mDatabase.findBestFitBucket(beginTime, endTime);
+ private static final StatCombiner<UsageStats> sUsageStatsCombiner =
+ new StatCombiner<UsageStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<UsageStats> accResult) {
+ if (!mutable) {
+ accResult.addAll(stats.packageStats.values());
+ return;
+ }
+
+ final int statCount = stats.packageStats.size();
+ for (int i = 0; i < statCount; i++) {
+ accResult.add(new UsageStats(stats.packageStats.valueAt(i)));
+ }
+ }
+ };
+
+ private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
+ new StatCombiner<ConfigurationStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<ConfigurationStats> accResult) {
+ if (!mutable) {
+ accResult.addAll(stats.configurations.values());
+ return;
+ }
+
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
+ }
+ }
+ };
+
+ /**
+ * Generic query method that selects the appropriate IntervalStats for the specified time range
+ * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
+ * provided to select the stats to use from the IntervalStats object.
+ */
+ private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
+ StatCombiner<T> combiner) {
+ if (intervalType == UsageStatsManager.INTERVAL_BEST) {
+ intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
+ if (intervalType < 0) {
+ // Nothing saved to disk yet, so every stat is just as equal (no rollover has
+ // occurred.
+ intervalType = UsageStatsManager.INTERVAL_DAILY;
+ }
}
- if (bucketType < 0 || bucketType >= mCurrentStats.length) {
+ if (intervalType < 0 || intervalType >= mCurrentStats.length) {
if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Bad bucketType used " + bucketType);
+ Slog.d(TAG, mLogPrefix + "Bad intervalType used " + intervalType);
}
return null;
}
- if (beginTime >= mCurrentStats[bucketType].endTime) {
+ final IntervalStats currentStats = mCurrentStats[intervalType];
+
+ if (DEBUG) {
+ Slog.d(TAG, mLogPrefix + "SELECT * FROM " + intervalType + " WHERE beginTime >= "
+ + beginTime + " AND endTime < " + endTime);
+ }
+
+ if (beginTime >= currentStats.endTime) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
- + mCurrentStats[bucketType].endTime);
+ + currentStats.endTime);
}
// Nothing newer available.
return null;
-
- } else if (beginTime >= mCurrentStats[bucketType].beginTime) {
- if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Returning in-memory stats for bucket " + bucketType);
- }
- // Fast path for retrieving in-memory state.
- ArrayList<UsageStats> results = new ArrayList<>();
- final int packageCount = mCurrentStats[bucketType].stats.size();
- for (int i = 0; i < packageCount; i++) {
- results.add(new UsageStats(mCurrentStats[bucketType].stats.valueAt(i)));
- }
- return results;
}
- // Flush any changes that were made to disk before we do a disk query.
- // If we're not grabbing the ongoing stats, no need to persist.
- persistActiveStats();
+ // Truncate the endTime to just before the in-memory stats. Then, we'll append the
+ // in-memory stats to the results (if necessary) so as to avoid writing to disk too
+ // often.
+ final long truncatedEndTime = Math.min(currentStats.beginTime, endTime);
+ // Get the stats from disk.
+ List<T> results = mDatabase.queryUsageStats(intervalType, beginTime,
+ truncatedEndTime, combiner);
if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "SELECT * FROM " + bucketType + " WHERE beginTime >= "
- + beginTime + " AND endTime < " + endTime);
+ Slog.d(TAG, "Got " + (results != null ? results.size() : 0) + " results from disk");
+ Slog.d(TAG, "Current stats beginTime=" + currentStats.beginTime +
+ " endTime=" + currentStats.endTime);
+ }
+
+ // Now check if the in-memory stats match the range and add them if they do.
+ if (beginTime < currentStats.endTime && endTime > currentStats.beginTime) {
+ if (DEBUG) {
+ Slog.d(TAG, mLogPrefix + "Returning in-memory stats");
+ }
+
+ if (results == null) {
+ results = new ArrayList<>();
+ }
+ combiner.combine(currentStats, true, results);
}
- final List<UsageStats> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime);
if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Results: " + (results == null ? 0 : results.size()));
+ Slog.d(TAG, mLogPrefix + "Results: " + (results != null ? results.size() : 0));
}
return results;
}
- UsageEvents queryEvents(long beginTime, long endTime) {
- if (endTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime) {
- if (beginTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].endTime) {
- return null;
- }
+ List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
+ return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
+ }
- TimeSparseArray<UsageEvents.Event> events =
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events;
- if (events == null) {
- return null;
- }
+ List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
+ return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
+ }
- final int startIndex = events.closestIndexOnOrAfter(beginTime);
- if (startIndex < 0) {
- return null;
- }
+ UsageEvents queryEvents(final long beginTime, final long endTime) {
+ final ArraySet<String> names = new ArraySet<>();
+ List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
+ beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<UsageEvents.Event> accumulatedResult) {
+ if (stats.events == null) {
+ return;
+ }
+
+ final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
+ if (startIndex < 0) {
+ return;
+ }
+
+ final int size = stats.events.size();
+ for (int i = startIndex; i < size; i++) {
+ if (stats.events.keyAt(i) >= endTime) {
+ return;
+ }
+
+ final UsageEvents.Event event = stats.events.valueAt(i);
+ names.add(event.mPackage);
+ if (event.mClass != null) {
+ names.add(event.mClass);
+ }
+ accumulatedResult.add(event);
+ }
+ }
+ });
- ArraySet<String> names = new ArraySet<>();
- ArrayList<UsageEvents.Event> results = new ArrayList<>();
- final int size = events.size();
- for (int i = startIndex; i < size; i++) {
- if (events.keyAt(i) >= endTime) {
- break;
- }
- final UsageEvents.Event event = events.valueAt(i);
- names.add(event.mPackage);
- if (event.mClass != null) {
- names.add(event.mClass);
- }
- results.add(event);
- }
- String[] table = names.toArray(new String[names.size()]);
- Arrays.sort(table);
- return new UsageEvents(results, table);
+ if (results == null || results.isEmpty()) {
+ return null;
}
- // TODO(adamlesinski): Query the previous days.
- return null;
+ String[] table = names.toArray(new String[names.size()]);
+ Arrays.sort(table);
+ return new UsageEvents(results, table);
}
void persistActiveStats() {
@@ -245,19 +336,23 @@ class UserUsageStatsService {
// Finish any ongoing events with an END_OF_DAY event. Make a note of which components
// need a new CONTINUE_PREVIOUS_DAY entry.
+ final Configuration previousConfig =
+ mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
ArraySet<String> continuePreviousDay = new ArraySet<>();
for (IntervalStats stat : mCurrentStats) {
- final int pkgCount = stat.stats.size();
+ final int pkgCount = stat.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
- UsageStats pkgStats = stat.stats.valueAt(i);
+ UsageStats pkgStats = stat.packageStats.valueAt(i);
if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
continuePreviousDay.add(pkgStats.mPackageName);
- stat.update(pkgStats.mPackageName,
- mDailyExpiryDate.getTimeInMillis() - 1, UsageEvents.Event.END_OF_DAY);
+ stat.update(pkgStats.mPackageName, mDailyExpiryDate.getTimeInMillis() - 1,
+ UsageEvents.Event.END_OF_DAY);
mStatsChanged = true;
}
}
+
+ stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
}
persistActiveStats();
@@ -267,9 +362,10 @@ class UserUsageStatsService {
final int continueCount = continuePreviousDay.size();
for (int i = 0; i < continueCount; i++) {
String name = continuePreviousDay.valueAt(i);
+ final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
for (IntervalStats stat : mCurrentStats) {
- stat.update(name, mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime,
- UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ stat.update(name, beginTime, UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ stat.updateConfigurationStats(previousConfig, beginTime);
mStatsChanged = true;
}
}
@@ -290,54 +386,53 @@ class UserUsageStatsService {
private void loadActiveStats() {
final long timeNow = System.currentTimeMillis();
- Calendar tempCal = mDailyExpiryDate;
- for (int bucketType = 0; bucketType < mCurrentStats.length; bucketType++) {
+ final UnixCalendar tempCal = mDailyExpiryDate;
+ for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
tempCal.setTimeInMillis(timeNow);
- UsageStatsUtils.truncateDateTo(bucketType, tempCal);
+ UnixCalendar.truncateTo(tempCal, intervalType);
- if (mCurrentStats[bucketType] != null &&
- mCurrentStats[bucketType].beginTime == tempCal.getTimeInMillis()) {
+ if (mCurrentStats[intervalType] != null &&
+ mCurrentStats[intervalType].beginTime == tempCal.getTimeInMillis()) {
// These are the same, no need to load them (in memory stats are always newer
// than persisted stats).
continue;
}
- final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(bucketType);
- if (lastBeginTime >= tempCal.getTimeInMillis()) {
+ final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(intervalType);
+ if (lastBeginTime > timeNow) {
+ Slog.e(TAG, mLogPrefix + "Latest usage stats for interval " +
+ intervalType + " begins in the future");
+ mCurrentStats[intervalType] = null;
+ } else if (lastBeginTime >= tempCal.getTimeInMillis()) {
if (DEBUG) {
- Slog.d(TAG, mLogPrefix + "Loading existing stats (" + lastBeginTime +
- ") for bucket " + bucketType);
- }
- mCurrentStats[bucketType] = mDatabase.getLatestUsageStats(bucketType);
- if (DEBUG) {
- if (mCurrentStats[bucketType] != null) {
- Slog.d(TAG, mLogPrefix + "Found " +
- (mCurrentStats[bucketType].events == null ?
- 0 : mCurrentStats[bucketType].events.size()) +
- " events");
- }
+ Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
+ sDateFormat.format(lastBeginTime) + "(" + lastBeginTime +
+ ") for interval " + intervalType);
}
+ mCurrentStats[intervalType] = mDatabase.getLatestUsageStats(intervalType);
} else {
- mCurrentStats[bucketType] = null;
+ mCurrentStats[intervalType] = null;
}
- if (mCurrentStats[bucketType] == null) {
+ if (mCurrentStats[intervalType] == null) {
if (DEBUG) {
- Slog.d(TAG, "Creating new stats (" + tempCal.getTimeInMillis() +
- ") for bucket " + bucketType);
+ Slog.d(TAG, "Creating new stats @ " +
+ sDateFormat.format(tempCal.getTimeInMillis()) + "(" +
+ tempCal.getTimeInMillis() + ") for interval " + intervalType);
}
- mCurrentStats[bucketType] = new IntervalStats();
- mCurrentStats[bucketType].beginTime = tempCal.getTimeInMillis();
- mCurrentStats[bucketType].endTime = timeNow;
+ mCurrentStats[intervalType] = new IntervalStats();
+ mCurrentStats[intervalType].beginTime = tempCal.getTimeInMillis();
+ mCurrentStats[intervalType].endTime = timeNow;
}
}
mStatsChanged = false;
mDailyExpiryDate.setTimeInMillis(timeNow);
- mDailyExpiryDate.add(Calendar.DAY_OF_YEAR, 1);
- UsageStatsUtils.truncateDateTo(UsageStatsManager.INTERVAL_DAILY, mDailyExpiryDate);
- Slog.i(TAG, mLogPrefix + "Rollover scheduled for "
- + sDateFormat.format(mDailyExpiryDate.getTime()));
+ mDailyExpiryDate.addDays(1);
+ mDailyExpiryDate.truncateToDay();
+ Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
+ sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
+ tempCal.getTimeInMillis() + ")");
}
@@ -353,6 +448,8 @@ class UserUsageStatsService {
return "END_OF_DAY";
case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
return "CONTINUE_PREVIOUS_DAY";
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ return "CONFIGURATION_CHANGE";
default:
return "UNKNOWN";
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
index 376230b..ad38b22 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
@@ -67,7 +67,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
final ModuleProperties moduleProperties;
/** The properties for the DSP module */
- private final SoundTriggerModule mModule;
+ private SoundTriggerModule mModule;
private final Object mLock = new Object();
private final Context mContext;
private final TelephonyManager mTelephonyManager;
@@ -105,7 +105,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
} else {
// TODO: Figure out how to determine which module corresponds to the DSP hardware.
moduleProperties = modules.get(0);
- mModule = SoundTrigger.attachModule(moduleProperties.id, this, null);
}
}
@@ -155,10 +154,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mIsPowerSaveMode = mPowerManager.isPowerSaveMode();
}
- if (moduleProperties == null || mModule == null) {
+ if (moduleProperties == null) {
Slog.w(TAG, "Attempting startRecognition without the capability");
return STATUS_ERROR;
}
+ if (mModule == null) {
+ mModule = SoundTrigger.attachModule(moduleProperties.id, this, null);
+ if (mModule == null) {
+ Slog.w(TAG, "startRecognition cannot attach to sound trigger module");
+ return STATUS_ERROR;
+ }
+ }
if (mCurrentSoundModelHandle != INVALID_VALUE
&& !soundModel.uuid.equals(mCurrentSoundModelUuid)) {
@@ -446,6 +452,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
Slog.w(TAG, "RemoteException in onError", e);
} finally {
internalClearStateLocked();
+ if (mModule != null) {
+ mModule.detach();
+ mModule = null;
+ }
}
}