summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/appwidget/Android.mk2
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java76
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java137
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java13
-rw-r--r--services/core/java/com/android/server/BootReceiver.java260
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java201
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java63
-rw-r--r--services/core/java/com/android/server/IntentResolver.java118
-rw-r--r--services/core/java/com/android/server/NetworkScoreService.java12
-rw-r--r--services/core/java/com/android/server/SystemService.java199
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java227
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java2
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java4
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityManagerService.java92
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java17
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java4
-rw-r--r--services/core/java/com/android/server/am/CoreSettingsObserver.java28
-rw-r--r--services/core/java/com/android/server/am/LockTaskNotify.java7
-rw-r--r--services/core/java/com/android/server/am/LockToAppRequestDialog.java9
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java3
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java9
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java56
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java2
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java172
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java16
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerState.java26
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java12
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayAdapter.java4
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java169
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java9
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecKeycode.java50
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java72
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java104
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java15
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java149
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiUtils.java16
-rw-r--r--services/core/java/com/android/server/hdmi/HotplugDetectionAction.java2
-rw-r--r--services/core/java/com/android/server/hdmi/SendKeyAction.java15
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java2
-rw-r--r--services/core/java/com/android/server/lights/LightsService.java8
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java95
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java6
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java3
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java345
-rw-r--r--services/core/java/com/android/server/net/IpConfigStore.java90
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java24
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java20
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java19
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java90
-rw-r--r--services/core/java/com/android/server/notification/CountdownConditionProvider.java5
-rw-r--r--services/core/java/com/android/server/notification/DowntimeConditionProvider.java289
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java3
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java3
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java31
-rw-r--r--services/core/java/com/android/server/notification/ZenLog.java47
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java138
-rw-r--r--services/core/java/com/android/server/pm/Installer.java290
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java394
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java230
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java640
-rw-r--r--services/core/java/com/android/server/pm/PreferredComponent.java36
-rw-r--r--services/core/java/com/android/server/power/Notifier.java1
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java353
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java39
-rw-r--r--services/core/java/com/android/server/storage/DeviceStorageMonitorService.java2
-rw-r--r--services/core/java/com/android/server/tv/PersistentDataStore.java32
-rw-r--r--services/core/java/com/android/server/tv/TvInputHal.java11
-rw-r--r--services/core/java/com/android/server/tv/TvInputHardwareManager.java34
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java93
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java9
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java69
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java6
-rw-r--r--services/core/jni/onload.cpp4
-rw-r--r--services/devicepolicy/Android.mk2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java298
-rw-r--r--services/print/Android.mk2
-rw-r--r--services/print/java/com/android/server/print/PrintManagerService.java4
-rw-r--r--services/restrictions/Android.mk2
-rw-r--r--services/usage/Android.mk2
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java4
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java16
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java96
86 files changed, 4046 insertions, 2240 deletions
diff --git a/services/appwidget/Android.mk b/services/appwidget/Android.mk
index ca38f2f..e9bab4a 100644
--- a/services/appwidget/Android.mk
+++ b/services/appwidget/Android.mk
@@ -7,4 +7,6 @@ LOCAL_MODULE := services.appwidget
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 609ffda..c6aa30b 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -20,6 +20,7 @@ import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
@@ -102,7 +103,8 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
-class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider {
+class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider,
+ OnCrossProfileWidgetProvidersChangeListener {
private static final String TAG = "AppWidgetServiceImpl";
private static boolean DEBUG = false;
@@ -199,6 +201,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
mSecurityPolicy = new SecurityPolicy();
computeMaximumWidgetBitmapMemory();
registerBroadcastReceiver();
+ registerOnCrossProfileProvidersChangedListener();
}
private void computeMaximumWidgetBitmapMemory() {
@@ -243,6 +246,15 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
userFilter, null, null);
}
+ private void registerOnCrossProfileProvidersChangedListener() {
+ DevicePolicyManagerInternal devicePolicyManager = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+ // The device policy is an optional component.
+ if (devicePolicyManager != null) {
+ devicePolicyManager.addOnCrossProfileWidgetProvidersChangeListener(this);
+ }
+ }
+
public void setSafeMode(boolean safeMode) {
mSafeMode = safeMode;
}
@@ -368,7 +380,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
saveGroupStateAsync(userId);
// If the set of providers has been modified, notify each active AppWidgetHost
- scheduleNotifyHostsForProvidersChangedLocked();
+ scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
}
}
}
@@ -1657,11 +1669,27 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
- private void scheduleNotifyHostsForProvidersChangedLocked() {
+ private void scheduleNotifyGroupHostsForProvidersChangedLocked(int userId) {
+ final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
+
final int N = mHosts.size();
for (int i = N - 1; i >= 0; i--) {
Host host = mHosts.get(i);
+ boolean hostInGroup = false;
+ final int M = profileIds.length;
+ for (int j = 0; j < M; j++) {
+ final int profileId = profileIds[j];
+ if (host.getUserId() == profileId) {
+ hostInGroup = true;
+ break;
+ }
+ }
+
+ if (!hostInGroup) {
+ continue;
+ }
+
if (host == null || host.zombie || host.callbacks == null) {
continue;
}
@@ -2617,6 +2645,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private void onUserStopped(int userId) {
synchronized (mLock) {
+ boolean providersChanged = false;
+ boolean crossProfileWidgetsChanged = false;
+
// Remove widgets that have both host and provider in the user.
final int widgetCount = mWidgets.size();
for (int i = widgetCount - 1; i >= 0; i--) {
@@ -2645,6 +2676,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
for (int i = hostCount - 1; i >= 0; i--) {
Host host = mHosts.get(i);
if (host.getUserId() == userId) {
+ crossProfileWidgetsChanged |= !host.widgets.isEmpty();
deleteHostLocked(host);
}
}
@@ -2654,6 +2686,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
for (int i = providerCount - 1; i >= 0; i--) {
Provider provider = mProviders.get(i);
if (provider.getUserId() == userId) {
+ crossProfileWidgetsChanged |= !provider.widgets.isEmpty();
+ providersChanged = true;
deleteProviderLocked(provider);
}
}
@@ -2678,6 +2712,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (nextIdIndex >= 0) {
mNextAppWidgetIds.removeAt(nextIdIndex);
}
+
+ // Announce removed provider changes to all hosts in the group.
+ if (providersChanged) {
+ scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+ }
+
+ // Save state if removing a profile changed the group state.
+ // Nothing will be saved if the group parent was removed.
+ if (crossProfileWidgetsChanged) {
+ saveGroupStateAsync(userId);
+ }
}
}
@@ -2843,6 +2888,31 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
+ @Override
+ public void onCrossProfileWidgetProvidersChanged(int userId, List<String> packages) {
+ final int parentId = mSecurityPolicy.getProfileParent(userId);
+ // We care only if the white-listed package is in a profile of
+ // the group parent as only the parent can add widgets from the
+ // profile and not the other way around.
+ if (parentId != userId) {
+ synchronized (mLock) {
+ boolean providersChanged = false;
+
+ final int packageCount = packages.size();
+ for (int i = 0; i < packageCount; i++) {
+ String packageName = packages.get(i);
+ providersChanged |= updateProvidersForPackageLocked(packageName,
+ userId, null);
+ }
+
+ if (providersChanged) {
+ saveGroupStateAsync(userId);
+ scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+ }
+ }
+ }
+ }
+
private final class CallbackHandler extends Handler {
public static final int MSG_NOTIFY_UPDATE_APP_WIDGET = 1;
public static final int MSG_NOTIFY_PROVIDER_CHANGED = 2;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b2d1b71..b576324 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -34,7 +34,6 @@ import android.app.backup.IBackupManager;
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
-import android.app.job.JobParameters;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -79,6 +78,7 @@ import android.os.storage.IMountService;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
@@ -308,12 +308,13 @@ public class BackupManagerService extends IBackupManager.Stub {
volatile boolean mClearingData;
// Transport bookkeeping
- final HashMap<String,String> mTransportNames
- = new HashMap<String,String>(); // component name -> registration name
- final HashMap<String,IBackupTransport> mTransports
- = new HashMap<String,IBackupTransport>(); // registration name -> binder
- final ArrayList<TransportConnection> mTransportConnections
- = new ArrayList<TransportConnection>();
+ final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
+ final ArrayMap<String,String> mTransportNames
+ = new ArrayMap<String,String>(); // component name -> registration name
+ final ArrayMap<String,IBackupTransport> mTransports
+ = new ArrayMap<String,IBackupTransport>(); // registration name -> binder
+ final ArrayMap<String,TransportConnection> mTransportConnections
+ = new ArrayMap<String,TransportConnection>();
String mCurrentTransport;
ActiveRestoreSession mActiveRestoreSession;
@@ -867,10 +868,12 @@ public class BackupManagerService extends IBackupManager.Stub {
// the timeout is suspended while a restore is in progress. Clean
// up now.
Slog.w(TAG, "Restore session timed out; aborting");
+ mActiveRestoreSession.markTimedOut();
post(mActiveRestoreSession.new EndRestoreRunnable(
BackupManagerService.this, mActiveRestoreSession));
}
}
+ break;
}
case MSG_FULL_CONFIRMATION_TIMEOUT:
@@ -1062,9 +1065,8 @@ public class BackupManagerService extends IBackupManager.Stub {
if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
// Find transport hosts and bind to their services
- Intent transportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
- transportServiceIntent, 0, UserHandle.USER_OWNER);
+ mTransportServiceIntent, 0, UserHandle.USER_OWNER);
if (DEBUG) {
Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
}
@@ -1080,17 +1082,7 @@ public class BackupManagerService extends IBackupManager.Stub {
ServiceInfo info = hosts.get(i).serviceInfo;
PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
- ComponentName svcName = new ComponentName(info.packageName, info.name);
- if (DEBUG) {
- Slog.i(TAG, "Binding to transport host " + svcName);
- }
- Intent intent = new Intent(transportServiceIntent);
- intent.setComponent(svcName);
- TransportConnection connection = new TransportConnection();
- mTransportConnections.add(connection);
- context.bindServiceAsUser(intent,
- connection, Context.BIND_AUTO_CREATE,
- UserHandle.OWNER);
+ bindTransport(info);
} else {
Slog.w(TAG, "Transport package not privileged: " + info.packageName);
}
@@ -1750,6 +1742,7 @@ public class BackupManagerService extends IBackupManager.Stub {
String action = intent.getAction();
boolean replacing = false;
boolean added = false;
+ boolean rebind = false;
Bundle extras = intent.getExtras();
String pkgList[] = null;
if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
@@ -1762,7 +1755,7 @@ public class BackupManagerService extends IBackupManager.Stub {
if (pkgName != null) {
pkgList = new String[] { pkgName };
}
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ rebind = added = Intent.ACTION_PACKAGE_ADDED.equals(action);
replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
added = true;
@@ -1795,6 +1788,20 @@ public class BackupManagerService extends IBackupManager.Stub {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob();
}
+
+ // if this was the PACKAGE_ADDED conclusion of an upgrade of the package
+ // hosting one of our transports, we need to explicitly rebind now.
+ if (rebind) {
+ synchronized (mTransportConnections) {
+ final TransportConnection conn = mTransportConnections.get(packageName);
+ if (conn != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "Transport package changed; rebinding");
+ }
+ bindTransport(conn.mTransport);
+ }
+ }
+ }
} catch (NameNotFoundException e) {
// doesn't really exist; ignore it
if (DEBUG) {
@@ -1802,6 +1809,7 @@ public class BackupManagerService extends IBackupManager.Stub {
}
}
}
+
} else {
if (replacing) {
// The package is being updated. We'll receive a PACKAGE_ADDED shortly.
@@ -1816,6 +1824,12 @@ public class BackupManagerService extends IBackupManager.Stub {
// ----- Track connection to transports service -----
class TransportConnection implements ServiceConnection {
+ ServiceInfo mTransport;
+
+ public TransportConnection(ServiceInfo transport) {
+ mTransport = transport;
+ }
+
@Override
public void onServiceConnected(ComponentName component, IBinder service) {
if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
@@ -1839,6 +1853,32 @@ public class BackupManagerService extends IBackupManager.Stub {
}
};
+ void bindTransport(ServiceInfo transport) {
+ ComponentName svcName = new ComponentName(transport.packageName, transport.name);
+ if (DEBUG) {
+ Slog.i(TAG, "Binding to transport host " + svcName);
+ }
+ Intent intent = new Intent(mTransportServiceIntent);
+ intent.setComponent(svcName);
+
+ TransportConnection connection;
+ synchronized (mTransportConnections) {
+ connection = mTransportConnections.get(transport.packageName);
+ if (null == connection) {
+ connection = new TransportConnection(transport);
+ mTransportConnections.put(transport.packageName, connection);
+ } else {
+ // This is a rebind due to package upgrade. The service won't be
+ // automatically relaunched for us until we explicitly rebind, but
+ // we need to unbind the now-orphaned original connection.
+ mContext.unbindService(connection);
+ }
+ }
+ mContext.bindServiceAsUser(intent,
+ connection, Context.BIND_AUTO_CREATE,
+ UserHandle.OWNER);
+ }
+
// Add the backup agents in the given packages to our set of known backup participants.
// If 'packageNames' is null, adds all backup agents in the whole system.
void addPackageParticipantsLocked(String[] packageNames) {
@@ -7538,6 +7578,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
}
+ // Clear any ongoing session timeout.
+ mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
+
// If we have a PM token, we must under all circumstances be sure to
// handshake when we've finished.
if (mPmToken > 0) {
@@ -7545,6 +7588,11 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
try {
mPackageManagerBinder.finishPackageInstall(mPmToken);
} catch (RemoteException e) { /* can't happen */ }
+ } else {
+ // We were invoked via an active restore session, not by the Package
+ // Manager, so start up the session timeout again.
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
+ TIMEOUT_RESTORE_INTERVAL);
}
// Kick off any work that may be needed regarding app widget restores
@@ -7558,11 +7606,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
writeRestoreTokens();
}
- // Furthermore we need to reset the session timeout clock
- mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
- mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
- TIMEOUT_RESTORE_INTERVAL);
-
// done; we can finally release the wakelock and be legitimately done.
Slog.i(TAG, "Restore complete.");
mWakelock.release();
@@ -8599,7 +8642,15 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
skip = true;
}
- if (!skip && mAutoRestore && mProvisioned) {
+ if (!mAutoRestore || !mProvisioned) {
+ if (DEBUG) {
+ Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore
+ + " prov=" + mProvisioned);
+ }
+ skip = true;
+ }
+
+ if (!skip) {
try {
// okay, we're going to attempt a restore of this package from this restore set.
// The eventual message back into the Package Manager to run the post-install
@@ -8632,7 +8683,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
if (skip) {
// Auto-restore disabled or no way to attempt a restore; just tell the Package
// Manager to proceed with the post-install handling for this package.
- if (DEBUG) Slog.v(TAG, "Skipping");
+ if (DEBUG) Slog.v(TAG, "Finishing install immediately");
try {
mPackageManagerBinder.finishPackageInstall(token);
} catch (RemoteException e) { /* can't happen */ }
@@ -8726,12 +8777,17 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
private IBackupTransport mRestoreTransport = null;
RestoreSet[] mRestoreSets = null;
boolean mEnded = false;
+ boolean mTimedOut = false;
ActiveRestoreSession(String packageName, String transport) {
mPackageName = packageName;
mRestoreTransport = getTransport(transport);
}
+ public void markTimedOut() {
+ mTimedOut = true;
+ }
+
// --- Binder interface ---
public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -8744,6 +8800,11 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
throw new IllegalStateException("Restore session already ended");
}
+ if (mTimedOut) {
+ Slog.i(TAG, "Session already timed out");
+ return -1;
+ }
+
long oldId = Binder.clearCallingIdentity();
try {
if (mRestoreTransport == null) {
@@ -8775,6 +8836,11 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
throw new IllegalStateException("Restore session already ended");
}
+ if (mTimedOut) {
+ Slog.i(TAG, "Session already timed out");
+ return -1;
+ }
+
if (mRestoreTransport == null || mRestoreSets == null) {
Slog.e(TAG, "Ignoring restoreAll() with no restore set");
return -1;
@@ -8849,6 +8915,11 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
throw new IllegalStateException("Restore session already ended");
}
+ if (mTimedOut) {
+ Slog.i(TAG, "Session already timed out");
+ return -1;
+ }
+
if (mRestoreTransport == null || mRestoreSets == null) {
Slog.e(TAG, "Ignoring restoreAll() with no restore set");
return -1;
@@ -8897,6 +8968,11 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
throw new IllegalStateException("Restore session already ended");
}
+ if (mTimedOut) {
+ Slog.i(TAG, "Session already timed out");
+ return -1;
+ }
+
if (mPackageName != null) {
if (! mPackageName.equals(packageName)) {
Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
@@ -8994,6 +9070,11 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
public synchronized void endRestoreSession() {
if (DEBUG) Slog.d(TAG, "endRestoreSession");
+ if (mTimedOut) {
+ Slog.i(TAG, "Session already timed out");
+ return;
+ }
+
if (mEnded) {
throw new IllegalStateException("Restore session already ended");
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 07c9048..7b64139 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -121,6 +121,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private int mState;
private final BluetoothHandler mHandler;
private int mErrorRecoveryRetryCounter;
+ private final int mSystemUiUid;
private void registerForAirplaneMode(IntentFilter filter) {
final ContentResolver resolver = mContext.getContentResolver();
@@ -218,6 +219,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (isBluetoothPersistedStateOn()) {
mEnableExternal = true;
}
+
+ int sysUiUid = -1;
+ try {
+ sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui",
+ UserHandle.USER_OWNER);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.wtf(TAG, "Unable to resolve SystemUI's UID.", e);
+ }
+ mSystemUiUid = sysUiUid;
}
/**
@@ -1118,7 +1128,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
try {
foregroundUser = ActivityManager.getCurrentUser();
valid = (callingUser == foregroundUser) ||
- callingAppId == Process.NFC_UID;
+ callingAppId == Process.NFC_UID ||
+ callingAppId == mSystemUiUid;
if (DBG) {
Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
+ " callingUser=" + callingUser
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
deleted file mode 100644
index 7249985..0000000
--- a/services/core/java/com/android/server/BootReceiver.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.IPackageManager;
-import android.os.Build;
-import android.os.DropBoxManager;
-import android.os.FileObserver;
-import android.os.FileUtils;
-import android.os.RecoverySystem;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.provider.Downloads;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Performs a number of miscellaneous, non-system-critical actions
- * after the system has finished booting.
- */
-public class BootReceiver extends BroadcastReceiver {
- private static final String TAG = "BootReceiver";
-
- // Maximum size of a logged event (files get truncated if they're longer).
- // Give userdebug builds a larger max to capture extra debug, esp. for last_kmsg.
- private static final int LOG_SIZE =
- SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536;
-
- private static final File TOMBSTONE_DIR = new File("/data/tombstones");
-
- // The pre-froyo package and class of the system updater, which
- // ran in the system process. We need to remove its packages here
- // in order to clean up after a pre-froyo-to-froyo update.
- private static final String OLD_UPDATER_PACKAGE =
- "com.google.android.systemupdater";
- private static final String OLD_UPDATER_CLASS =
- "com.google.android.systemupdater.SystemUpdateReceiver";
-
- // Keep a reference to the observer so the finalizer doesn't disable it.
- private static FileObserver sTombstoneObserver = null;
-
- @Override
- public void onReceive(final Context context, Intent intent) {
- // Log boot events in the background to avoid blocking the main thread with I/O
- new Thread() {
- @Override
- public void run() {
- try {
- logBootEvents(context);
- } catch (Exception e) {
- Slog.e(TAG, "Can't log boot events", e);
- }
- try {
- boolean onlyCore = false;
- try {
- onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService(
- "package")).isOnlyCoreApps();
- } catch (RemoteException e) {
- }
- if (!onlyCore) {
- removeOldUpdatePackages(context);
- }
- } catch (Exception e) {
- Slog.e(TAG, "Can't remove old update packages", e);
- }
-
- }
- }.start();
- }
-
- private void removeOldUpdatePackages(Context context) {
- Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
- }
-
- private void logBootEvents(Context ctx) throws IOException {
- final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE);
- final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE);
- final String headers = new StringBuilder(512)
- .append("Build: ").append(Build.FINGERPRINT).append("\n")
- .append("Hardware: ").append(Build.BOARD).append("\n")
- .append("Revision: ")
- .append(SystemProperties.get("ro.revision", "")).append("\n")
- .append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
- .append("Radio: ").append(Build.RADIO).append("\n")
- .append("Kernel: ")
- .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
- .append("\n").toString();
- final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
-
- String recovery = RecoverySystem.handleAftermath();
- if (recovery != null && db != null) {
- db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
- }
-
- String lastKmsgFooter = "";
- if (bootReason != null) {
- lastKmsgFooter = new StringBuilder(512)
- .append("\n")
- .append("Boot info:\n")
- .append("Last boot reason: ").append(bootReason).append("\n")
- .toString();
- }
-
- if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
- String now = Long.toString(System.currentTimeMillis());
- SystemProperties.set("ro.runtime.firstboot", now);
- if (db != null) db.addText("SYSTEM_BOOT", headers);
-
- // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile())
- addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
- "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG");
- addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
- "/sys/fs/pstore/console-ramoops", -LOG_SIZE,
- "SYSTEM_LAST_KMSG");
- addFileToDropBox(db, prefs, headers, "/cache/recovery/log",
- -LOG_SIZE, "SYSTEM_RECOVERY_LOG");
- addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console",
- -LOG_SIZE, "APANIC_CONSOLE");
- addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads",
- -LOG_SIZE, "APANIC_THREADS");
- addAuditErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_AUDIT");
- addFsckErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_FSCK");
- } else {
- if (db != null) db.addText("SYSTEM_RESTART", headers);
- }
-
- // Scan existing tombstones (in case any new ones appeared)
- File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
- for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
- addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(),
- LOG_SIZE, "SYSTEM_TOMBSTONE");
- }
-
- // Start watching for new tombstone files; will record them as they occur.
- // This gets registered with the singleton file observer thread.
- sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) {
- @Override
- public void onEvent(int event, String path) {
- try {
- String filename = new File(TOMBSTONE_DIR, path).getPath();
- addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE");
- } catch (IOException e) {
- Slog.e(TAG, "Can't log tombstone", e);
- }
- }
- };
-
- sTombstoneObserver.startWatching();
- }
-
- private static void addFileToDropBox(
- DropBoxManager db, SharedPreferences prefs,
- String headers, String filename, int maxSize, String tag) throws IOException {
- addFileWithFootersToDropBox(db, prefs, headers, "", filename, maxSize,
- tag);
- }
-
- private static void addFileWithFootersToDropBox(
- DropBoxManager db, SharedPreferences prefs,
- String headers, String footers, String filename, int maxSize,
- String tag) throws IOException {
- if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
-
- File file = new File(filename);
- long fileTime = file.lastModified();
- if (fileTime <= 0) return; // File does not exist
-
- if (prefs != null) {
- long lastTime = prefs.getLong(filename, 0);
- if (lastTime == fileTime) return; // Already logged this particular file
- // TODO: move all these SharedPreferences Editor commits
- // outside this function to the end of logBootEvents
- prefs.edit().putLong(filename, fileTime).apply();
- }
-
- Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
- db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + footers);
- }
-
- private static void addAuditErrorsToDropBox(DropBoxManager db, SharedPreferences prefs,
- String headers, int maxSize, String tag) throws IOException {
- if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
- Slog.i(TAG, "Copying audit failures to DropBox");
-
- File file = new File("/proc/last_kmsg");
- long fileTime = file.lastModified();
- if (fileTime <= 0) {
- file = new File("/sys/fs/pstore/console-ramoops");
- fileTime = file.lastModified();
- }
-
- if (fileTime <= 0) return; // File does not exist
-
- if (prefs != null) {
- long lastTime = prefs.getLong(tag, 0);
- if (lastTime == fileTime) return; // Already logged this particular file
- // TODO: move all these SharedPreferences Editor commits
- // outside this function to the end of logBootEvents
- prefs.edit().putLong(tag, fileTime).apply();
- }
-
- String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
- StringBuilder sb = new StringBuilder();
- for (String line : log.split("\n")) {
- if (line.contains("audit")) {
- sb.append(line + "\n");
- }
- }
- Slog.i(TAG, "Copied " + sb.toString().length() + " worth of audits to DropBox");
- db.addText(tag, headers + sb.toString());
- }
-
- private static void addFsckErrorsToDropBox(DropBoxManager db, SharedPreferences prefs,
- String headers, int maxSize, String tag) throws IOException {
- boolean upload_needed = false;
- if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
- Slog.i(TAG, "Checking for fsck errors");
-
- File file = new File("/dev/fscklogs/log");
- long fileTime = file.lastModified();
- if (fileTime <= 0) return; // File does not exist
-
- String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
- StringBuilder sb = new StringBuilder();
- for (String line : log.split("\n")) {
- if (line.contains("FILE SYSTEM WAS MODIFIED")) {
- upload_needed = true;
- break;
- }
- }
-
- if (upload_needed) {
- addFileToDropBox(db, prefs, headers, "/dev/fscklogs/log", maxSize, tag);
- }
-
- // Remove the file so we don't re-upload if the runtime restarts.
- file.delete();
- }
-}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dd5a7ea..96f8324 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -264,6 +264,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private INetworkManagementService mNetd;
private INetworkPolicyManager mPolicyManager;
+ private String mCurrentTcpBufferSizes;
+
private static final int ENABLED = 1;
private static final int DISABLED = 0;
@@ -754,10 +756,20 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return mNextNetworkRequestId++;
}
- private synchronized int nextNetId() {
- int netId = mNextNetId;
- if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
- return netId;
+ private void assignNextNetId(NetworkAgentInfo nai) {
+ synchronized (mNetworkForNetId) {
+ for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
+ int netId = mNextNetId;
+ if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
+ // Make sure NetID unused. http://b/16815182
+ if (mNetworkForNetId.get(netId) == null) {
+ nai.network = new Network(netId);
+ mNetworkForNetId.put(netId, nai);
+ return;
+ }
+ }
+ }
+ throw new IllegalStateException("No free netIds");
}
private int getConnectivityChangeDelay() {
@@ -1534,11 +1546,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return;
}
- if (mtu < 68 || mtu > 10000) {
+ if (LinkProperties.isValidMtu(mtu, newLp.hasGlobalIPv6Address()) == false) {
loge("Unexpected mtu value: " + mtu + ", " + iface);
return;
}
+ // Cannot set MTU without interface name
+ if (TextUtils.isEmpty(iface)) {
+ loge("Setting MTU size with null iface.");
+ return;
+ }
+
try {
if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
mNetd.setMtu(iface, mtu);
@@ -1547,30 +1565,40 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- /**
- * Reads the network specific TCP buffer sizes from SystemProperties
- * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
- * wide use
- */
- private void updateNetworkSettings(NetworkStateTracker nt) {
- String key = nt.getTcpBufferSizesPropName();
- String bufferSizes = key == null ? null : SystemProperties.get(key);
+ private static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
- if (TextUtils.isEmpty(bufferSizes)) {
- if (VDBG) log(key + " not found in system properties. Using defaults");
+ private void updateTcpBufferSizes(NetworkAgentInfo nai) {
+ if (isDefaultNetwork(nai) == false) {
+ return;
+ }
- // Setting to default values so we won't be stuck to previous values
- key = "net.tcp.buffersize.default";
- bufferSizes = SystemProperties.get(key);
+ String tcpBufferSizes = nai.linkProperties.getTcpBufferSizes();
+ String[] values = null;
+ if (tcpBufferSizes != null) {
+ values = tcpBufferSizes.split(",");
}
- // Set values in kernel
- if (bufferSizes.length() != 0) {
- if (VDBG) {
- log("Setting TCP values: [" + bufferSizes
- + "] which comes from [" + key + "]");
- }
- setBufferSize(bufferSizes);
+ if (values == null || values.length != 6) {
+ if (VDBG) log("Invalid tcpBufferSizes string: " + tcpBufferSizes +", using defaults");
+ tcpBufferSizes = DEFAULT_TCP_BUFFER_SIZES;
+ values = tcpBufferSizes.split(",");
+ }
+
+ if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return;
+
+ try {
+ if (VDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
+
+ final String prefix = "/sys/kernel/ipv4/tcp_";
+ FileUtils.stringToFile(prefix + "rmem_min", values[0]);
+ FileUtils.stringToFile(prefix + "rmem_def", values[1]);
+ FileUtils.stringToFile(prefix + "rmem_max", values[2]);
+ FileUtils.stringToFile(prefix + "wmem_min", values[3]);
+ FileUtils.stringToFile(prefix + "wmem_def", values[4]);
+ FileUtils.stringToFile(prefix + "wmem_max", values[5]);
+ mCurrentTcpBufferSizes = tcpBufferSizes;
+ } catch (IOException e) {
+ loge("Can't set TCP buffer sizes:" + e);
}
final String defaultRwndKey = "net.tcp.default_init_rwnd";
@@ -1583,33 +1611,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- /**
- * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
- * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
- *
- * @param bufferSizes in the format of "readMin, readInitial, readMax,
- * writeMin, writeInitial, writeMax"
- */
- private void setBufferSize(String bufferSizes) {
- try {
- String[] values = bufferSizes.split(",");
-
- if (values.length == 6) {
- final String prefix = "/sys/kernel/ipv4/tcp_";
- FileUtils.stringToFile(prefix + "rmem_min", values[0]);
- FileUtils.stringToFile(prefix + "rmem_def", values[1]);
- FileUtils.stringToFile(prefix + "rmem_max", values[2]);
- FileUtils.stringToFile(prefix + "wmem_min", values[3]);
- FileUtils.stringToFile(prefix + "wmem_def", values[4]);
- FileUtils.stringToFile(prefix + "wmem_max", values[5]);
- } else {
- loge("Invalid buffersize string: " + bufferSizes);
- }
- } catch (IOException e) {
- loge("Can't set tcp buffer sizes:" + e);
- }
- }
-
private void flushVmDnsCache() {
/*
* Tell the VMs to toss their DNS caches
@@ -1742,6 +1743,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
+ private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) {
+ final NetworkAgentInfo officialNai;
+ synchronized (mNetworkForNetId) {
+ officialNai = mNetworkForNetId.get(nai.network.netId);
+ }
+ if (officialNai != null && officialNai.equals(nai)) return true;
+ if (officialNai != null || VDBG) {
+ loge(msg + " - validateNetworkAgent found mismatched netId: " + officialNai +
+ " - " + nai);
+ }
+ return false;
+ }
+
// must be stateless - things change under us.
private class NetworkStateTrackerHandler extends Handler {
public NetworkStateTrackerHandler(Looper looper) {
@@ -1871,23 +1885,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
case NetworkMonitor.EVENT_NETWORK_VALIDATED: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
- handleConnectionValidated(nai);
+ if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
+ handleConnectionValidated(nai);
+ }
break;
}
case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
- handleLingerComplete(nai);
+ if (isLiveNetworkAgent(nai, "EVENT_NETWORK_LINGER_COMPLETE")) {
+ handleLingerComplete(nai);
+ }
break;
}
case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
+ NetworkAgentInfo nai = null;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(msg.arg2);
+ }
+ if (nai == null) {
+ loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
+ break;
+ }
if (msg.arg1 == 0) {
setProvNotificationVisibleIntent(false, msg.arg2, 0, null, null);
} else {
- NetworkAgentInfo nai = mNetworkForNetId.get(msg.arg2);
- if (nai == null) {
- loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
- break;
- }
setProvNotificationVisibleIntent(true, msg.arg2, nai.networkInfo.getType(),
nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
}
@@ -1972,12 +1993,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
break;
}
- case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
- info = (NetworkInfo) msg.obj;
- int type = info.getType();
- if (mNetConfigs[type].isDefault()) updateNetworkSettings(mNetTrackers[type]);
- break;
- }
}
}
}
@@ -2104,13 +2119,21 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
if (VDBG) log("apparently satisfied. currentScore=" + network.currentScore);
if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) {
- bestNetwork = network;
+ if (!nri.isRequest) {
+ // Not setting bestNetwork here as a listening NetworkRequest may be
+ // satisfied by multiple Networks. Instead the request is added to
+ // each satisfying Network and notified about each.
+ network.addRequest(nri.request);
+ notifyNetworkCallback(network, nri);
+ } else {
+ bestNetwork = network;
+ }
}
}
}
if (bestNetwork != null) {
if (VDBG) log("using " + bestNetwork.name());
- if (nri.isRequest && bestNetwork.networkInfo.isConnected()) {
+ if (bestNetwork.networkInfo.isConnected()) {
// Cancel any lingering so the linger timeout doesn't teardown this network
// even though we have a request for it.
bestNetwork.networkLingered.clear();
@@ -2120,7 +2143,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetworkForRequestId.put(nri.request.requestId, bestNetwork);
notifyNetworkCallback(bestNetwork, nri);
score = bestNetwork.currentScore;
- if (nri.isRequest && nri.request.legacyType != TYPE_NONE) {
+ if (nri.request.legacyType != TYPE_NONE) {
mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork);
}
}
@@ -2678,6 +2701,20 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
+ * Set whether the current VPN package has the ability to launch VPNs without
+ * user intervention. This method is used by system UIs and not available
+ * in ConnectivityManager. Permissions are checked in Vpn class.
+ * @hide
+ */
+ @Override
+ public void setVpnPackageAuthorization(boolean authorized) {
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ mVpns.get(user).setPackageAuthorization(authorized);
+ }
+ }
+
+ /**
* Configure a TUN interface and return its file descriptor. Parameters
* are encoded and opaque to this class. This method is used by VpnBuilder
* and not available in ConnectivityManager. Permissions are checked in
@@ -3505,10 +3542,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mIsProvisioningNetwork.set(false);
// Check for apps that can handle provisioning first
Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
- provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
- + mTelephonyManager.getSimOperator());
- if (mContext.getPackageManager().resolveActivity(provisioningIntent, 0 /* flags */)
- != null) {
+ List<String> carrierPackages =
+ mTelephonyManager.getCarrierPackageNamesForBroadcastIntent(provisioningIntent);
+ if (carrierPackages != null && !carrierPackages.isEmpty()) {
+ if (carrierPackages.size() != 1) {
+ if (DBG) log("Multiple matching carrier apps found, launching the first.");
+ }
+ provisioningIntent.setPackage(carrierPackages.get(0));
provisioningIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(provisioningIntent);
@@ -3617,6 +3657,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
notification.icon = icon;
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.tickerText = title;
+ notification.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
notification.contentIntent = intent;
@@ -4117,7 +4159,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
int currentScore, NetworkMisc networkMisc) {
enforceConnectivityInternalPermission();
- NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(),
+ NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
networkMisc);
@@ -4131,9 +4173,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
if (VDBG) log("Got NetworkAgent Messenger");
mNetworkAgentInfos.put(na.messenger, na);
- synchronized (mNetworkForNetId) {
- mNetworkForNetId.put(na.network.netId, na);
- }
+ assignNextNetId(na);
na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
NetworkInfo networkInfo = na.networkInfo;
na.networkInfo = null;
@@ -4146,6 +4186,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
updateInterfaces(newLp, oldLp, netId);
updateMtu(newLp, oldLp);
+ updateTcpBufferSizes(networkAgent);
// TODO - figure out what to do for clat
// for (LinkProperties lp : newLp.getStackedLinks()) {
// updateMtu(lp, null);
@@ -4371,6 +4412,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Exception setting default network :" + e);
}
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
+ updateTcpBufferSizes(newNetwork);
}
private void handleConnectionValidated(NetworkAgentInfo newNetwork) {
@@ -4397,6 +4439,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (VDBG) log(" checking if request is satisfied: " + nri.request);
if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
newNetwork.networkCapabilities)) {
+ if (!nri.isRequest) {
+ newNetwork.addRequest(nri.request);
+ continue;
+ }
// next check if it's better than any current network we're using for
// this request
if (VDBG) {
@@ -4428,6 +4474,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
isNewDefault = true;
updateActiveDefaultNetwork(newNetwork);
if (newNetwork.linkProperties != null) {
+ updateTcpBufferSizes(newNetwork);
setDefaultDnsSystemProperties(
newNetwork.linkProperties.getDnsServers());
} else {
@@ -4486,8 +4533,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
1000);
}
}
- // TODO - read the tcp buffer size config string from somewhere
- // updateNetworkSettings();
}
// Notify battery stats service about this network, both the normal
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 4687e3f..122786f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -66,6 +66,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.inputmethodservice.InputMethodService;
+import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -392,6 +393,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private InputMethodInfo[] mIms;
private int[] mSubtypeIds;
private Locale mLastSystemLocale;
+ private boolean mShowImeWithHardKeyboard;
private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
private final IPackageManager mIPackageManager;
@@ -407,17 +409,25 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Settings.Secure.ENABLED_INPUT_METHODS), false, this);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this);
}
- @Override public void onChange(boolean selfChange) {
+ @Override public void onChange(boolean selfChange, Uri uri) {
+ final Uri showImeUri =
+ Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
synchronized (mMethodMap) {
- boolean enabledChanged = false;
- String newEnabled = mSettings.getEnabledInputMethodsStr();
- if (!mLastEnabled.equals(newEnabled)) {
- mLastEnabled = newEnabled;
- enabledChanged = true;
+ if (showImeUri.equals(uri)) {
+ updateKeyboardFromSettingsLocked();
+ } else {
+ boolean enabledChanged = false;
+ String newEnabled = mSettings.getEnabledInputMethodsStr();
+ if (!mLastEnabled.equals(newEnabled)) {
+ mLastEnabled = newEnabled;
+ enabledChanged = true;
+ }
+ updateInputMethodsFromSettingsLocked(enabledChanged);
}
- updateFromSettingsLocked(enabledChanged);
}
}
}
@@ -598,15 +608,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private class HardKeyboardListener
implements WindowManagerService.OnHardKeyboardStatusChangeListener {
@Override
- public void onHardKeyboardStatusChange(boolean available, boolean enabled) {
- mHandler.sendMessage(mHandler.obtainMessage(
- MSG_HARD_KEYBOARD_SWITCH_CHANGED, available ? 1 : 0, enabled ? 1 : 0));
+ public void onHardKeyboardStatusChange(boolean available) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
+ available ? 1 : 0));
}
- public void handleHardKeyboardStatusChange(boolean available, boolean enabled) {
+ public void handleHardKeyboardStatusChange(boolean available) {
if (DEBUG) {
- Slog.w(TAG, "HardKeyboardStatusChanged: available = " + available + ", enabled = "
- + enabled);
+ Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
}
synchronized(mMethodMap) {
if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
@@ -1579,6 +1588,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName(
mContext, imi, mCurrentSubtype);
+ mImeSwitcherNotification.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
mImeSwitcherNotification.setLatestEventInfo(
mContext, title, summary, mImeSwitchPendingIntent);
if ((mNotificationManager != null)
@@ -1656,6 +1667,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
void updateFromSettingsLocked(boolean enabledMayChange) {
+ updateInputMethodsFromSettingsLocked(enabledMayChange);
+ updateKeyboardFromSettingsLocked();
+ }
+
+ void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
if (enabledMayChange) {
List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
for (int i=0; i<enabled.size(); i++) {
@@ -1709,6 +1725,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// TODO: Make sure that mSwitchingController and mSettings are sharing the
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
+
+ }
+
+ public void updateKeyboardFromSettingsLocked() {
+ mShowImeWithHardKeyboard = mSettings.isShowImeWithHardKeyboardEnabled();
+ if (mSwitchingDialog != null
+ && mSwitchingDialogTitleView != null
+ && mSwitchingDialog.isShowing()) {
+ final Switch hardKeySwitch = (Switch)mSwitchingDialogTitleView.findViewById(
+ com.android.internal.R.id.hard_keyboard_switch);
+ hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
+ }
}
/* package */ void setInputMethodLocked(String id, int subtypeId) {
@@ -2593,8 +2621,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// --------------------------------------------------------------
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
- mHardKeyboardListener.handleHardKeyboardStatusChange(
- msg.arg1 == 1, msg.arg2 == 1);
+ mHardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
return true;
}
return false;
@@ -2683,7 +2710,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!map.containsKey(defaultImiId)) {
Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
if (chooseNewDefaultIMELocked()) {
- updateFromSettingsLocked(true);
+ updateInputMethodsFromSettingsLocked(true);
}
} else {
// Double check that the default IME is certainly enabled.
@@ -2810,11 +2837,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
? View.VISIBLE : View.GONE);
final Switch hardKeySwitch = (Switch)mSwitchingDialogTitleView.findViewById(
com.android.internal.R.id.hard_keyboard_switch);
- hardKeySwitch.setChecked(mWindowManagerService.isHardKeyboardEnabled());
+ hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
hardKeySwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mWindowManagerService.setHardKeyboardEnabled(isChecked);
+ mSettings.setShowImeWithHardKeyboard(isChecked);
// Ensure that the input method dialog is dismissed when changing
// the hardware keyboard state.
hideInputMethodMenu();
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 64b0487..07cc864 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -69,6 +69,124 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
}
+ private boolean filterEquals(IntentFilter f1, IntentFilter f2) {
+ int s1 = f1.countActions();
+ int s2 = f2.countActions();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasAction(f1.getAction(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countCategories();
+ s2 = f2.countCategories();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasCategory(f1.getCategory(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataTypes();
+ s2 = f2.countDataTypes();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasExactDataType(f1.getDataType(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataSchemes();
+ s2 = f2.countDataSchemes();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasDataScheme(f1.getDataScheme(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataAuthorities();
+ s2 = f2.countDataAuthorities();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasDataAuthority(f1.getDataAuthority(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataPaths();
+ s2 = f2.countDataPaths();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasDataPath(f1.getDataPath(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataSchemeSpecificParts();
+ s2 = f2.countDataSchemeSpecificParts();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private ArrayList<F> collectFilters(F[] array, IntentFilter matching) {
+ ArrayList<F> res = null;
+ if (array != null) {
+ for (int i=0; i<array.length; i++) {
+ F cur = array[i];
+ if (cur == null) {
+ break;
+ }
+ if (filterEquals(cur, matching)) {
+ if (res == null) {
+ res = new ArrayList<>();
+ }
+ res.add(cur);
+ }
+ }
+ }
+ return res;
+ }
+
+ public ArrayList<F> findFilters(IntentFilter matching) {
+ if (matching.countDataSchemes() == 1) {
+ // Fast case.
+ return collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching);
+ } else if (matching.countDataTypes() != 0 && matching.countActions() == 1) {
+ // Another fast case.
+ return collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching);
+ } else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0
+ && matching.countActions() == 1) {
+ // Last fast case.
+ return collectFilters(mActionToFilter.get(matching.getAction(0)), matching);
+ } else {
+ ArrayList<F> res = null;
+ for (F cur : mFilters) {
+ if (filterEquals(cur, matching)) {
+ if (res == null) {
+ res = new ArrayList<>();
+ }
+ res.add(cur);
+ }
+ }
+ return res;
+ }
+ }
+
public void removeFilter(F f) {
removeFilterInternal(f);
mFilters.remove(f);
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index ab4d4dc..395e365 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -17,9 +17,9 @@
package com.android.server;
import android.Manifest.permission;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.INetworkScoreCache;
import android.net.INetworkScoreService;
@@ -30,6 +30,7 @@ import android.net.ScoredNetwork;
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -51,9 +52,6 @@ import java.util.Set;
public class NetworkScoreService extends INetworkScoreService.Stub {
private static final String TAG = "NetworkScoreService";
- /** SharedPreference bit set to true after the service is first initialized. */
- private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
-
private final Context mContext;
private final Map<Integer, INetworkScoreCache> mScoreCaches;
@@ -65,8 +63,8 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
/** Called when the system is ready to run third-party code but before it actually does so. */
void systemReady() {
- SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
- if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
+ ContentResolver cr = mContext.getContentResolver();
+ if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
// On first run, we try to initialize the scorer to the one configured at build time.
// This will be a no-op if the scorer isn't actually valid.
String defaultPackage = mContext.getResources().getString(
@@ -74,7 +72,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
if (!TextUtils.isEmpty(defaultPackage)) {
NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
}
- prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
+ Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
}
}
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
new file mode 100644
index 0000000..6e67970
--- /dev/null
+++ b/services/core/java/com/android/server/SystemService.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * The base class for services running in the system process. Override and implement
+ * the lifecycle event callback methods as needed.
+ * <p>
+ * The lifecycle of a SystemService:
+ * </p><ul>
+ * <li>The constructor is called and provided with the system {@link Context}
+ * to initialize the system service.
+ * <li>{@link #onStart()} is called to get the service running. The service should
+ * publish its binder interface at this point using
+ * {@link #publishBinderService(String, IBinder)}. It may also publish additional
+ * local interfaces that other services within the system server may use to access
+ * privileged internal functions.
+ * <li>Then {@link #onBootPhase(int)} is called as many times as there are boot phases
+ * until {@link #PHASE_BOOT_COMPLETE} is sent, which is the last boot phase. Each phase
+ * is an opportunity to do special work, like acquiring optional service dependencies,
+ * waiting to see if SafeMode is enabled, or registering with a service that gets
+ * started after this one.
+ * </ul><p>
+ * NOTE: All lifecycle methods are called from the system server's main looper thread.
+ * </p>
+ *
+ * {@hide}
+ */
+public abstract class SystemService {
+ /*
+ * Boot Phases
+ */
+ public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // maybe should be a dependency?
+
+ /**
+ * After receiving this boot phase, services can obtain lock settings data.
+ */
+ public static final int PHASE_LOCK_SETTINGS_READY = 480;
+
+ /**
+ * After receiving this boot phase, services can safely call into core system services
+ * such as the PowerManager or PackageManager.
+ */
+ public static final int PHASE_SYSTEM_SERVICES_READY = 500;
+
+ /**
+ * After receiving this boot phase, services can broadcast Intents.
+ */
+ public static final int PHASE_ACTIVITY_MANAGER_READY = 550;
+
+ /**
+ * After receiving this boot phase, services can start/bind to third party apps.
+ * Apps will be able to make Binder calls into services at this point.
+ */
+ public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600;
+
+ /**
+ * After receiving this boot phase, services can allow user interaction with the device.
+ * This phase occurs when boot has completed and the home application has started.
+ * System services may prefer to listen to this phase rather than registering a
+ * broadcast receiver for ACTION_BOOT_COMPLETED to reduce overall latency.
+ */
+ public static final int PHASE_BOOT_COMPLETED = 1000;
+
+ private final Context mContext;
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public SystemService(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Gets the system context.
+ */
+ public final Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Returns true if the system is running in safe mode.
+ * TODO: we should define in which phase this becomes valid
+ */
+ public final boolean isSafeMode() {
+ return getManager().isSafeMode();
+ }
+
+ /**
+ * Called when the dependencies listed in the @Service class-annotation are available
+ * and after the chosen start phase.
+ * When this method returns, the service should be published.
+ */
+ public abstract void onStart();
+
+ /**
+ * Called on each phase of the boot process. Phases before the service's start phase
+ * (as defined in the @Service annotation) are never received.
+ *
+ * @param phase The current boot phase.
+ */
+ public void onBootPhase(int phase) {}
+
+ /**
+ * Called when a new user is starting, for system services to initialize any per-user
+ * state they maintain for running users.
+ * @param userHandle The identifier of the user.
+ */
+ public void onStartUser(int userHandle) {}
+
+ /**
+ * Called when switching to a different foreground user, for system services that have
+ * special behavior for whichever user is currently in the foreground. This is called
+ * before any application processes are aware of the new user.
+ * @param userHandle The identifier of the user.
+ */
+ public void onSwitchUser(int userHandle) {}
+
+ /**
+ * Called when an existing user is stopping, for system services to finalize any per-user
+ * state they maintain for running users. This is called prior to sending the SHUTDOWN
+ * broadcast to the user; it is a good place to stop making use of any resources of that
+ * user (such as binding to a service running in the user).
+ * @param userHandle The identifier of the user.
+ */
+ public void onStopUser(int userHandle) {}
+
+ /**
+ * Called when an existing user is stopping, for system services to finalize any per-user
+ * state they maintain for running users. This is called after all application process
+ * teardown of the user is complete.
+ * @param userHandle The identifier of the user.
+ */
+ public void onCleanupUser(int userHandle) {}
+
+ /**
+ * Publish the service so it is accessible to other services and apps.
+ */
+ protected final void publishBinderService(String name, IBinder service) {
+ publishBinderService(name, service, false);
+ }
+
+ /**
+ * Publish the service so it is accessible to other services and apps.
+ */
+ protected final void publishBinderService(String name, IBinder service,
+ boolean allowIsolated) {
+ ServiceManager.addService(name, service, allowIsolated);
+ }
+
+ /**
+ * Get a binder service by its name.
+ */
+ protected final IBinder getBinderService(String name) {
+ return ServiceManager.getService(name);
+ }
+
+ /**
+ * Publish the service so it is only accessible to the system process.
+ */
+ protected final <T> void publishLocalService(Class<T> type, T service) {
+ LocalServices.addService(type, service);
+ }
+
+ /**
+ * Get a local service by interface.
+ */
+ protected final <T> T getLocalService(Class<T> type) {
+ return LocalServices.getService(type);
+ }
+
+ private SystemServiceManager getManager() {
+ return LocalServices.getService(SystemServiceManager.class);
+ }
+}
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
new file mode 100644
index 0000000..fda6479
--- /dev/null
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.util.Slog;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * Manages creating, starting, and other lifecycle events of
+ * {@link com.android.server.SystemService system services}.
+ *
+ * {@hide}
+ */
+public class SystemServiceManager {
+ private static final String TAG = "SystemServiceManager";
+
+ private final Context mContext;
+ private boolean mSafeMode;
+
+ // Services that should receive lifecycle events.
+ private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
+
+ private int mCurrentPhase = -1;
+
+ public SystemServiceManager(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Starts a service by class name.
+ *
+ * @return The service instance.
+ */
+ @SuppressWarnings("unchecked")
+ public SystemService startService(String className) {
+ final Class<SystemService> serviceClass;
+ try {
+ serviceClass = (Class<SystemService>)Class.forName(className);
+ } catch (ClassNotFoundException ex) {
+ Slog.i(TAG, "Starting " + className);
+ throw new RuntimeException("Failed to create service " + className
+ + ": service class not found, usually indicates that the caller should "
+ + "have called PackageManager.hasSystemFeature() to check whether the "
+ + "feature is available on this device before trying to start the "
+ + "services that implement it", ex);
+ }
+ return startService(serviceClass);
+ }
+
+ /**
+ * Creates and starts a system service. The class must be a subclass of
+ * {@link com.android.server.SystemService}.
+ *
+ * @param serviceClass A Java class that implements the SystemService interface.
+ * @return The service instance, never null.
+ * @throws RuntimeException if the service fails to start.
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends SystemService> T startService(Class<T> serviceClass) {
+ final String name = serviceClass.getName();
+ Slog.i(TAG, "Starting " + name);
+
+ // Create the service.
+ if (!SystemService.class.isAssignableFrom(serviceClass)) {
+ throw new RuntimeException("Failed to create " + name
+ + ": service must extend " + SystemService.class.getName());
+ }
+ final T service;
+ try {
+ Constructor<T> constructor = serviceClass.getConstructor(Context.class);
+ service = constructor.newInstance(mContext);
+ } catch (InstantiationException ex) {
+ throw new RuntimeException("Failed to create service " + name
+ + ": service could not be instantiated", ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException("Failed to create service " + name
+ + ": service must have a public constructor with a Context argument", ex);
+ } catch (NoSuchMethodException ex) {
+ throw new RuntimeException("Failed to create service " + name
+ + ": service must have a public constructor with a Context argument", ex);
+ } catch (InvocationTargetException ex) {
+ throw new RuntimeException("Failed to create service " + name
+ + ": service constructor threw an exception", ex);
+ }
+
+ // Register it.
+ mServices.add(service);
+
+ // Start it.
+ try {
+ service.onStart();
+ } catch (RuntimeException ex) {
+ throw new RuntimeException("Failed to start service " + name
+ + ": onStart threw an exception", ex);
+ }
+ return service;
+ }
+
+ /**
+ * Starts the specified boot phase for all system services that have been started up to
+ * this point.
+ *
+ * @param phase The boot phase to start.
+ */
+ public void startBootPhase(final int phase) {
+ if (phase <= mCurrentPhase) {
+ throw new IllegalArgumentException("Next phase must be larger than previous");
+ }
+ mCurrentPhase = phase;
+
+ Slog.i(TAG, "Starting phase " + mCurrentPhase);
+
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onBootPhase(mCurrentPhase);
+ } catch (Exception ex) {
+ throw new RuntimeException("Failed to boot service "
+ + service.getClass().getName()
+ + ": onBootPhase threw an exception during phase "
+ + mCurrentPhase, ex);
+ }
+ }
+ }
+
+ public void startUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onStartUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting start of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void switchUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onSwitchUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void stopUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onStopUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void cleanupUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onCleanupUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ /** Sets the safe mode flag for services to query. */
+ public void setSafeMode(boolean safeMode) {
+ mSafeMode = safeMode;
+ }
+
+ /**
+ * Returns whether we are booting into safe mode.
+ * @return safe mode flag
+ */
+ public boolean isSafeMode() {
+ return mSafeMode;
+ }
+
+ /**
+ * Outputs the state of this manager to the System log.
+ */
+ public void dump() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Current phase: ").append(mCurrentPhase).append("\n");
+ builder.append("Services:\n");
+ final int startedLen = mServices.size();
+ for (int i = 0; i < startedLen; i++) {
+ final SystemService service = mServices.get(i);
+ builder.append("\t")
+ .append(service.getClass().getSimpleName())
+ .append("\n");
+ }
+
+ Slog.e(TAG, builder.toString());
+ }
+}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6ab52c6..d1b4569 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -599,6 +599,8 @@ final class UiModeManagerService extends SystemService {
n.defaults = Notification.DEFAULT_LIGHTS;
n.flags = Notification.FLAG_ONGOING_EVENT;
n.when = 0;
+ n.color = context.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
n.setLatestEventInfo(
context,
context.getString(R.string.car_mode_disable_notification_title),
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2a66baf..94d979e 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1700,6 +1700,8 @@ public class AccountManagerService
subtitle = titleAndSubtitle.substring(index + 1);
}
UserHandle user = new UserHandle(userId);
+ n.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
n.setLatestEventInfo(mContext, title, subtitle,
PendingIntent.getActivityAsUser(mContext, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT, null, user));
@@ -2968,6 +2970,8 @@ public class AccountManagerService
UserHandle user = new UserHandle(userId);
final String notificationTitleFormat =
mContext.getText(R.string.notification_title).toString();
+ n.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
n.setLatestEventInfo(mContext,
String.format(notificationTitleFormat, account.name),
message, PendingIntent.getActivityAsUser(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ad2704a..b1d84f5 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1472,6 +1472,8 @@ public final class ActivityManagerService extends ActivityManagerNative
notification.defaults = 0; // please be quiet
notification.sound = null;
notification.vibrate = null;
+ notification.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
notification.setLatestEventInfo(context, text,
mContext.getText(R.string.heavy_weight_notification_detail),
PendingIntent.getActivityAsUser(mContext, 0, root.intent,
@@ -2031,7 +2033,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS);
- mSystemThread.installSystemApplicationInfo(info);
+ mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
@@ -3434,6 +3436,70 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
+ Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, int startFlags,
+ String profileFile, ParcelFileDescriptor profileFd, Bundle options) {
+
+ // This is very dangerous -- it allows you to perform a start activity (including
+ // permission grants) as any app that may launch one of your own activities. So
+ // we will only allow this to be done from activities that are part of the core framework,
+ // and then only when they are running as the system.
+ final ActivityRecord sourceRecord;
+ final int targetUid;
+ final String targetPackage;
+ synchronized (this) {
+ if (resultTo == null) {
+ throw new SecurityException("Must be called from an activity");
+ }
+ sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
+ if (sourceRecord == null) {
+ throw new SecurityException("Called with bad activity token: " + resultTo);
+ }
+ if (!sourceRecord.info.packageName.equals("android")) {
+ throw new SecurityException(
+ "Must be called from an activity that is declared in the android package");
+ }
+ if (sourceRecord.app == null) {
+ throw new SecurityException("Called without a process attached to activity");
+ }
+ if (UserHandle.getAppId(sourceRecord.app.uid) != Process.SYSTEM_UID) {
+ // This is still okay, as long as this activity is running under the
+ // uid of the original calling activity.
+ if (sourceRecord.app.uid != sourceRecord.launchedFromUid) {
+ throw new SecurityException(
+ "Calling activity in uid " + sourceRecord.app.uid
+ + " must be system uid or original calling uid "
+ + sourceRecord.launchedFromUid);
+ }
+ }
+ targetUid = sourceRecord.launchedFromUid;
+ targetPackage = sourceRecord.launchedFromPackage;
+ }
+
+ // TODO: Switch to user app stacks here.
+ try {
+ int ret = mStackSupervisor.startActivityMayWait(null, targetUid, targetPackage, intent,
+ resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
+ null, null, null, null, options, UserHandle.getUserId(targetUid), null);
+ return ret;
+ } catch (SecurityException e) {
+ // XXX need to figure out how to propagate to original app.
+ // A SecurityException here is generally actually a fault of the original
+ // calling activity (such as a fairly granting permissions), so propagate it
+ // back to them.
+ /*
+ StringBuilder msg = new StringBuilder();
+ msg.append("While launching");
+ msg.append(intent.toString());
+ msg.append(": ");
+ msg.append(e.getMessage());
+ */
+ throw e;
+ }
+ }
+
+ @Override
public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, String profileFile,
@@ -11402,6 +11468,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" [-a] [-c] [-h] [cmd] ...");
pw.println(" cmd may be one of:");
pw.println(" a[ctivities]: activity stack state");
+ pw.println(" r[recents]: recent activities state");
pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state");
pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state");
pw.println(" p[rocesses] [PACKAGE_NAME]: process state");
@@ -11435,6 +11502,10 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, null);
}
+ } else if ("recents".equals(cmd) || "r".equals(cmd)) {
+ synchronized (this) {
+ dumpRecentsLocked(fd, pw, args, opti, true, null);
+ }
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
String[] newArgs;
String name;
@@ -11578,6 +11649,11 @@ public final class ActivityManagerService extends ActivityManagerNative
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
+ dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
pw.println();
if (dumpAll) {
@@ -11612,6 +11688,17 @@ public final class ActivityManagerService extends ActivityManagerNative
mStackSupervisor.dump(pw, " ");
}
+ if (!printedAnything) {
+ pw.println(" (nothing)");
+ }
+ }
+
+ void dumpRecentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage) {
+ pw.println("ACTIVITY MANAGER RECENT ACTIVITIES (dumpsys activity recents)");
+
+ boolean printedAnything = false;
+
if (mRecentTasks.size() > 0) {
boolean printedHeader = false;
@@ -11625,9 +11712,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
if (!printedHeader) {
- if (needSep) {
- pw.println();
- }
pw.println(" Recent tasks:");
printedHeader = true;
printedAnything = true;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index e528d57..abacb2d 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -575,6 +575,10 @@ final class ActivityRecord {
void makeFinishing() {
if (!finishing) {
+ if (this == task.stack.getVisibleBehindActivity()) {
+ // A finishing activity should not remain as visible in the background
+ mStackSupervisor.requestVisibleBehindLocked(this, false);
+ }
finishing = true;
if (stopped) {
clearOptionsLocked();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1107139..8aec392 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2659,13 +2659,20 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
// A non-top activity is reporting a visibility change.
- if (top.fullscreen || top.state != ActivityState.RESUMED || top.app == null ||
- top.app.thread == null) {
+ if ((visible && (top.fullscreen || top.state != ActivityState.RESUMED)) ||
+ top.app == null || top.app.thread == null) {
// Can't carry out this request.
if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG, "requestVisibleBehind: returning top.fullscreen="
- + top.fullscreen+ " top.state=" + top.state + " top.app=" + top.app +
+ + top.fullscreen + " top.state=" + top.state + " top.app=" + top.app +
" top.app.thread=" + top.app.thread);
return false;
+ } else if (!visible && stack.getVisibleBehindActivity() != r) {
+ // Only the activity set as currently visible behind should actively reset its
+ // visible behind state.
+ if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG, "requestVisibleBehind: returning visible="
+ + visible + " stack.getVisibleBehindActivity()=" +
+ stack.getVisibleBehindActivity() + " r=" + r);
+ return false;
}
stack.setVisibleBehindActivity(visible ? r : null);
@@ -3182,7 +3189,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
void endLockTaskModeIfTaskEnding(TaskRecord task) {
if (mLockTaskModeTask != null && mLockTaskModeTask == task) {
+ final Message lockTaskMsg = Message.obtain();
+ lockTaskMsg.arg1 = mLockTaskModeTask.userId;
+ lockTaskMsg.what = LOCK_TASK_END_MSG;
mLockTaskModeTask = null;
+ mHandler.sendMessage(lockTaskMsg);
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d8da700..786196f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -39,6 +39,7 @@ import android.telephony.TelephonyManager;
import android.util.Slog;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.PowerProfile;
import com.android.server.LocalServices;
@@ -868,6 +869,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
if (noOutput) {
return;
}
+ if (BatteryStatsHelper.checkWifiOnly(mContext)) {
+ flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
+ }
if (useCheckinFormat) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
if (isRealCheckin) {
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 4c887dd..0dc163b 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -41,12 +41,17 @@ final class CoreSettingsObserver extends ContentObserver {
String, Class<?>>();
private static final Map<String, Class<?>> sSystemSettingToTypeMap = new HashMap<
String, Class<?>>();
+ private static final Map<String, Class<?>> sGlobalSettingToTypeMap = new HashMap<
+ String, Class<?>>();
static {
sSecureSettingToTypeMap.put(Settings.Secure.LONG_PRESS_TIMEOUT, int.class);
// add other secure settings here...
sSystemSettingToTypeMap.put(Settings.System.TIME_12_24, String.class);
// add other system settings here...
+
+ sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class);
+ // add other global settings here...
}
private final Bundle mCoreSettings = new Bundle();
@@ -74,6 +79,7 @@ final class CoreSettingsObserver extends ContentObserver {
private void sendCoreSettings() {
populateSettings(mCoreSettings, sSecureSettingToTypeMap);
populateSettings(mCoreSettings, sSystemSettingToTypeMap);
+ populateSettings(mCoreSettings, sGlobalSettingToTypeMap);
mActivityManagerService.onCoreSettingsChange(mCoreSettings);
}
@@ -89,6 +95,12 @@ final class CoreSettingsObserver extends ContentObserver {
mActivityManagerService.mContext.getContentResolver().registerContentObserver(
uri, false, this);
}
+
+ for (String setting : sGlobalSettingToTypeMap.keySet()) {
+ Uri uri = Settings.Global.getUriFor(setting);
+ mActivityManagerService.mContext.getContentResolver().registerContentObserver(
+ uri, false, this);
+ }
}
private void populateSettings(Bundle snapshot, Map<String, Class<?>> map) {
@@ -101,32 +113,40 @@ final class CoreSettingsObserver extends ContentObserver {
final String value;
if (map == sSecureSettingToTypeMap) {
value = Settings.Secure.getString(context.getContentResolver(), setting);
- } else {
+ } else if (map == sSystemSettingToTypeMap) {
value = Settings.System.getString(context.getContentResolver(), setting);
+ } else {
+ value = Settings.Global.getString(context.getContentResolver(), setting);
}
snapshot.putString(setting, value);
} else if (type == int.class) {
final int value;
if (map == sSecureSettingToTypeMap) {
value = Settings.Secure.getInt(context.getContentResolver(), setting);
- } else {
+ } else if (map == sSystemSettingToTypeMap) {
value = Settings.System.getInt(context.getContentResolver(), setting);
+ } else {
+ value = Settings.Global.getInt(context.getContentResolver(), setting);
}
snapshot.putInt(setting, value);
} else if (type == float.class) {
final float value;
if (map == sSecureSettingToTypeMap) {
value = Settings.Secure.getFloat(context.getContentResolver(), setting);
- } else {
+ } else if (map == sSystemSettingToTypeMap) {
value = Settings.System.getFloat(context.getContentResolver(), setting);
+ } else {
+ value = Settings.Global.getFloat(context.getContentResolver(), setting);
}
snapshot.putFloat(setting, value);
} else if (type == long.class) {
final long value;
if (map == sSecureSettingToTypeMap) {
value = Settings.Secure.getLong(context.getContentResolver(), setting);
- } else {
+ } else if (map == sSystemSettingToTypeMap) {
value = Settings.System.getLong(context.getContentResolver(), setting);
+ } else {
+ value = Settings.Global.getLong(context.getContentResolver(), setting);
}
snapshot.putLong(setting, value);
}
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
index 6f9b23d..cf65243 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
import com.android.internal.R;
@@ -32,9 +33,12 @@ public class LockTaskNotify {
private final Context mContext;
private final H mHandler;
+ private AccessibilityManager mAccessibilityManager;
public LockTaskNotify(Context context) {
mContext = context;
+ mAccessibilityManager = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mHandler = new H();
}
@@ -45,6 +49,9 @@ public class LockTaskNotify {
public void handleShowToast(boolean isLocked) {
String text = mContext.getString(isLocked
? R.string.lock_to_app_toast_locked : R.string.lock_to_app_toast);
+ if (!isLocked && mAccessibilityManager.isEnabled()) {
+ text = mContext.getString(R.string.lock_to_app_toast_accessible);
+ }
Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
}
diff --git a/services/core/java/com/android/server/am/LockToAppRequestDialog.java b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
index 0847b52..12dcf7e 100644
--- a/services/core/java/com/android/server/am/LockToAppRequestDialog.java
+++ b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
@@ -13,6 +13,7 @@ import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import android.widget.CheckBox;
import com.android.internal.R;
@@ -33,8 +34,12 @@ public class LockToAppRequestDialog implements OnClickListener {
private ILockSettings mLockSettingsService;
+ private AccessibilityManager mAccessibilityService;
+
public LockToAppRequestDialog(Context context, ActivityManagerService activityManagerService) {
mContext = context;
+ mAccessibilityService = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mService = activityManagerService;
}
@@ -78,7 +83,9 @@ public class LockToAppRequestDialog implements OnClickListener {
final int unlockStringId = getLockString(task.userId);
final Resources r = Resources.getSystem();
- final String description= r.getString(R.string.lock_to_app_description);
+ final String description= r.getString(mAccessibilityService.isEnabled()
+ ? R.string.lock_to_app_description_accessible
+ : R.string.lock_to_app_description);
AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
.setTitle(r.getString(R.string.lock_to_app_title))
.setMessage(description)
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index f79c026..0a66a5c 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -450,6 +450,9 @@ final class ServiceRecord extends Binder {
appInfo.packageName, null));
PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ localForegroundNoti.color = ams.mContext.getResources().getColor(
+ com.android.internal
+ .R.color.system_notification_accent_color);
localForegroundNoti.setLatestEventInfo(ctx,
ams.mContext.getString(
com.android.internal.R.string
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4eb2ef1..5a97aee 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -40,7 +40,7 @@ import java.util.ArrayList;
*/
public class NetworkAgentInfo {
public NetworkInfo networkInfo;
- public final Network network;
+ public Network network;
public LinkProperties linkProperties;
public NetworkCapabilities networkCapabilities;
public int currentScore;
@@ -55,12 +55,12 @@ public class NetworkAgentInfo {
public final Messenger messenger;
public final AsyncChannel asyncChannel;
- public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, int netId, NetworkInfo info,
+ public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc) {
this.messenger = messenger;
asyncChannel = ac;
- network = new Network(netId);
+ network = null;
networkInfo = info;
linkProperties = lp;
networkCapabilities = nc;
@@ -87,6 +87,7 @@ public class NetworkAgentInfo {
public String name() {
return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
- networkInfo.getSubtypeName() + ") - " + network.toString() + "]";
+ networkInfo.getSubtypeName() + ") - " +
+ (network == null ? "null" : network.toString()) + "]";
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 5057994..b09298c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -272,23 +272,22 @@ public class NetworkMonitor extends StateMachine {
case CMD_NETWORK_LINGER:
if (DBG) log("Lingering");
transitionTo(mLingeringState);
- break;
+ return HANDLED;
case CMD_NETWORK_CONNECTED:
if (DBG) log("Connected");
transitionTo(mEvaluatingState);
- break;
+ return HANDLED;
case CMD_NETWORK_DISCONNECTED:
if (DBG) log("Disconnected - quitting");
quit();
- break;
+ return HANDLED;
case CMD_FORCE_REEVALUATION:
if (DBG) log("Forcing reevaluation");
transitionTo(mEvaluatingState);
- break;
+ return HANDLED;
default:
- break;
+ return HANDLED;
}
- return HANDLED;
}
}
@@ -314,11 +313,10 @@ public class NetworkMonitor extends StateMachine {
switch (message.what) {
case CMD_NETWORK_CONNECTED:
transitionTo(mValidatedState);
- break;
+ return HANDLED;
default:
return NOT_HANDLED;
}
- return HANDLED;
}
}
@@ -347,23 +345,25 @@ public class NetworkMonitor extends StateMachine {
switch (message.what) {
case CMD_REEVALUATE:
if (message.arg1 != mReevaluateToken)
- break;
+ return HANDLED;
if (mNetworkAgentInfo.isVPN()) {
transitionTo(mValidatedState);
+ return HANDLED;
}
// If network provides no internet connectivity adjust evaluation.
if (!mNetworkAgentInfo.networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
// TODO: Try to verify something works. Do all gateways respond to pings?
transitionTo(mValidatedState);
+ return HANDLED;
}
// Kick off a thread to perform internet connectivity evaluation.
Thread thread = new EvaluateInternetConnectivity(mReevaluateToken);
thread.run();
- break;
+ return HANDLED;
case EVENT_REEVALUATION_COMPLETE:
if (message.arg1 != mReevaluateToken)
- break;
+ return HANDLED;
int httpResponseCode = message.arg2;
if (httpResponseCode == 204) {
transitionTo(mValidatedState);
@@ -375,11 +375,10 @@ public class NetworkMonitor extends StateMachine {
Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
}
- break;
+ return HANDLED;
default:
return NOT_HANDLED;
}
- return HANDLED;
}
}
@@ -432,17 +431,16 @@ public class NetworkMonitor extends StateMachine {
switch (message.what) {
case EVENT_APP_BYPASSED_CAPTIVE_PORTAL:
transitionTo(mValidatedState);
- break;
+ return HANDLED;
case EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE:
transitionTo(mOfflineState);
- break;
+ return HANDLED;
case EVENT_NO_APP_RESPONSE:
transitionTo(mUserPromptedState);
- break;
+ return HANDLED;
default:
return NOT_HANDLED;
}
- return HANDLED;
}
public void exit() {
mReceiver.cancel();
@@ -488,13 +486,12 @@ public class NetworkMonitor extends StateMachine {
switch (message.what) {
case CMD_USER_WANTS_SIGN_IN:
if (message.arg1 != mUserPromptedToken)
- break;
+ return HANDLED;
transitionTo(mInteractiveAppsPromptedState);
- break;
+ return HANDLED;
default:
return NOT_HANDLED;
}
- return HANDLED;
}
@Override
@@ -520,17 +517,16 @@ public class NetworkMonitor extends StateMachine {
switch (message.what) {
case EVENT_APP_BYPASSED_CAPTIVE_PORTAL:
transitionTo(mValidatedState);
- break;
+ return HANDLED;
case EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE:
transitionTo(mOfflineState);
- break;
+ return HANDLED;
case EVENT_NO_APP_RESPONSE:
transitionTo(mCaptivePortalState);
- break;
+ return HANDLED;
default:
return NOT_HANDLED;
}
- return HANDLED;
}
public void exit() {
mReceiver.cancel();
@@ -581,18 +577,17 @@ public class NetworkMonitor extends StateMachine {
switch (message.what) {
case CMD_CAPTIVE_PORTAL_LOGGED_IN:
if (message.arg1 != mCaptivePortalLoggedInToken)
- break;
+ return HANDLED;
if (message.arg2 == 0) {
// TODO: Should teardown network.
transitionTo(mOfflineState);
} else {
transitionTo(mValidatedState);
}
- break;
+ return HANDLED;
default:
return NOT_HANDLED;
}
- return HANDLED;
}
@Override
@@ -616,17 +611,16 @@ public class NetworkMonitor extends StateMachine {
case CMD_NETWORK_CONNECTED:
// Go straight to active as we've already evaluated.
transitionTo(mValidatedState);
- break;
+ return HANDLED;
case CMD_LINGER_EXPIRED:
if (message.arg1 != mLingerToken)
- break;
+ return HANDLED;
mConnectivityServiceHandler.sendMessage(
obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo));
- break;
+ return HANDLED;
default:
return NOT_HANDLED;
}
- return HANDLED;
}
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index fa8626f..1fd114c 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -471,6 +471,8 @@ public class Tethering extends BaseNetworkObserver {
mTetheredNotification.flags = Notification.FLAG_ONGOING_EVENT;
mTetheredNotification.tickerText = title;
mTetheredNotification.visibility = Notification.VISIBILITY_PUBLIC;
+ mTetheredNotification.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
mTetheredNotification.setLatestEventInfo(mContext, title, message, pi);
mTetheredNotification.category = Notification.CATEGORY_STATUS;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index eddf414..69caab9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,9 +21,7 @@ import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import android.app.AppGlobals;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -35,10 +33,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.net.BaseNetworkStateTracker;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
@@ -51,7 +45,6 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
-import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.UidRange;
import android.os.Binder;
@@ -70,15 +63,15 @@ import android.os.UserManager;
import android.security.Credentials;
import android.security.KeyStore;
import android.util.Log;
-import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.server.net.BaseNetworkObserver;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -92,8 +85,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
-import libcore.io.IoUtils;
-
/**
* @hide
*/
@@ -114,8 +105,6 @@ public class Vpn {
private boolean mAllowIPv6;
private Connection mConnection;
private LegacyVpnRunner mLegacyVpnRunner;
- private PendingIntent mStatusIntent;
- private volatile boolean mEnableNotif = true;
private volatile boolean mEnableTeardown = true;
private final IConnectivityManager mConnService;
private final INetworkManagementService mNetd;
@@ -180,14 +169,6 @@ public class Vpn {
}
/**
- * Set if this object is responsible for showing its own notifications. When
- * {@code false}, notifications are handled externally by someone else.
- */
- public void setEnableNotifications(boolean enableNotif) {
- mEnableNotif = enableNotif;
- }
-
- /**
* Set if this object is responsible for watching for {@link NetworkInfo}
* teardown. When {@code false}, teardown is handled externally by someone
* else.
@@ -228,6 +209,20 @@ public class Vpn {
public synchronized boolean prepare(String oldPackage, String newPackage) {
// Return false if the package does not match.
if (oldPackage != null && !oldPackage.equals(mPackage)) {
+ // The package doesn't match. If this VPN was not previously authorized, return false
+ // to force user authorization. Otherwise, revoke the VPN anyway.
+ if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ // This looks bizarre, but it is what ConfirmDialog in VpnDialogs is doing when
+ // the user clicks through to allow the VPN to consent. So we are emulating the
+ // action of the dialog without actually showing it.
+ prepare(null, oldPackage);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return true;
+ }
return false;
}
@@ -240,11 +235,8 @@ public class Vpn {
// Check if the caller is authorized.
enforceControlPermission();
- // Reset the interface and hide the notification.
+ // Reset the interface.
if (mInterface != null) {
- for (UidRange uidRange : mVpnUsers) {
- hideNotification(uidRange.getStartUser());
- }
agentDisconnect();
jniReset(mInterface);
mInterface = null;
@@ -287,12 +279,46 @@ public class Vpn {
Binder.restoreCallingIdentity(token);
}
mConfig = null;
+
updateState(DetailedState.IDLE, "prepare");
return true;
}
+ /**
+ * Set whether the current package has the ability to launch VPNs without user intervention.
+ */
+ public void setPackageAuthorization(boolean authorized) {
+ // Check if the caller is authorized.
+ enforceControlPermission();
+
+ if (mPackage == null || VpnConfig.LEGACY_VPN.equals(mPackage)) {
+ return;
+ }
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ AppOpsManager appOps =
+ (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, mOwnerUID, mPackage,
+ authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Failed to set app ops for package " + mPackage, e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private boolean isVpnUserPreConsented(String packageName) {
+ AppOpsManager appOps =
+ (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+
+ // Verify that the caller matches the given package and has permission to activate VPNs.
+ return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
+ packageName) == AppOpsManager.MODE_ALLOWED;
+ }
+
private int getAppUid(String app) {
- if (app == VpnConfig.LEGACY_VPN) {
+ if (VpnConfig.LEGACY_VPN.equals(app)) {
return Process.myUid();
}
PackageManager pm = mContext.getPackageManager();
@@ -355,9 +381,10 @@ public class Vpn {
try {
mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
+ @Override
public void unwanted() {
// We are user controlled, not driven by NetworkRequest.
- };
+ }
};
} finally {
Binder.restoreCallingIdentity(token);
@@ -540,39 +567,6 @@ public class Vpn {
// add the user
mVpnUsers.add(UidRange.createForUser(user));
-
- // show the notification
- if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
- // Load everything for the user's notification
- PackageManager pm = mContext.getPackageManager();
- ApplicationInfo app = null;
- final long token = Binder.clearCallingIdentity();
- try {
- app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
- } catch (RemoteException e) {
- throw new IllegalStateException("Invalid application");
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- String label = app.loadLabel(pm).toString();
- // Load the icon and convert it into a bitmap.
- Drawable icon = app.loadIcon(pm);
- Bitmap bitmap = null;
- if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
- int width = mContext.getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- int height = mContext.getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_height);
- icon.setBounds(0, 0, width, height);
- bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(bitmap);
- icon.draw(c);
- c.setBitmap(null);
- }
- showNotification(label, bitmap, user);
- } else {
- showNotification(null, null, user);
- }
}
private void removeVpnUserLocked(int user) {
@@ -584,7 +578,6 @@ public class Vpn {
mNetworkAgent.removeUidRanges(new UidRange[] { uidRange });
}
mVpnUsers.remove(uidRange);
- hideNotification(user);
}
private void onUserAdded(int userId) {
@@ -652,9 +645,6 @@ public class Vpn {
public void interfaceRemoved(String interfaze) {
synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
- for (UidRange uidRange : mVpnUsers) {
- hideNotification(uidRange.getStartUser());
- }
mVpnUsers = null;
mInterface = null;
if (mConnection != null) {
@@ -712,54 +702,6 @@ public class Vpn {
}
}
- private void showNotification(String label, Bitmap icon, int user) {
- if (!mEnableNotif) return;
- final long token = Binder.clearCallingIdentity();
- try {
- mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
-
- NotificationManager nm = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (nm != null) {
- String title = (label == null) ? mContext.getString(R.string.vpn_title) :
- mContext.getString(R.string.vpn_title_long, label);
- String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
- mContext.getString(R.string.vpn_text_long, mConfig.session);
-
- Notification notification = new Notification.Builder(mContext)
- .setSmallIcon(R.drawable.vpn_connected)
- .setLargeIcon(icon)
- .setContentTitle(title)
- .setContentText(text)
- .setContentIntent(mStatusIntent)
- .setDefaults(0)
- .setOngoing(true)
- .build();
- nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void hideNotification(int user) {
- if (!mEnableNotif) return;
- mStatusIntent = null;
-
- NotificationManager nm = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (nm != null) {
- final long token = Binder.clearCallingIdentity();
- try {
- nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
public synchronized boolean addAddress(String address, int prefixLength) {
if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
return false;
@@ -969,9 +911,6 @@ public class Vpn {
final LegacyVpnInfo info = new LegacyVpnInfo();
info.key = mConfig.user;
info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
- if (mNetworkInfo.isConnected()) {
- info.intent = mStatusIntent;
- }
return info;
}
@@ -1261,6 +1200,7 @@ public class Vpn {
}
} catch (Exception e) {
Log.i(TAG, "Aborting", e);
+ updateState(DetailedState.FAILED, e.getMessage());
exit();
} finally {
// Kill the daemons if they fail to stop.
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 08d6fc9..949019e 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -3064,6 +3064,8 @@ public class SyncManager {
new Notification(R.drawable.stat_notify_sync_error,
mContext.getString(R.string.contentServiceSync),
System.currentTimeMillis());
+ notification.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
notification.setLatestEventInfo(mContext,
mContext.getString(R.string.contentServiceSyncNotificationTitle),
String.format(tooManyDeletesDescFormat.toString(), authorityName),
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 9a67321..2a1ceaa 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -37,7 +37,7 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.text.format.DateUtils;
+import android.os.Trace;
import android.util.MathUtils;
import android.util.Slog;
import android.util.Spline;
@@ -74,6 +74,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private static boolean DEBUG = false;
private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
+ private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
+
// If true, uses the electron beam on animation.
// We might want to turn this off if we cannot get a guarantee that the screen
// actually turns on and starts showing new content after the call to set the
@@ -85,7 +87,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private static final int SCREEN_DIM_MINIMUM_REDUCTION = 10;
private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250;
- private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 600;
+ private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400;
private static final int MSG_UPDATE_POWER_STATE = 1;
private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
@@ -714,11 +716,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void blockScreenOn() {
if (!mScreenOnWasBlocked) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
mScreenOnWasBlocked = true;
mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
- if (DEBUG) {
- Slog.d(TAG, "Blocked screen on.");
- }
+ Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");
}
}
@@ -726,9 +727,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mScreenOnWasBlocked) {
mScreenOnWasBlocked = false;
long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
- if (delay > 1000 || DEBUG) {
- Slog.d(TAG, "Unblocked screen on after " + delay + " ms");
- }
+ Slog.i(TAG, "Unblocked screen on after " + delay + " ms");
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 6522b89..a7651e4 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -23,6 +23,7 @@ import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.Trace;
import android.util.FloatProperty;
import android.util.IntProperty;
import android.util.Slog;
@@ -405,19 +406,38 @@ final class DisplayPowerState {
}
boolean suspending = Display.isSuspendedState(state);
if (stateChanged && !suspending) {
- mBlanker.requestDisplayState(state);
+ requestDisplayState(state);
}
if (backlightChanged) {
- mBacklight.setBrightness(backlight);
+ setBrightness(backlight);
}
if (stateChanged && suspending) {
- mBlanker.requestDisplayState(state);
+ requestDisplayState(state);
}
}
// Let the outer class know that all changes have been applied.
postScreenUpdateThreadSafe();
}
+
+ private void requestDisplayState(int state) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState("
+ + Display.stateToString(state) + ")");
+ try {
+ mBlanker.requestDisplayState(state);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ }
+
+ private void setBrightness(int backlight) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "setBrightness(" + backlight + ")");
+ try {
+ mBacklight.setBrightness(backlight);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ }
};
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4fd006d..9c91ab5 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -21,7 +21,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemProperties;
-import android.util.Pair;
+import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -224,8 +224,14 @@ final class LocalDisplayAdapter extends DisplayAdapter {
@Override
public void requestDisplayStateLocked(int state) {
if (mState != state) {
- SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(),
- getPowerModeForState(state));
+ final int mode = getPowerModeForState(state);
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState("
+ + Display.stateToString(state) + ", id=" + mBuiltInDisplayId + ")");
+ try {
+ SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(), mode);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
mState = state;
updateDeviceInfoLocked();
}
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index a05bf2c..9d008b9 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -502,6 +502,8 @@ final class WifiDisplayAdapter extends DisplayAdapter {
.addAction(android.R.drawable.ic_menu_close_clear_cancel,
r.getString(R.string.wifi_display_notification_disconnect),
mDisconnectPendingIntent)
+ .setColor(r.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
.build();
} else {
notification = new Notification.Builder(context)
@@ -516,6 +518,8 @@ final class WifiDisplayAdapter extends DisplayAdapter {
.addAction(android.R.drawable.ic_menu_close_clear_cancel,
r.getString(R.string.wifi_display_notification_disconnect),
mDisconnectPendingIntent)
+ .setColor(r.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
.build();
}
mNotificationManager.notifyAsUser(null,
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 334f0ac..768ccf2 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.IBinder.DeathRecipient;
+import android.os.Trace;
import android.os.UserHandle;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamService;
@@ -111,41 +112,46 @@ final class DreamController {
boolean isTest, boolean canDoze, int userId) {
stopDream(true /*immediate*/);
- // Close the notification shade. Don't need to send to all, but better to be explicit.
- mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
+ try {
+ // Close the notification shade. Don't need to send to all, but better to be explicit.
+ mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
- Slog.i(TAG, "Starting dream: name=" + name
- + ", isTest=" + isTest + ", canDoze=" + canDoze
- + ", userId=" + userId);
+ Slog.i(TAG, "Starting dream: name=" + name
+ + ", isTest=" + isTest + ", canDoze=" + canDoze
+ + ", userId=" + userId);
- mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId);
+ mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId);
- try {
- mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Unable to add window token for dream.", ex);
- stopDream(true /*immediate*/);
- return;
- }
+ try {
+ mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Unable to add window token for dream.", ex);
+ stopDream(true /*immediate*/);
+ return;
+ }
- Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
- intent.setComponent(name);
- intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- try {
- if (!mContext.bindServiceAsUser(intent, mCurrentDream,
- Context.BIND_AUTO_CREATE, new UserHandle(userId))) {
- Slog.e(TAG, "Unable to bind dream service: " + intent);
+ Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
+ intent.setComponent(name);
+ intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ try {
+ if (!mContext.bindServiceAsUser(intent, mCurrentDream,
+ Context.BIND_AUTO_CREATE, new UserHandle(userId))) {
+ Slog.e(TAG, "Unable to bind dream service: " + intent);
+ stopDream(true /*immediate*/);
+ return;
+ }
+ } catch (SecurityException ex) {
+ Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
stopDream(true /*immediate*/);
return;
}
- } catch (SecurityException ex) {
- Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
- stopDream(true /*immediate*/);
- return;
- }
- mCurrentDream.mBound = true;
- mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
+ mCurrentDream.mBound = true;
+ mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
}
public void stopDream(boolean immediate) {
@@ -153,71 +159,76 @@ final class DreamController {
return;
}
- if (!immediate) {
- if (mCurrentDream.mWakingGently) {
- return; // already waking gently
- }
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "stopDream");
+ try {
+ if (!immediate) {
+ if (mCurrentDream.mWakingGently) {
+ return; // already waking gently
+ }
- if (mCurrentDream.mService != null) {
- // Give the dream a moment to wake up and finish itself gently.
- mCurrentDream.mWakingGently = true;
- try {
- mCurrentDream.mService.wakeUp();
- mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT);
- return;
- } catch (RemoteException ex) {
- // oh well, we tried, finish immediately instead
+ if (mCurrentDream.mService != null) {
+ // Give the dream a moment to wake up and finish itself gently.
+ mCurrentDream.mWakingGently = true;
+ try {
+ mCurrentDream.mService.wakeUp();
+ mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT);
+ return;
+ } catch (RemoteException ex) {
+ // oh well, we tried, finish immediately instead
+ }
}
}
- }
- final DreamRecord oldDream = mCurrentDream;
- mCurrentDream = null;
- Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
- + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze
- + ", userId=" + oldDream.mUserId);
+ final DreamRecord oldDream = mCurrentDream;
+ mCurrentDream = null;
+ Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
+ + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze
+ + ", userId=" + oldDream.mUserId);
- mHandler.removeCallbacks(mStopUnconnectedDreamRunnable);
- mHandler.removeCallbacks(mStopStubbornDreamRunnable);
+ mHandler.removeCallbacks(mStopUnconnectedDreamRunnable);
+ mHandler.removeCallbacks(mStopStubbornDreamRunnable);
- if (oldDream.mSentStartBroadcast) {
- mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
- }
+ if (oldDream.mSentStartBroadcast) {
+ mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
+ }
- if (oldDream.mService != null) {
- // Tell the dream that it's being stopped so that
- // it can shut down nicely before we yank its window token out from
- // under it.
- try {
- oldDream.mService.detach();
- } catch (RemoteException ex) {
- // we don't care; this thing is on the way out
+ if (oldDream.mService != null) {
+ // Tell the dream that it's being stopped so that
+ // it can shut down nicely before we yank its window token out from
+ // under it.
+ try {
+ oldDream.mService.detach();
+ } catch (RemoteException ex) {
+ // we don't care; this thing is on the way out
+ }
+
+ try {
+ oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
+ } catch (NoSuchElementException ex) {
+ // don't care
+ }
+ oldDream.mService = null;
}
- try {
- oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
- } catch (NoSuchElementException ex) {
- // don't care
+ if (oldDream.mBound) {
+ mContext.unbindService(oldDream);
}
- oldDream.mService = null;
- }
- if (oldDream.mBound) {
- mContext.unbindService(oldDream);
- }
+ try {
+ mIWindowManager.removeWindowToken(oldDream.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Error removing window token for dream.", ex);
+ }
- try {
- mIWindowManager.removeWindowToken(oldDream.mToken);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Error removing window token for dream.", ex);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDreamStopped(oldDream.mToken);
+ }
+ });
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onDreamStopped(oldDream.mToken);
- }
- });
}
private void attach(IDreamService service) {
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 2407253..4b812cf 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -173,6 +173,10 @@ final class Constants {
static final int SYSTEM_AUDIO_STATUS_OFF = 0;
static final int SYSTEM_AUDIO_STATUS_ON = 1;
+ // [Menu State]
+ static final int MENU_STATE_ACTIVATED = 0;
+ static final int MENU_STATE_DEACTIVATED = 1;
+
// Bit mask used to get the routing path of the top level device.
// When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0.
static final int ROUTING_PATH_TOP_MASK = 0xF000;
@@ -276,5 +280,10 @@ final class Constants {
// values which denotes the device type in HDMI Spec 1.4.
static final String PROPERTY_DEVICE_TYPE = "ro.hdmi.device_type";
+ // MHL RCPE messages
+ static final int MHL_RCPE_NO_ERROR = 0x00;
+ static final int MHL_RCPE_INEFFECTIVE_KEYCODE = 0x01;
+ static final int MHL_RCPE_RESPONDER_BUSY = 0x02;
+
private Constants() { /* cannot be instantiated */ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index c0c8424..aed5dcc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -21,7 +21,7 @@ import android.view.KeyEvent;
/**
* Helper class to translate android keycode to hdmi cec keycode and vice versa.
*/
-public class HdmiCecKeycode {
+final class HdmiCecKeycode {
public static final int UNSUPPORTED_KEYCODE = -1;
public static final int NO_PARAM = -1;
@@ -155,7 +155,7 @@ public class HdmiCecKeycode {
}
/**
- * A mapping between andorid and cec keycode.
+ * A mapping between Android and CEC keycode.
*
* <p>Normal implementation of this looks like
* <pre>
@@ -174,43 +174,28 @@ public class HdmiCecKeycode {
private static class KeycodeEntry {
private final int mAndroidKeycode;
private final int mCecKeycode;
- private final int mParam;
private final boolean mIsRepeatable;
- private KeycodeEntry(int androidKeycode, int cecKeycode, int param, boolean isRepeatable) {
+ private KeycodeEntry(int androidKeycode, int cecKeycode, boolean isRepeatable) {
mAndroidKeycode = androidKeycode;
mCecKeycode = cecKeycode;
- mParam = param;
mIsRepeatable = isRepeatable;
}
private KeycodeEntry(int androidKeycode, int cecKeycode) {
- this(androidKeycode, cecKeycode, NO_PARAM, true);
- }
-
- private KeycodeEntry(int androidKeycode, int cecKeycode, boolean isRepeatable) {
- this(androidKeycode, cecKeycode, NO_PARAM, isRepeatable);
+ this(androidKeycode, cecKeycode, true);
}
- private byte[] toCecKeycodeIfMatched(int androidKeycode) {
+ private int toCecKeycodeIfMatched(int androidKeycode) {
if (mAndroidKeycode == androidKeycode) {
- if (mParam == NO_PARAM) {
- return new byte[] {
- (byte) (mCecKeycode & 0xFF)
- };
- } else {
- return new byte[] {
- (byte) (mCecKeycode & 0xFF),
- (byte) (mParam & 0xFF)
- };
- }
+ return mCecKeycode;
} else {
- return null;
+ return UNSUPPORTED_KEYCODE;
}
}
- private int toAndroidKeycodeIfMatched(int cecKeycode, int param) {
- if (cecKeycode == mCecKeycode && mParam == param) {
+ private int toAndroidKeycodeIfMatched(int cecKeycode) {
+ if (cecKeycode == mCecKeycode) {
return mAndroidKeycode;
} else {
return UNSUPPORTED_KEYCODE;
@@ -365,29 +350,28 @@ public class HdmiCecKeycode {
* Translate Android keycode to Hdmi Cec keycode.
*
* @param keycode Android keycode. For details, refer {@link KeyEvent}
- * @return array of byte which contains cec keycode and param if it has;
- * return null if failed to find matched cec keycode
+ * @return single byte CEC keycode if matched.
*/
- static byte[] androidKeyToCecKey(int keycode) {
+ static int androidKeyToCecKey(int keycode) {
for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) {
- byte[] cecKeycode = KEYCODE_ENTRIES[i].toCecKeycodeIfMatched(keycode);
- if (cecKeycode != null) {
+ int cecKeycode = KEYCODE_ENTRIES[i].toCecKeycodeIfMatched(keycode);
+ if (cecKeycode != UNSUPPORTED_KEYCODE) {
return cecKeycode;
}
}
- return null;
+ return UNSUPPORTED_KEYCODE;
}
/**
* Translate Hdmi CEC keycode to Android keycode.
*
- * @param keycode Cec keycode. If has no param, put {@link #NO_PARAM}
+ * @param keycode CEC keycode
* @return cec keycode corresponding to the given android keycode.
* If finds no matched keycode, return {@link #UNSUPPORTED_KEYCODE}
*/
- static int cecKeyToAndroidKey(int keycode, int param) {
+ static int cecKeyToAndroidKey(int keycode) {
for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) {
- int androidKey = KEYCODE_ENTRIES[i].toAndroidKeycodeIfMatched(keycode, param);
+ int androidKey = KEYCODE_ENTRIES[i].toAndroidKeycodeIfMatched(keycode);
if (androidKey != UNSUPPORTED_KEYCODE) {
return androidKey;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index b894fd7..b43ad1b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -17,10 +17,15 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemClock;
import android.util.Slog;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -39,15 +44,21 @@ abstract class HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDevice";
private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
+ private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2;
// Timeout in millisecond for device clean up (5s).
// Normal actions timeout is 2s but some of them would have several sequence of timeout.
private static final int DEVICE_CLEANUP_TIMEOUT = 5000;
+ // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
+ // When it expires, we can assume <User Control Release> is received.
+ private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
protected final HdmiControlService mService;
protected final int mDeviceType;
protected int mAddress;
protected int mPreferredAddress;
protected HdmiDeviceInfo mDeviceInfo;
+ protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
+ protected int mLastKeyRepeatCount = 0;
static class ActiveSource {
int logicalAddress;
@@ -111,6 +122,9 @@ abstract class HdmiCecLocalDevice {
case MSG_DISABLE_DEVICE_TIMEOUT:
handleDisableDeviceTimeout();
break;
+ case MSG_USER_CONTROL_RELEASE_TIMEOUT:
+ handleUserControlReleased();
+ break;
}
}
};
@@ -230,10 +244,14 @@ abstract class HdmiCecLocalDevice {
return handleImageViewOn(message);
case Constants.MESSAGE_USER_CONTROL_PRESSED:
return handleUserControlPressed(message);
+ case Constants.MESSAGE_USER_CONTROL_RELEASED:
+ return handleUserControlReleased();
case Constants.MESSAGE_SET_STREAM_PATH:
return handleSetStreamPath(message);
case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
return handleGiveDevicePowerStatus(message);
+ case Constants.MESSAGE_MENU_REQUEST:
+ return handleGiveDeviceMenuStatus(message);
case Constants.MESSAGE_VENDOR_COMMAND:
return handleVendorCommand(message);
case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
@@ -376,6 +394,7 @@ abstract class HdmiCecLocalDevice {
@ServiceThreadOnly
protected boolean handleUserControlPressed(HdmiCecMessage message) {
assertRunOnServiceThread();
+ mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) {
mService.standby();
return true;
@@ -383,9 +402,55 @@ abstract class HdmiCecLocalDevice {
mService.wakeUp();
return true;
}
+
+ final long downTime = SystemClock.uptimeMillis();
+ final byte[] params = message.getParams();
+ // Note that we don't support parameterized keycode now.
+ // TODO: translate parameterized keycode as well.
+ final int keycode = HdmiCecKeycode.cecKeyToAndroidKey(params[0]);
+ int keyRepeatCount = 0;
+ if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
+ if (keycode == mLastKeycode) {
+ keyRepeatCount = mLastKeyRepeatCount + 1;
+ } else {
+ injectKeyEvent(downTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
+ }
+ }
+ mLastKeycode = keycode;
+ mLastKeyRepeatCount = keyRepeatCount;
+
+ if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
+ injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT),
+ FOLLOWER_SAFETY_TIMEOUT);
+ return true;
+ }
+ return false;
+ }
+
+ @ServiceThreadOnly
+ protected boolean handleUserControlReleased() {
+ assertRunOnServiceThread();
+ mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
+ mLastKeyRepeatCount = 0;
+ if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
+ final long upTime = SystemClock.uptimeMillis();
+ injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
+ mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
+ return true;
+ }
return false;
}
+ static void injectKeyEvent(long time, int action, int keycode, int repeat) {
+ KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode,
+ repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
+ InputDevice.SOURCE_HDMI, null);
+ InputManager.getInstance().injectInputEvent(keyEvent,
+ InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ keyEvent.recycle();
+ }
+
static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
byte[] params = message.getParams();
return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
@@ -420,6 +485,13 @@ abstract class HdmiCecLocalDevice {
return true;
}
+ protected boolean handleGiveDeviceMenuStatus(HdmiCecMessage message) {
+ // Always report menu active to receive Remote Control.
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus(
+ mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED));
+ return true;
+ }
+
protected boolean handleVendorCommand(HdmiCecMessage message) {
mService.invokeVendorCommandListeners(mDeviceType, message.getSource(),
message.getParams(), false);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 47b0794..73f5cfd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -100,7 +100,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private List<HdmiDeviceInfo> mSafeExternalInputs = Collections.emptyList();
// Map-like container of all cec devices including local ones.
- // A logical address of device is used as key of container.
+ // device id is used as key of container.
// This is not thread-safe. For external purpose use mSafeDeviceInfos.
private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
@@ -167,12 +167,18 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
/**
* Performs the action 'device select', or 'one touch play' initiated by TV.
*
- * @param targetAddress logical address of the device to select
+ * @param id id of HDMI device to select
* @param callback callback object to report the result with
*/
@ServiceThreadOnly
- void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
+ void deviceSelect(int id, IHdmiControlCallback callback) {
assertRunOnServiceThread();
+ HdmiDeviceInfo targetDevice = mDeviceInfos.get(id);
+ if (targetDevice == null) {
+ invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
+ return;
+ }
+ int targetAddress = targetDevice.getLogicalAddress();
ActiveSource active = getActiveSource();
if (active.isValid() && targetAddress == active.logicalAddress) {
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
@@ -187,18 +193,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return;
}
if (!mService.isControlEnabled()) {
- HdmiDeviceInfo info = getDeviceInfo(targetAddress);
- if (info != null) {
- setActiveSource(info);
- }
+ setActiveSource(targetDevice);
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
return;
}
- HdmiDeviceInfo targetDevice = getDeviceInfo(targetAddress);
- if (targetDevice == null) {
- invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
- return;
- }
removeAction(DeviceSelectAction.class);
addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
}
@@ -231,7 +229,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
setActiveSource(newActive);
int logicalAddress = newActive.logicalAddress;
- if (getDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) {
+ if (getCecDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) {
if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) {
setPrevPortId(getActivePortId());
}
@@ -279,7 +277,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// Show OSD port change banner
if (notifyInputChange) {
ActiveSource activeSource = getActiveSource();
- HdmiDeviceInfo info = getDeviceInfo(activeSource.logicalAddress);
+ HdmiDeviceInfo info = getCecDeviceInfo(activeSource.logicalAddress);
if (info == null) {
info = new HdmiDeviceInfo(Constants.ADDR_INVALID, path, portId,
HdmiDeviceInfo.DEVICE_RESERVED, 0, null);
@@ -363,7 +361,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
assertRunOnServiceThread();
int logicalAddress = message.getSource();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
- if (getDeviceInfo(logicalAddress) == null) {
+ if (getCecDeviceInfo(logicalAddress) == null) {
handleNewDeviceAtTheTailOfActivePath(physicalAddress);
} else {
ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
@@ -389,7 +387,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (portId != Constants.INVALID_PORT_ID) {
// TODO: Do this only if TV is not showing multiview like PIP/PAP.
- HdmiDeviceInfo inactiveSource = getDeviceInfo(message.getSource());
+ HdmiDeviceInfo inactiveSource = getCecDeviceInfo(message.getSource());
if (inactiveSource == null) {
return true;
}
@@ -420,17 +418,25 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
assertRunOnServiceThread();
- HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
- mAddress, Locale.getDefault().getISO3Language());
// TODO: figure out how to handle failed to get language code.
- if (command != null) {
- mService.sendCecCommand(command);
- } else {
+ if (!broadcastMenuLanguage(Locale.getDefault().getISO3Language())) {
Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
}
return true;
}
+ @ServiceThreadOnly
+ boolean broadcastMenuLanguage(String language) {
+ assertRunOnServiceThread();
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
+ mAddress, language);
+ if (command != null) {
+ mService.sendCecCommand(command);
+ return true;
+ }
+ return false;
+ }
+
@Override
@ServiceThreadOnly
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
@@ -577,7 +583,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
protected boolean handleSetOsdName(HdmiCecMessage message) {
int source = message.getSource();
- HdmiDeviceInfo deviceInfo = getDeviceInfo(source);
+ HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
// If the device is not in device list, ignore it.
if (deviceInfo == null) {
Slog.e(TAG, "No source device info for <Set Osd Name>." + message);
@@ -952,11 +958,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
private HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
assertRunOnServiceThread();
- HdmiDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
+ HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
if (oldDeviceInfo != null) {
- removeDeviceInfo(deviceInfo.getLogicalAddress());
+ removeDeviceInfo(deviceInfo.getId());
}
- mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
+ mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
updateSafeDeviceInfoList();
return oldDeviceInfo;
}
@@ -967,15 +973,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*
- * @param logicalAddress logical address of device to be removed
+ * @param id id of device to be removed
* @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
*/
@ServiceThreadOnly
- private HdmiDeviceInfo removeDeviceInfo(int logicalAddress) {
+ private HdmiDeviceInfo removeDeviceInfo(int id) {
assertRunOnServiceThread();
- HdmiDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
+ HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
if (deviceInfo != null) {
- mDeviceInfos.remove(logicalAddress);
+ mDeviceInfos.remove(id);
}
updateSafeDeviceInfoList();
return deviceInfo;
@@ -1008,10 +1014,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
/**
* Return external input devices.
*/
- List<HdmiDeviceInfo> getSafeExternalInputs() {
- synchronized (mLock) {
- return mSafeExternalInputs;
- }
+ List<HdmiDeviceInfo> getSafeExternalInputsLocked() {
+ return mSafeExternalInputs;
}
@ServiceThreadOnly
@@ -1035,7 +1039,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (int i = 0; i < mDeviceInfos.size(); ++i) {
HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
- if (isLocalDeviceAddress(i)) {
+ if (isLocalDeviceAddress(info.getLogicalAddress())) {
continue;
}
if (info.isSourceType() && !hideDevicesBehindLegacySwitch(info)) {
@@ -1095,23 +1099,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
HdmiDeviceInfo getAvrDeviceInfo() {
assertRunOnServiceThread();
- return getDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
+ return getCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
/**
* Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
*
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- * This is not thread-safe. For thread safety, call {@link #getSafeDeviceInfo(int)}.
+ * This is not thread-safe. For thread safety, call {@link #getSafeCecDeviceInfo(int)}.
*
- * @param logicalAddress logical address to be retrieved
+ * @param address logical address of the device to be retrieved
* @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
* Returns null if no logical address matched
*/
@ServiceThreadOnly
- HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
+ HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
- return mDeviceInfos.get(logicalAddress);
+ return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
}
boolean hasSystemAudioDevice() {
@@ -1119,19 +1122,24 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
HdmiDeviceInfo getSafeAvrDeviceInfo() {
- return getSafeDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
+ return getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
/**
- * Thread safe version of {@link #getDeviceInfo(int)}.
+ * Thread safe version of {@link #getCecDeviceInfo(int)}.
*
* @param logicalAddress logical address to be retrieved
* @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
* Returns null if no logical address matched
*/
- HdmiDeviceInfo getSafeDeviceInfo(int logicalAddress) {
+ HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) {
synchronized (mLock) {
- return mSafeAllDeviceInfos.get(logicalAddress);
+ for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+ if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) {
+ return info;
+ }
+ }
+ return null;
}
}
@@ -1160,7 +1168,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
final void removeCecDevice(int address) {
assertRunOnServiceThread();
- HdmiDeviceInfo info = removeDeviceInfo(address);
+ HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
mCecMessageCache.flushMessagesFrom(address);
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
@@ -1240,7 +1248,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
boolean isInDeviceList(int logicalAddress, int physicalAddress) {
assertRunOnServiceThread();
- HdmiDeviceInfo device = getDeviceInfo(logicalAddress);
+ HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress);
if (device == null) {
return false;
}
@@ -1427,7 +1435,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
private boolean checkRecorder(int recorderAddress) {
- HdmiDeviceInfo device = getDeviceInfo(recorderAddress);
+ HdmiDeviceInfo device = getCecDeviceInfo(recorderAddress);
return (device != null)
&& (HdmiUtils.getTypeFromAddress(recorderAddress)
== HdmiDeviceInfo.DEVICE_RECORDER);
@@ -1528,7 +1536,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
- HdmiDeviceInfo info = getDeviceInfo(logicalAddress);
+ HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
if (info == null) {
Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
return;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 0855bfa..b53cd45 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -338,6 +338,21 @@ public class HdmiCecMessageBuilder {
}
/**
+ * Build &lt;Report Menu Status&gt; command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param menuStatus menu status of the device
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildReportMenuStatus(int src, int dest, int menuStatus) {
+ byte[] param = new byte[] {
+ (byte) (menuStatus)
+ };
+ return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param);
+ }
+
+ /**
* Build &lt;System Audio Mode Request&gt; command.
*
* @param src source address of command
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 2818ea7..5b99f14 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -16,6 +16,8 @@
package com.android.server.hdmi;
+import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
+import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
import static com.android.server.hdmi.Constants.DISABLED;
import static com.android.server.hdmi.Constants.ENABLED;
import static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP;
@@ -76,6 +78,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
/**
* Provides a service for sending and processing HDMI control messages,
@@ -121,7 +124,7 @@ public final class HdmiControlService extends SystemService {
void onPollingFinished(List<Integer> ackedAddress);
}
- private class PowerStateReceiver extends BroadcastReceiver {
+ private class HdmiControlBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
@@ -135,6 +138,12 @@ public final class HdmiControlService extends SystemService {
onWakeUp();
}
break;
+ case Intent.ACTION_CONFIGURATION_CHANGED:
+ String language = Locale.getDefault().getISO3Language();
+ if (!mLanguage.equals(language)) {
+ onLanguageChanged(language);
+ }
+ break;
}
}
}
@@ -202,6 +211,9 @@ public final class HdmiControlService extends SystemService {
@GuardedBy("mLock")
private boolean mMhlInputChangeEnabled;
+ @GuardedBy("mLock")
+ private List<HdmiDeviceInfo> mMhlDevices;
+
// List of listeners registered by callers that want to get notified of
// system audio mode changes.
private final ArrayList<IHdmiSystemAudioModeChangeListener>
@@ -233,12 +245,16 @@ public final class HdmiControlService extends SystemService {
private HdmiCecMessageValidator mMessageValidator;
- private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
+ private final HdmiControlBroadcastReceiver
+ mHdmiControlBroadcastReceiver = new HdmiControlBroadcastReceiver();
@ServiceThreadOnly
private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
@ServiceThreadOnly
+ private String mLanguage = Locale.getDefault().getISO3Language();
+
+ @ServiceThreadOnly
private boolean mStandbyMessageReceived = false;
@ServiceThreadOnly
@@ -293,6 +309,7 @@ public final class HdmiControlService extends SystemService {
Slog.i(TAG, "Device does not support MHL-control.");
}
initPortInfo();
+ mMhlDevices = Collections.emptyList();
mMessageValidator = new HdmiCecMessageValidator(this);
publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
@@ -301,7 +318,8 @@ public final class HdmiControlService extends SystemService {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
- getContext().registerReceiver(mPowerStateReceiver, filter);
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter);
}
}
@@ -320,7 +338,6 @@ public final class HdmiControlService extends SystemService {
}
}
-
private void registerContentObserver() {
ContentResolver resolver = getContext().getContentResolver();
String[] settings = new String[] {
@@ -330,7 +347,7 @@ public final class HdmiControlService extends SystemService {
Global.MHL_INPUT_SWITCHING_ENABLED,
Global.MHL_POWER_CHARGE_ENABLED
};
- for (String s: settings) {
+ for (String s : settings) {
resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
UserHandle.USER_ALL);
}
@@ -351,7 +368,7 @@ public final class HdmiControlService extends SystemService {
break;
case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
tv().setAutoWakeup(enabled);
- setOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled));
+ setCecOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled));
break;
case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
tv().setAutoDeviceOff(enabled);
@@ -490,6 +507,10 @@ public final class HdmiControlService extends SystemService {
}
}
+ List<HdmiPortInfo> getPortInfo() {
+ return mPortInfo;
+ }
+
/**
* Returns HDMI port information for the given port id.
*
@@ -568,7 +589,7 @@ public final class HdmiControlService extends SystemService {
if (tv == null) {
return null;
}
- return tv.getDeviceInfo(logicalAddress);
+ return tv.getCecDeviceInfo(logicalAddress);
}
/**
@@ -684,16 +705,16 @@ public final class HdmiControlService extends SystemService {
/**
* Called when a new hotplug event is issued.
*
- * @param portNo hdmi port number where hot plug event issued.
+ * @param portId hdmi port number where hot plug event issued.
* @param connected whether to be plugged in or not
*/
@ServiceThreadOnly
- void onHotplug(int portNo, boolean connected) {
+ void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
- device.onHotplug(portNo, connected);
+ device.onHotplug(portId, connected);
}
- announceHotplugEvent(portNo, connected);
+ announceHotplugEvent(portId, connected);
}
/**
@@ -794,10 +815,15 @@ public final class HdmiControlService extends SystemService {
HdmiMhlLocalDevice device = mMhlController.removeLocalDevice(portId);
if (device != null) {
device.onDeviceRemoved();
+ // There is no explicit event for device removal unlike capability register event
+ // used for device addition . Hence we remove the device on hotplug event.
+ invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_REMOVE_DEVICE);
+ updateSafeMhlInput();
} else {
Slog.w(TAG, "No device to remove:[portId=" + portId);
}
}
+ announceHotplugEvent(portId, connected);
}
@ServiceThreadOnly
@@ -824,18 +850,45 @@ public final class HdmiControlService extends SystemService {
}
@ServiceThreadOnly
- void handleCapabilityRegisterChanged(int portId, int adopterId, int deviceId) {
+ void handleMhlCapabilityRegisterChanged(int portId, int adopterId, int deviceId) {
assertRunOnServiceThread();
HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
- // Hot plug event should be called before capability register change event.
+
+ // Hotplug event should already have been called before capability register change event.
if (device != null) {
device.setCapabilityRegister(adopterId, deviceId);
+ invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_ADD_DEVICE);
+ updateSafeMhlInput();
} else {
Slog.w(TAG, "No mhl device exists for capability register change event[portId:"
+ portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]");
}
}
+ @ServiceThreadOnly
+ private void updateSafeMhlInput() {
+ assertRunOnServiceThread();
+ List<HdmiDeviceInfo> inputs = Collections.emptyList();
+ SparseArray<HdmiMhlLocalDevice> devices = mMhlController.getAllLocalDevices();
+ for (int i = 0; i < devices.size(); ++i) {
+ HdmiMhlLocalDevice device = devices.valueAt(i);
+ HdmiDeviceInfo info = device.getInfo();
+ if (info != null) {
+ if (inputs.isEmpty()) {
+ inputs = new ArrayList<>();
+ }
+ inputs.add(device.getInfo());
+ }
+ }
+ synchronized (mLock) {
+ mMhlDevices = inputs;
+ }
+ }
+
+ private List<HdmiDeviceInfo> getMhlDevicesLocked() {
+ return mMhlDevices;
+ }
+
// Record class that monitors the event of the caller of being killed. Used to clean up
// the listener list and record list accordingly.
private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
@@ -949,7 +1002,7 @@ public final class HdmiControlService extends SystemService {
}
@Override
- public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
+ public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
enforceAccessPermission();
runOnServiceThread(new Runnable() {
@Override
@@ -964,7 +1017,18 @@ public final class HdmiControlService extends SystemService {
invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- tv.deviceSelect(logicalAddress, callback);
+ if (mMhlController != null) {
+ HdmiMhlLocalDevice device = mMhlController.getLocalDeviceById(deviceId);
+ if (device != null) {
+ // Upon selecting MHL device, we send RAP[Content On] to wake up
+ // the connected mobile device, start routing control to switch ports.
+ // callback is handled by MHL action.
+ device.turnOn(callback);
+ tv.doManualPortSwitching(device.getInfo().getPortId(), null);
+ return;
+ }
+ }
+ tv.deviceSelect(deviceId, callback);
}
});
}
@@ -996,12 +1060,21 @@ public final class HdmiControlService extends SystemService {
runOnServiceThread(new Runnable() {
@Override
public void run() {
- HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
- if (localDevice == null) {
- Slog.w(TAG, "Local device not available");
- return;
+ if (mMhlController != null) {
+ HdmiMhlLocalDevice device = mMhlController.getLocalDevice(mActivePortId);
+ if (device != null) {
+ device.sendKeyEvent(keyCode, isPressed);
+ return;
+ }
+ }
+ if (mCecController != null) {
+ HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
+ if (localDevice == null) {
+ Slog.w(TAG, "Local device not available");
+ return;
+ }
+ localDevice.sendKeyEvent(keyCode, isPressed);
}
- localDevice.sendKeyEvent(keyCode, isPressed);
}
});
}
@@ -1064,7 +1137,7 @@ public final class HdmiControlService extends SystemService {
@Override
public List<HdmiPortInfo> getPortInfo() {
enforceAccessPermission();
- return mPortInfo;
+ return HdmiControlService.this.getPortInfo();
}
@Override
@@ -1130,10 +1203,12 @@ public final class HdmiControlService extends SystemService {
// No need to hold the lock for obtaining TV device as the local device instance
// is preserved while the HDMI control is enabled.
HdmiCecLocalDeviceTv tv = tv();
- if (tv == null) {
- return Collections.emptyList();
+ synchronized (mLock) {
+ List<HdmiDeviceInfo> cecDevices = (tv == null)
+ ? Collections.<HdmiDeviceInfo>emptyList()
+ : tv.getSafeExternalInputsLocked();
+ return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked());
}
- return tv.getSafeExternalInputs();
}
@Override
@@ -1634,12 +1709,28 @@ public final class HdmiControlService extends SystemService {
});
}
+ @ServiceThreadOnly
+ private void onLanguageChanged(String language) {
+ assertRunOnServiceThread();
+ mLanguage = language;
+
+ if (isTvDevice()) {
+ tv().broadcastMenuLanguage(language);
+ }
+ }
+
private void disableDevices(PendingActionClearedCallback callback) {
- for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
- device.disableDevice(mStandbyMessageReceived, callback);
+ if (mCecController != null) {
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ device.disableDevice(mStandbyMessageReceived, callback);
+ }
+ if (isTvDevice()) {
+ unregisterSettingsObserver();
+ }
}
- if (isTvDevice()) {
- unregisterSettingsObserver();
+
+ if (mMhlController != null) {
+ mMhlController.clearAllLocalDevices();
}
}
@@ -1711,7 +1802,7 @@ public final class HdmiControlService extends SystemService {
}
@ServiceThreadOnly
- void setOption(int key, int value) {
+ void setCecOption(int key, int value) {
assertRunOnServiceThread();
mCecController.setOption(key, value);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 23f19ff..22a519b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -206,6 +206,22 @@ final class HdmiUtils {
return list;
}
+ static <T> List<T> mergeToUnmodifiableList(List<T> a, List<T> b) {
+ if (a.isEmpty() && b.isEmpty()) {
+ return Collections.emptyList();
+ }
+ if (a.isEmpty()) {
+ return Collections.unmodifiableList(b);
+ }
+ if (b.isEmpty()) {
+ return Collections.unmodifiableList(a);
+ }
+ List<T> newList = new ArrayList<>();
+ newList.addAll(a);
+ newList.addAll(b);
+ return Collections.unmodifiableList(newList);
+ }
+
/**
* See if the new path is affecting the active path.
*
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 51e68b6..722be71 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -207,7 +207,7 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
}
private void mayChangeRoutingPath(int address) {
- HdmiDeviceInfo info = tv().getDeviceInfo(address);
+ HdmiDeviceInfo info = tv().getCecDeviceInfo(address);
if (info != null) {
tv().handleRemoveActiveRoutingPath(info.getPhysicalAddress());
}
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 9f09eb4..ed978e0 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -109,12 +109,12 @@ final class SendKeyAction extends HdmiCecFeatureAction {
}
private void sendKeyDown(int keycode) {
- byte[] keycodeAndParam = getCecKeycodeAndParam(keycode);
- if (keycodeAndParam == null) {
+ int cecKeycode = HdmiCecKeycode.androidKeyToCecKey(keycode);
+ if (cecKeycode == HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
return;
}
sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
- mTargetAddress, keycodeAndParam));
+ mTargetAddress, new byte[] { (byte) (cecKeycode & 0xFF) }));
}
private void sendKeyUp() {
@@ -141,13 +141,4 @@ final class SendKeyAction extends HdmiCecFeatureAction {
sendKeyDown(mLastKeycode);
addTimer(mState, IRT_MS);
}
-
- // Converts the Android key code to corresponding CEC key code definition. Those CEC keys
- // with additional parameters should be mapped from individual Android key code. 'Select
- // Broadcast' with the parameter 'cable', for instance, shall have its counterpart such as
- // KeyEvent.KEYCODE_TV_BROADCAST_CABLE.
- // The return byte array contains both UI command (keycode) and optional parameter.
- private byte[] getCecKeycodeAndParam(int keycode) {
- return HdmiCecKeycode.androidKeyToCecKey(keycode);
- }
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 7c1681c..93dceff 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -774,6 +774,8 @@ public class InputManagerService extends IInputManager.Stub
.setContentIntent(mKeyboardLayoutIntent)
.setSmallIcon(R.drawable.ic_settings_language)
.setPriority(Notification.PRIORITY_LOW)
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
.build();
mNotificationManager.notifyAsUser(null,
R.string.select_keyboard_layout_notification_title,
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 94cf668..9dcc529 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -23,6 +23,7 @@ import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.IHardwareService;
import android.os.Message;
+import android.os.Trace;
import android.util.Slog;
import java.io.FileInputStream;
@@ -105,7 +106,12 @@ public class LightsService extends SystemService {
mMode = mode;
mOnMS = onMS;
mOffMS = offMS;
- setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", " + color + ")");
+ try {
+ setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index e549ead..978a9f4 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -22,17 +22,16 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.media.AudioManager;
+import android.media.AudioManagerInternal;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.routing.IMediaRouter;
-import android.media.routing.IMediaRouterDelegate;
-import android.media.routing.IMediaRouterStateCallback;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.ISessionController;
import android.media.session.ISessionControllerCallback;
import android.media.session.MediaController;
+import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession;
import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
@@ -52,6 +51,8 @@ import android.util.Log;
import android.util.Slog;
import android.view.KeyEvent;
+import com.android.server.LocalServices;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.UUID;
@@ -93,7 +94,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
new ArrayList<ISessionControllerCallback>();
private long mFlags;
- private IMediaRouter mMediaRouter;
private PendingIntent mMediaButtonReceiver;
private PendingIntent mLaunchIntent;
@@ -111,7 +111,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
// Volume handling fields
private AudioAttributes mAudioAttrs;
private AudioManager mAudioManager;
- private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
+ private AudioManagerInternal mAudioManagerInternal;
+ private int mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL;
private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
private int mMaxVolume = 0;
private int mCurrentVolume = 0;
@@ -134,6 +135,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mService = service;
mHandler = new MessageHandler(handler.getLooper());
mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
+ mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
}
@@ -227,7 +229,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
*
* @param direction The direction to adjust volume in.
*/
- public void adjustVolume(int direction, int flags) {
+ public void adjustVolume(int direction, int flags, String packageName, int uid) {
if (isPlaybackActive(false)) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
@@ -236,9 +238,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
} else if (direction < -1) {
direction = -1;
}
- if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
+ if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- mAudioManager.adjustStreamVolume(stream, direction, flags);
+ mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, packageName,
+ uid);
} else {
if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
@@ -262,10 +265,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
}
- public void setVolumeTo(int value, int flags) {
- if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
+ public void setVolumeTo(int value, int flags, String packageName, int uid) {
+ if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- mAudioManager.setStreamVolume(stream, value, flags);
+ mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid);
} else {
if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
// Nothing to do. The volume can't be set directly.
@@ -397,6 +400,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
return;
}
mDestroyed = true;
+ mHandler.post(MessageHandler.MSG_DESTROYED);
}
}
@@ -575,6 +579,29 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
}
+ private void pushSessionDestroyed() {
+ synchronized (mLock) {
+ // This is the only method that may be (and can only be) called
+ // after the session is destroyed.
+ if (!mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onSessionDestroyed();
+ } catch (DeadObjectException e) {
+ Log.w(TAG, "Removing dead callback in pushEvent.", e);
+ mControllerCallbacks.remove(i);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushEvent.", e);
+ }
+ }
+ // After notifying clear all listeners
+ mControllerCallbacks.clear();
+ }
+ }
+
private PlaybackState getStateWithUpdatedPosition() {
PlaybackState state = mPlaybackState;
long duration = -1;
@@ -662,12 +689,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
- public void setMediaRouter(IMediaRouter router) {
- mMediaRouter = router;
- mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
- }
-
- @Override
public void setMediaButtonReceiver(PendingIntent pi) {
mMediaButtonReceiver = pi;
}
@@ -728,8 +749,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
public void setPlaybackToLocal(AudioAttributes attributes) {
boolean typeChanged;
synchronized (mLock) {
- typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE;
- mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
+ typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
+ mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL;
if (attributes != null) {
mAudioAttrs = attributes;
} else {
@@ -745,8 +766,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
public void setPlaybackToRemote(int control, int max) {
boolean typeChanged;
synchronized (mLock) {
- typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL;
- mVolumeType = MediaSession.PLAYBACK_TYPE_REMOTE;
+ typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL;
+ mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE;
mVolumeControlType = control;
mMaxVolume = max;
}
@@ -919,6 +940,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
@Override
public void registerCallbackListener(ISessionControllerCallback cb) {
synchronized (mLock) {
+ // If this session is already destroyed tell the caller and
+ // don't add them.
+ if (mDestroyed) {
+ try {
+ cb.onSessionDestroyed();
+ } catch (Exception e) {
+ // ignored
+ }
+ return;
+ }
if (getControllerCbIndexForCb(cb) < 0) {
mControllerCallbacks.add(cb);
if (DEBUG) {
@@ -968,7 +999,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
int type;
int max;
int current;
- if (mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE) {
+ if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
type = mVolumeControlType;
max = mMaxVolume;
current = mOptimisticVolume != -1 ? mOptimisticVolume
@@ -984,20 +1015,22 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
- public void adjustVolume(int direction, int flags) {
+ public void adjustVolume(int direction, int flags, String packageName) {
+ int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.adjustVolume(direction, flags);
+ MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public void setVolumeTo(int value, int flags) {
+ public void setVolumeTo(int value, int flags, String packageName) {
+ int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.setVolumeTo(value, flags);
+ MediaSessionRecord.this.setVolumeTo(value, flags, packageName, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1105,13 +1138,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
public boolean isTransportControlEnabled() {
return MediaSessionRecord.this.isTransportControlEnabled();
}
-
- @Override
- public IMediaRouterDelegate createMediaRouterDelegate(
- IMediaRouterStateCallback callback) {
- // todo
- return null;
- }
}
private class MessageHandler extends Handler {
@@ -1123,6 +1149,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private static final int MSG_SEND_EVENT = 6;
private static final int MSG_UPDATE_SESSION_STATE = 7;
private static final int MSG_UPDATE_VOLUME = 8;
+ private static final int MSG_DESTROYED = 9;
public MessageHandler(Looper looper) {
super(looper);
@@ -1154,6 +1181,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
case MSG_UPDATE_VOLUME:
pushVolumeUpdate();
break;
+ case MSG_DESTROYED:
+ pushSessionDestroyed();
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0514f48..0c6d46c 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -37,6 +37,7 @@ import android.media.session.IActiveSessionsListener;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.ISessionManager;
+import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Binder;
@@ -812,8 +813,9 @@ public class MediaSessionService extends SystemService implements Monitor {
Log.e(TAG, "Error adjusting default volume.", e);
}
} else {
- session.adjustVolume(direction, flags);
- if (session.getPlaybackType() == MediaSession.PLAYBACK_TYPE_REMOTE
+ session.adjustVolume(direction, flags, getContext().getPackageName(),
+ UserHandle.myUserId());
+ if (session.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE
&& mRvc != null) {
try {
mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index fec11fb..a9bc818 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import android.media.session.MediaController.PlaybackInfo;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
import android.os.UserHandle;
@@ -209,7 +210,7 @@ public class MediaSessionStack {
int size = records.size();
for (int i = 0; i < size; i++) {
MediaSessionRecord record = records.get(i);
- if (record.getPlaybackType() == MediaSession.PLAYBACK_TYPE_REMOTE) {
+ if (record.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
return record;
}
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 289b5aa..69d1dc9 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -26,6 +26,8 @@ import android.hardware.display.DisplayManager;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
+import android.media.projection.IMediaProjectionWatcherCallback;
+import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Binder;
import android.os.Handler;
@@ -34,6 +36,7 @@ import android.os.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
@@ -59,15 +62,20 @@ public final class MediaProjectionManagerService extends SystemService
private static final String TAG = "MediaProjectionManagerService";
private final Object mLock = new Object(); // Protects the list of media projections
- private final Map<IBinder, MediaProjection> mProjectionGrants;
+ private final Map<IBinder, IBinder.DeathRecipient> mDeathEaters;
+ private final CallbackDelegate mCallbackDelegate;
private final Context mContext;
private final AppOpsManager mAppOps;
+ private IBinder mProjectionToken;
+ private MediaProjection mProjectionGrant;
+
public MediaProjectionManagerService(Context context) {
super(context);
mContext = context;
- mProjectionGrants = new ArrayMap<IBinder, MediaProjection>();
+ mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>();
+ mCallbackDelegate = new CallbackDelegate();
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Watchdog.getInstance().addMonitor(this);
}
@@ -83,13 +91,97 @@ public final class MediaProjectionManagerService extends SystemService
synchronized (mLock) { /* check for deadlock */ }
}
+ private void startProjectionLocked(final MediaProjection projection) {
+ if (mProjectionGrant != null) {
+ mProjectionGrant.stop();
+ }
+ mProjectionToken = projection.asBinder();
+ mProjectionGrant = projection;
+ dispatchStart(projection);
+ }
+
+ private void stopProjectionLocked(final MediaProjection projection) {
+ mProjectionToken = null;
+ mProjectionGrant = null;
+ dispatchStop(projection);
+ }
+
+ private void addCallback(final IMediaProjectionWatcherCallback callback) {
+ IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ unlinkDeathRecipientLocked(callback);
+ removeCallback(callback);
+ }
+ }
+ };
+ synchronized (mLock) {
+ mCallbackDelegate.add(callback);
+ linkDeathRecipientLocked(callback, deathRecipient);
+ }
+ }
+
+ private void removeCallback(IMediaProjectionWatcherCallback callback) {
+ synchronized (mLock) {
+ unlinkDeathRecipientLocked(callback);
+ removeCallback(callback);
+ }
+ }
+
+ private void linkDeathRecipientLocked(IMediaProjectionWatcherCallback callback,
+ IBinder.DeathRecipient deathRecipient) {
+ try {
+ final IBinder token = callback.asBinder();
+ token.linkToDeath(deathRecipient, 0);
+ mDeathEaters.put(token, deathRecipient);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to link to death for media projection monitoring callback", e);
+ }
+ }
+
+ private void unlinkDeathRecipientLocked(IMediaProjectionWatcherCallback callback) {
+ final IBinder token = callback.asBinder();
+ IBinder.DeathRecipient deathRecipient = mDeathEaters.remove(token);
+ if (deathRecipient != null) {
+ token.unlinkToDeath(deathRecipient, 0);
+ }
+ }
+
+ private void dispatchStart(MediaProjection projection) {
+ mCallbackDelegate.dispatchStart(projection);
+ }
+
+ private void dispatchStop(MediaProjection projection) {
+ mCallbackDelegate.dispatchStop(projection);
+ }
+
+ private boolean isValidMediaProjection(IBinder token) {
+ synchronized (mLock) {
+ if (mProjectionToken != null) {
+ return mProjectionToken.equals(token);
+ }
+ return false;
+ }
+ }
+
+ private MediaProjectionInfo getActiveProjectionInfo() {
+ synchronized (mLock) {
+ if (mProjectionGrant == null) {
+ return null;
+ }
+ return mProjectionGrant.getProjectionInfo();
+ }
+ }
+
private void dump(final PrintWriter pw) {
pw.println("MEDIA PROJECTION MANAGER (dumpsys media_projection)");
synchronized (mLock) {
- Collection<MediaProjection> projections = mProjectionGrants.values();
- pw.println("Media Projections: size=" + projections.size());
- for (MediaProjection mp : projections) {
- mp.dump(pw, " ");
+ pw.println("Media Projection: ");
+ if (mProjectionGrant != null ) {
+ mProjectionGrant.dump(pw);
+ } else {
+ pw.println("null");
}
}
}
@@ -115,11 +207,14 @@ public final class MediaProjectionManagerService extends SystemService
@Override // Binder call
public IMediaProjection createProjection(int uid, String packageName, int type,
boolean isPermanentGrant) {
- if (mContext.checkCallingPermission(Manifest.permission.CREATE_MEDIA_PROJECTION)
+ if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
!= PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires CREATE_MEDIA_PROJECTION in order to grant "
+ throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to grant "
+ "projection permission");
}
+ if (packageName == null || packageName.isEmpty()) {
+ throw new IllegalArgumentException("package name must not be empty");
+ }
long callingToken = Binder.clearCallingIdentity();
MediaProjection projection;
try {
@@ -136,7 +231,71 @@ public final class MediaProjectionManagerService extends SystemService
@Override // Binder call
public boolean isValidMediaProjection(IMediaProjection projection) {
- return mProjectionGrants.containsKey(projection.asBinder());
+ return MediaProjectionManagerService.this.isValidMediaProjection(
+ projection.asBinder());
+ }
+
+ @Override // Binder call
+ public MediaProjectionInfo getActiveProjectionInfo() {
+ if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add "
+ + "projection callbacks");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return MediaProjectionManagerService.this.getActiveProjectionInfo();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void stopActiveProjection() {
+ if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add "
+ + "projection callbacks");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (mProjectionGrant != null) {
+ mProjectionGrant.stop();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ }
+
+ @Override //Binder call
+ public void addCallback(final IMediaProjectionWatcherCallback callback) {
+ if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add "
+ + "projection callbacks");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ MediaProjectionManagerService.this.addCallback(callback);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeCallback(IMediaProjectionWatcherCallback callback) {
+ if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to remove "
+ + "projection callbacks");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ MediaProjectionManagerService.this.removeCallback(callback);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override // Binder call
@@ -157,25 +316,27 @@ public final class MediaProjectionManagerService extends SystemService
}
}
+
private boolean checkPermission(String packageName, String permission) {
return mContext.getPackageManager().checkPermission(permission, packageName)
== PackageManager.PERMISSION_GRANTED;
}
}
- private final class MediaProjection extends IMediaProjection.Stub implements DeathRecipient {
- public int uid;
- public String packageName;
+ private final class MediaProjection extends IMediaProjection.Stub {
+ public final int uid;
+ public final String packageName;
+ public final UserHandle userHandle;
private IBinder mToken;
+ private IBinder.DeathRecipient mDeathEater;
private int mType;
- private CallbackDelegate mCallbackDelegate;
public MediaProjection(int type, int uid, String packageName) {
mType = type;
this.uid = uid;
this.packageName = packageName;
- mCallbackDelegate = new CallbackDelegate();
+ userHandle = new UserHandle(UserHandle.getUserId(uid));
}
@Override // Binder call
@@ -220,49 +381,50 @@ public final class MediaProjectionManagerService extends SystemService
}
@Override // Binder call
- public void start(IMediaProjectionCallback callback) {
+ public void start(final IMediaProjectionCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
synchronized (mLock) {
- if (mProjectionGrants.containsKey(asBinder())) {
+ if (isValidMediaProjection(asBinder())) {
throw new IllegalStateException(
"Cannot start already started MediaProjection");
}
addCallback(callback);
try {
mToken = callback.asBinder();
- mToken.linkToDeath(this, 0);
+ mDeathEater = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mCallbackDelegate.remove(callback);
+ stop();
+ }
+ };
+ mToken.linkToDeath(mDeathEater, 0);
} catch (RemoteException e) {
Slog.w(TAG,
"MediaProjectionCallbacks must be valid, aborting MediaProjection", e);
return;
}
- mProjectionGrants.put(asBinder(), this);
+ startProjectionLocked(this);
}
}
@Override // Binder call
public void stop() {
synchronized (mLock) {
- if (!mProjectionGrants.containsKey(asBinder())) {
+ if (!isValidMediaProjection(asBinder())) {
Slog.w(TAG, "Attempted to stop inactive MediaProjection "
+ "(uid=" + Binder.getCallingUid() + ", "
+ "pid=" + Binder.getCallingPid() + ")");
return;
}
- mToken.unlinkToDeath(this, 0);
- mCallbackDelegate.dispatchStop();
- mProjectionGrants.remove(asBinder());
+ mToken.unlinkToDeath(mDeathEater, 0);
+ stopProjectionLocked(this);
}
}
@Override
- public void binderDied() {
- stop();
- }
-
- @Override
public void addCallback(IMediaProjectionCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
@@ -278,52 +440,143 @@ public final class MediaProjectionManagerService extends SystemService
mCallbackDelegate.remove(callback);
}
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "(" + packageName + ", uid=" + uid + "): " + typeToString(mType));
+ public MediaProjectionInfo getProjectionInfo() {
+ return new MediaProjectionInfo(packageName, userHandle);
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("(" + packageName + ", uid=" + uid + "): " + typeToString(mType));
}
}
+
private static class CallbackDelegate {
- private static final int MSG_ON_STOP = 0;
- private List<IMediaProjectionCallback> mCallbacks;
+ private Map<IBinder, IMediaProjectionCallback> mClientCallbacks;
+ private Map<IBinder, IMediaProjectionWatcherCallback> mWatcherCallbacks;
private Handler mHandler;
private Object mLock = new Object();
public CallbackDelegate() {
mHandler = new Handler(Looper.getMainLooper(), null, true /*async*/);
- mCallbacks = new ArrayList<IMediaProjectionCallback>();
+ mClientCallbacks = new ArrayMap<IBinder, IMediaProjectionCallback>();
+ mWatcherCallbacks = new ArrayMap<IBinder, IMediaProjectionWatcherCallback>();
}
public void add(IMediaProjectionCallback callback) {
synchronized (mLock) {
- mCallbacks.add(callback);
+ mClientCallbacks.put(callback.asBinder(), callback);
+ }
+ }
+
+ public void add(IMediaProjectionWatcherCallback callback) {
+ synchronized (mLock) {
+ mWatcherCallbacks.put(callback.asBinder(), callback);
}
}
public void remove(IMediaProjectionCallback callback) {
synchronized (mLock) {
- mCallbacks.remove(callback);
+ mClientCallbacks.remove(callback.asBinder());
}
}
- public void dispatchStop() {
+ public void remove(IMediaProjectionWatcherCallback callback) {
synchronized (mLock) {
- for (final IMediaProjectionCallback callback : mCallbacks) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- callback.onStop();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify media projection has stopped", e);
- }
- }
- });
+ mWatcherCallbacks.remove(callback.asBinder());
+ }
+ }
+
+ public void dispatchStart(MediaProjection projection) {
+ if (projection == null) {
+ Slog.e(TAG, "Tried to dispatch start notification for a null media projection."
+ + " Ignoring!");
+ return;
+ }
+ synchronized (mLock) {
+ for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) {
+ MediaProjectionInfo info = projection.getProjectionInfo();
+ mHandler.post(new WatcherStartCallback(info, callback));
}
}
}
+
+ public void dispatchStop(MediaProjection projection) {
+ if (projection == null) {
+ Slog.e(TAG, "Tried to dispatch stop notification for a null media projection."
+ + " Ignoring!");
+ return;
+ }
+ synchronized (mLock) {
+ for (IMediaProjectionCallback callback : mClientCallbacks.values()) {
+ mHandler.post(new ClientStopCallback(callback));
+ }
+
+ for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) {
+ MediaProjectionInfo info = projection.getProjectionInfo();
+ mHandler.post(new WatcherStopCallback(info, callback));
+ }
+ }
+ }
+ }
+
+ private static final class WatcherStartCallback implements Runnable {
+ private IMediaProjectionWatcherCallback mCallback;
+ private MediaProjectionInfo mInfo;
+
+ public WatcherStartCallback(MediaProjectionInfo info,
+ IMediaProjectionWatcherCallback callback) {
+ mInfo = info;
+ mCallback = callback;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mCallback.onStart(mInfo);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify media projection has stopped", e);
+ }
+ }
+ }
+
+ private static final class WatcherStopCallback implements Runnable {
+ private IMediaProjectionWatcherCallback mCallback;
+ private MediaProjectionInfo mInfo;
+
+ public WatcherStopCallback(MediaProjectionInfo info,
+ IMediaProjectionWatcherCallback callback) {
+ mInfo = info;
+ mCallback = callback;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mCallback.onStop(mInfo);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify media projection has stopped", e);
+ }
+ }
}
+ private static final class ClientStopCallback implements Runnable {
+ private IMediaProjectionCallback mCallback;
+
+ public ClientStopCallback(IMediaProjectionCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mCallback.onStop();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify media projection has stopped", e);
+ }
+ }
+ }
+
+
private static String typeToString(int type) {
switch (type) {
case MediaProjectionManager.TYPE_SCREEN_CAPTURE:
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index 907eeb2..857b9e9 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -20,10 +20,10 @@ import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
-import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.StaticIpConfiguration;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
@@ -41,6 +41,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
+import java.net.Inet4Address;
import java.util.Map;
public class IpConfigStore {
@@ -69,40 +70,32 @@ public class IpConfigStore {
}
private boolean writeConfig(DataOutputStream out, int configKey,
- IpConfiguration config) throws IOException {
+ IpConfiguration config) throws IOException {
boolean written = false;
try {
- LinkProperties linkProperties = config.linkProperties;
switch (config.ipAssignment) {
case STATIC:
out.writeUTF(IP_ASSIGNMENT_KEY);
out.writeUTF(config.ipAssignment.toString());
- for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
- out.writeUTF(LINK_ADDRESS_KEY);
- out.writeUTF(linkAddr.getAddress().getHostAddress());
- out.writeInt(linkAddr.getPrefixLength());
- }
- for (RouteInfo route : linkProperties.getRoutes()) {
- out.writeUTF(GATEWAY_KEY);
- LinkAddress dest = route.getDestinationLinkAddress();
- if (dest != null) {
- out.writeInt(1);
- out.writeUTF(dest.getAddress().getHostAddress());
- out.writeInt(dest.getPrefixLength());
- } else {
- out.writeInt(0);
+ StaticIpConfiguration staticIpConfiguration = config.staticIpConfiguration;
+ if (staticIpConfiguration != null) {
+ if (staticIpConfiguration.ipAddress != null) {
+ LinkAddress ipAddress = staticIpConfiguration.ipAddress;
+ out.writeUTF(LINK_ADDRESS_KEY);
+ out.writeUTF(ipAddress.getAddress().getHostAddress());
+ out.writeInt(ipAddress.getPrefixLength());
}
- if (route.getGateway() != null) {
- out.writeInt(1);
- out.writeUTF(route.getGateway().getHostAddress());
- } else {
- out.writeInt(0);
+ if (staticIpConfiguration.gateway != null) {
+ out.writeUTF(GATEWAY_KEY);
+ out.writeInt(0); // Default route.
+ out.writeInt(1); // Have a gateway.
+ out.writeUTF(staticIpConfiguration.gateway.getHostAddress());
+ }
+ for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {
+ out.writeUTF(DNS_KEY);
+ out.writeUTF(inetAddr.getHostAddress());
}
- }
- for (InetAddress inetAddr : linkProperties.getDnsServers()) {
- out.writeUTF(DNS_KEY);
- out.writeUTF(inetAddr.getHostAddress());
}
written = true;
break;
@@ -121,7 +114,7 @@ public class IpConfigStore {
switch (config.proxySettings) {
case STATIC:
- ProxyInfo proxyProperties = linkProperties.getHttpProxy();
+ ProxyInfo proxyProperties = config.httpProxy;
String exclusionList = proxyProperties.getExclusionListAsString();
out.writeUTF(PROXY_SETTINGS_KEY);
out.writeUTF(config.proxySettings.toString());
@@ -134,7 +127,7 @@ public class IpConfigStore {
written = true;
break;
case PAC:
- ProxyInfo proxyPacProperties = linkProperties.getHttpProxy();
+ ProxyInfo proxyPacProperties = config.httpProxy;
out.writeUTF(PROXY_SETTINGS_KEY);
out.writeUTF(config.proxySettings.toString());
out.writeUTF(PROXY_PAC_FILE);
@@ -159,7 +152,7 @@ public class IpConfigStore {
out.writeInt(configKey);
}
} catch (NullPointerException e) {
- loge("Failure in writing " + config.linkProperties + e);
+ loge("Failure in writing " + config + e);
}
out.writeUTF(EOS);
@@ -196,7 +189,7 @@ public class IpConfigStore {
// Default is DHCP with no proxy
IpAssignment ipAssignment = IpAssignment.DHCP;
ProxySettings proxySettings = ProxySettings.NONE;
- LinkProperties linkProperties = new LinkProperties();
+ StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
String proxyHost = null;
String pacFileUrl = null;
int proxyPort = -1;
@@ -213,13 +206,23 @@ public class IpConfigStore {
} else if (key.equals(LINK_ADDRESS_KEY)) {
LinkAddress linkAddr = new LinkAddress(
NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
- linkProperties.addLinkAddress(linkAddr);
+ if (linkAddr.getAddress() instanceof Inet4Address &&
+ staticIpConfiguration.ipAddress == null) {
+ staticIpConfiguration.ipAddress = linkAddr;
+ } else {
+ loge("Non-IPv4 or duplicate address: " + linkAddr);
+ }
} else if (key.equals(GATEWAY_KEY)) {
LinkAddress dest = null;
InetAddress gateway = null;
if (version == 1) {
// only supported default gateways - leave the dest/prefix empty
gateway = NetworkUtils.numericToInetAddress(in.readUTF());
+ if (staticIpConfiguration.gateway == null) {
+ staticIpConfiguration.gateway = gateway;
+ } else {
+ loge("Duplicate gateway: " + gateway.getHostAddress());
+ }
} else {
if (in.readInt() == 1) {
dest = new LinkAddress(
@@ -229,10 +232,16 @@ public class IpConfigStore {
if (in.readInt() == 1) {
gateway = NetworkUtils.numericToInetAddress(in.readUTF());
}
+ RouteInfo route = new RouteInfo(dest, gateway);
+ if (route.isIPv4Default() &&
+ staticIpConfiguration.gateway == null) {
+ staticIpConfiguration.gateway = gateway;
+ } else {
+ loge("Non-IPv4 default or duplicate route: " + route);
+ }
}
- linkProperties.addRoute(new RouteInfo(dest, gateway));
} else if (key.equals(DNS_KEY)) {
- linkProperties.addDnsServer(
+ staticIpConfiguration.dnsServers.add(
NetworkUtils.numericToInetAddress(in.readUTF()));
} else if (key.equals(PROXY_SETTINGS_KEY)) {
proxySettings = ProxySettings.valueOf(in.readUTF());
@@ -258,9 +267,11 @@ public class IpConfigStore {
IpConfiguration config = new IpConfiguration();
networks.put(id, config);
- config.linkProperties = linkProperties;
switch (ipAssignment) {
case STATIC:
+ config.staticIpConfiguration = staticIpConfiguration;
+ config.ipAssignment = ipAssignment;
+ break;
case DHCP:
config.ipAssignment = ipAssignment;
break;
@@ -276,16 +287,15 @@ public class IpConfigStore {
switch (proxySettings) {
case STATIC:
- config.proxySettings = proxySettings;
- ProxyInfo ProxyInfo =
+ ProxyInfo proxyInfo =
new ProxyInfo(proxyHost, proxyPort, exclusionList);
- linkProperties.setHttpProxy(ProxyInfo);
+ config.proxySettings = proxySettings;
+ config.httpProxy = proxyInfo;
break;
case PAC:
+ ProxyInfo proxyPacProperties = new ProxyInfo(pacFileUrl);
config.proxySettings = proxySettings;
- ProxyInfo proxyPacProperties =
- new ProxyInfo(pacFileUrl);
- linkProperties.setHttpProxy(proxyPacProperties);
+ config.httpProxy = proxyPacProperties;
break;
case NONE:
config.proxySettings = proxySettings;
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index a2e9d67..04df3e7 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -205,7 +205,6 @@ public class LockdownVpnTracker {
private void initLocked() {
Slog.d(TAG, "initLocked()");
- mVpn.setEnableNotifications(false);
mVpn.setEnableTeardown(false);
final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
@@ -249,7 +248,6 @@ public class LockdownVpnTracker {
hideNotification();
mContext.unregisterReceiver(mResetReceiver);
- mVpn.setEnableNotifications(true);
mVpn.setEnableTeardown(true);
}
@@ -304,16 +302,18 @@ public class LockdownVpnTracker {
}
private void showNotification(int titleRes, int iconRes) {
- final Notification.Builder builder = new Notification.Builder(mContext);
- builder.setWhen(0);
- builder.setSmallIcon(iconRes);
- builder.setContentTitle(mContext.getString(titleRes));
- builder.setContentText(mContext.getString(R.string.vpn_lockdown_config));
- builder.setContentIntent(mConfigIntent);
- builder.setPriority(Notification.PRIORITY_LOW);
- builder.setOngoing(true);
- builder.addAction(
- R.drawable.ic_menu_refresh, mContext.getString(R.string.reset), mResetIntent);
+ final Notification.Builder builder = new Notification.Builder(mContext)
+ .setWhen(0)
+ .setSmallIcon(iconRes)
+ .setContentTitle(mContext.getString(titleRes))
+ .setContentText(mContext.getString(R.string.vpn_lockdown_config))
+ .setContentIntent(mConfigIntent)
+ .setPriority(Notification.PRIORITY_LOW)
+ .setOngoing(true)
+ .addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset),
+ mResetIntent)
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color));
NotificationManager.from(mContext).notify(TAG, 0, builder.build());
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ad2bb92..9e5fa41 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -791,6 +791,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final Notification.Builder builder = new Notification.Builder(mContext);
builder.setOnlyAlertOnce(true);
builder.setWhen(0L);
+ builder.setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color));
final Resources res = mContext.getResources();
switch (type) {
@@ -916,6 +918,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
+ builder.setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color));
final Intent intent = buildAllowBackgroundDataIntent();
builder.setContentIntent(
@@ -1060,9 +1064,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
final String baseIface = state.linkProperties.getInterfaceName();
- connIdents.add(Pair.create(baseIface, ident));
- if (powerSave) {
- connIfaces.add(baseIface);
+ if (baseIface != null) {
+ connIdents.add(Pair.create(baseIface, ident));
+ if (powerSave) {
+ connIfaces.add(baseIface);
+ }
}
// Stacked interfaces are considered to have same identity as
@@ -1070,9 +1076,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
for (LinkProperties stackedLink : stackedLinks) {
final String stackedIface = stackedLink.getInterfaceName();
- connIdents.add(Pair.create(stackedIface, ident));
- if (powerSave) {
- connIfaces.add(stackedIface);
+ if (stackedIface != null) {
+ connIdents.add(Pair.create(stackedIface, ident));
+ if (powerSave) {
+ connIfaces.add(stackedIface);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index e35ca46..f995dee 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -896,10 +896,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// Traffic occurring on the base interface is always counted for
// both total usage and UID details.
final String baseIface = state.linkProperties.getInterfaceName();
- findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
- if (isMobile) {
- mobileIfaces.add(baseIface);
+ if (baseIface != null) {
+ findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+ if (isMobile) {
+ mobileIfaces.add(baseIface);
+ }
}
// Traffic occurring on stacked interfaces is usually clatd,
@@ -909,15 +911,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
for (LinkProperties stackedLink : stackedLinks) {
final String stackedIface = stackedLink.getInterfaceName();
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
- if (isMobile) {
- mobileIfaces.add(stackedIface);
+ if (stackedIface != null) {
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+ if (isMobile) {
+ mobileIfaces.add(stackedIface);
+ }
}
}
}
}
- mobileIfaces.remove(null);
mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index a06daf6..189131c 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -51,8 +51,9 @@ public class ConditionProviders extends ManagedServices {
= new ArrayMap<IBinder, IConditionListener>();
private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
+ private final DowntimeConditionProvider mDowntime = new DowntimeConditionProvider();
- private Uri mExitConditionId;
+ private Condition mExitCondition;
private ComponentName mExitConditionComponent;
public ConditionProviders(Context context, Handler handler,
@@ -97,6 +98,7 @@ public class ConditionProviders extends ManagedServices {
}
}
mCountdown.dump(pw, filter);
+ mDowntime.dump(pw, filter);
}
@Override
@@ -110,6 +112,10 @@ public class ConditionProviders extends ManagedServices {
mCountdown.attachBase(mContext);
registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
+ mDowntime.attachBase(mContext);
+ registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT,
+ UserHandle.USER_OWNER);
+ mDowntime.setCallback(new DowntimeCallback());
}
@Override
@@ -125,7 +131,7 @@ public class ConditionProviders extends ManagedServices {
if (info.component.equals(mExitConditionComponent)) {
// ensure record exists, we'll wire it up and subscribe below
final ConditionRecord manualRecord =
- getRecordLocked(mExitConditionId, mExitConditionComponent);
+ getRecordLocked(mExitCondition.id, mExitConditionComponent);
manualRecord.isManual = true;
}
final int N = mRecords.size();
@@ -149,11 +155,11 @@ public class ConditionProviders extends ManagedServices {
if (!r.component.equals(removed.component)) continue;
if (r.isManual) {
// removing the current manual condition, exit zen
- mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "manualServiceRemoved");
}
if (r.isAutomatic) {
// removing an automatic condition, exit zen
- mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "automaticServiceRemoved");
}
mRecords.remove(i);
}
@@ -249,7 +255,8 @@ public class ConditionProviders extends ManagedServices {
} else if (DEBUG) {
Slog.d(TAG, "Exit zen: manual condition false: " + c);
}
- mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+ mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
+ "manualConditionExit");
unsubscribeLocked(r);
r.isManual = false;
}
@@ -263,33 +270,46 @@ public class ConditionProviders extends ManagedServices {
} else if (DEBUG) {
Slog.d(TAG, "Exit zen: automatic condition false: " + c);
}
- mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+ mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
+ "automaticConditionExit");
} else if (c.state == Condition.STATE_TRUE) {
Slog.d(TAG, "Enter zen: automatic condition true: " + c);
- mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ "automaticConditionEnter");
}
}
}
}
}
- public void setZenModeCondition(Uri conditionId, String reason) {
- if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
+ public void setZenModeCondition(Condition condition, String reason) {
+ if (DEBUG) Slog.d(TAG, "setZenModeCondition " + condition);
synchronized(mMutex) {
ComponentName conditionComponent = null;
- if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
- // constructed by the client, make sure the record exists...
- final ConditionRecord r = getRecordLocked(conditionId,
- CountdownConditionProvider.COMPONENT);
- if (r.info == null) {
- // ... and is associated with the in-process service
- r.info = checkServiceTokenLocked(mCountdown.asInterface());
+ if (condition != null) {
+ if (ZenModeConfig.isValidCountdownConditionId(condition.id)) {
+ // constructed by the client, make sure the record exists...
+ final ConditionRecord r = getRecordLocked(condition.id,
+ CountdownConditionProvider.COMPONENT);
+ if (r.info == null) {
+ // ... and is associated with the in-process service
+ r.info = checkServiceTokenLocked(mCountdown.asInterface());
+ }
+ }
+ if (ZenModeConfig.isValidDowntimeConditionId(condition.id)) {
+ // constructed by the client, make sure the record exists...
+ final ConditionRecord r = getRecordLocked(condition.id,
+ DowntimeConditionProvider.COMPONENT);
+ if (r.info == null) {
+ // ... and is associated with the in-process service
+ r.info = checkServiceTokenLocked(mDowntime.asInterface());
+ }
}
}
final int N = mRecords.size();
for (int i = 0; i < N; i++) {
final ConditionRecord r = mRecords.get(i);
- final boolean idEqual = r.id.equals(conditionId);
+ final boolean idEqual = condition != null && r.id.equals(condition.id);
if (r.isManual && !idEqual) {
// was previous manual condition, unsubscribe
unsubscribeLocked(r);
@@ -303,10 +323,10 @@ public class ConditionProviders extends ManagedServices {
conditionComponent = r.component;
}
}
- if (!Objects.equals(mExitConditionId, conditionId)) {
- mExitConditionId = conditionId;
+ if (!Objects.equals(mExitCondition, condition)) {
+ mExitCondition = condition;
mExitConditionComponent = conditionComponent;
- ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, reason);
+ ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, reason);
saveZenConfigLocked();
}
}
@@ -318,6 +338,7 @@ public class ConditionProviders extends ManagedServices {
RemoteException re = null;
if (provider != null) {
try {
+ Slog.d(TAG, "Subscribing to " + r.id + " with " + provider);
provider.onSubscribe(r.id);
} catch (RemoteException e) {
Slog.w(TAG, "Error subscribing to " + r, e);
@@ -436,12 +457,13 @@ public class ConditionProviders extends ManagedServices {
return;
}
synchronized (mMutex) {
- final boolean changingExit = !Objects.equals(mExitConditionId, config.exitConditionId);
- mExitConditionId = config.exitConditionId;
+ final boolean changingExit = !Objects.equals(mExitCondition, config.exitCondition);
+ mExitCondition = config.exitCondition;
mExitConditionComponent = config.exitConditionComponent;
if (changingExit) {
- ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, "config");
+ ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, "config");
}
+ mDowntime.setConfig(config);
if (config.conditionComponents == null || config.conditionIds == null
|| config.conditionComponents.length != config.conditionIds.length) {
if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
@@ -488,7 +510,7 @@ public class ConditionProviders extends ManagedServices {
config.conditionIds[i] = r.id;
}
}
- config.exitConditionId = mExitConditionId;
+ config.exitCondition = mExitCondition;
config.exitConditionComponent = mExitConditionComponent;
if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
mZenModeHelper.setConfig(config);
@@ -510,6 +532,26 @@ public class ConditionProviders extends ManagedServices {
}
}
+ private class DowntimeCallback implements DowntimeConditionProvider.Callback {
+ @Override
+ public void onDowntimeChanged(boolean inDowntime) {
+ final int mode = mZenModeHelper.getZenMode();
+ final ZenModeConfig config = mZenModeHelper.getConfig();
+ // enter downtime
+ if (inDowntime && mode == Global.ZEN_MODE_OFF && config != null) {
+ final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(),
+ Condition.STATE_TRUE);
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, "downtimeEnter");
+ setZenModeCondition(condition, "downtime");
+ }
+ // exit downtime
+ if (!inDowntime && mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ && mDowntime.isDowntimeCondition(mExitCondition)) {
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
+ }
+ }
+ }
+
private static class ConditionRecord {
public final Uri id;
public final ComponentName component;
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index aaf7cfc..37aacaa 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -29,6 +29,7 @@ import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
+import android.util.Log;
import android.util.Slog;
import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -38,8 +39,8 @@ import java.util.Date;
/** Built-in zen condition provider for simple time-based conditions */
public class CountdownConditionProvider extends ConditionProviderService {
- private static final String TAG = "CountdownConditionProvider";
- private static final boolean DEBUG = false;
+ private static final String TAG = "CountdownConditions";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final ComponentName COMPONENT =
new ComponentName("android", CountdownConditionProvider.class.getName());
diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
new file mode 100644
index 0000000..317ebef
--- /dev/null
+++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
@@ -0,0 +1,289 @@
+/**
+ * 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.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.IConditionProvider;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.DowntimeInfo;
+import android.text.format.DateFormat;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.server.notification.NotificationManagerService.DumpFilter;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Objects;
+
+/** Built-in zen condition provider for managing downtime */
+public class DowntimeConditionProvider extends ConditionProviderService {
+ private static final String TAG = "DowntimeConditions";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final ComponentName COMPONENT =
+ new ComponentName("android", DowntimeConditionProvider.class.getName());
+
+ private static final String ENTER_ACTION = TAG + ".enter";
+ private static final int ENTER_CODE = 100;
+ private static final String EXIT_ACTION = TAG + ".exit";
+ private static final int EXIT_CODE = 101;
+ private static final String EXTRA_TIME = "time";
+
+ private final Calendar mCalendar = Calendar.getInstance();
+ private final Context mContext = this;
+ private final ArraySet<Integer> mDays = new ArraySet<Integer>();
+
+ private boolean mConnected;
+ private boolean mInDowntime;
+ private ZenModeConfig mConfig;
+ private Callback mCallback;
+
+ public DowntimeConditionProvider() {
+ if (DEBUG) Slog.d(TAG, "new DowntimeConditionProvider()");
+ }
+
+ public void dump(PrintWriter pw, DumpFilter filter) {
+ pw.println(" DowntimeConditionProvider:");
+ pw.print(" mConnected="); pw.println(mConnected);
+ pw.print(" mInDowntime="); pw.println(mInDowntime);
+ }
+
+ public void attachBase(Context base) {
+ attachBaseContext(base);
+ }
+
+ public IConditionProvider asInterface() {
+ return (IConditionProvider) onBind(null);
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onConnected() {
+ if (DEBUG) Slog.d(TAG, "onConnected");
+ mConnected = true;
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ENTER_ACTION);
+ filter.addAction(EXIT_ACTION);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+ init();
+ }
+
+ @Override
+ public void onDestroy() {
+ if (DEBUG) Slog.d(TAG, "onDestroy");
+ mConnected = false;
+ }
+
+ @Override
+ public void onRequestConditions(int relevance) {
+ if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
+ if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) {
+ if (mInDowntime && mConfig != null) {
+ notifyCondition(createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE));
+ }
+ }
+ }
+
+ @Override
+ public void onSubscribe(Uri conditionId) {
+ if (DEBUG) Slog.d(TAG, "onSubscribe conditionId=" + conditionId);
+ final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId);
+ if (downtime != null && mConfig != null) {
+ final int state = mConfig.toDowntimeInfo().equals(downtime) && mInDowntime
+ ? Condition.STATE_TRUE : Condition.STATE_FALSE;
+ if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state));
+ notifyCondition(createCondition(downtime, state));
+ }
+ }
+
+ @Override
+ public void onUnsubscribe(Uri conditionId) {
+ if (DEBUG) Slog.d(TAG, "onUnsubscribe conditionId=" + conditionId);
+ }
+
+ public void setConfig(ZenModeConfig config) {
+ if (Objects.equals(mConfig, config)) return;
+ if (DEBUG) Slog.d(TAG, "setConfig");
+ mConfig = config;
+ if (mConnected) {
+ init();
+ }
+ }
+
+ public boolean isInDowntime() {
+ return mInDowntime;
+ }
+
+ public Condition createCondition(DowntimeInfo downtime, int state) {
+ if (downtime == null) return null;
+ final Uri id = ZenModeConfig.toDowntimeConditionId(downtime);
+ final String skeleton = DateFormat.is24HourFormat(mContext) ? "Hm" : "hma";
+ final Locale locale = Locale.getDefault();
+ final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton);
+ final long time = getTime(System.currentTimeMillis(), downtime.endHour, downtime.endMinute);
+ final String formatted = new SimpleDateFormat(pattern, locale).format(new Date(time));
+ final String summary = mContext.getString(R.string.downtime_condition_summary, formatted);
+ return new Condition(id, summary, "", "", 0, state, Condition.FLAG_RELEVANT_NOW);
+ }
+
+ public boolean isDowntimeCondition(Condition condition) {
+ return condition != null && ZenModeConfig.isValidDowntimeConditionId(condition.id);
+ }
+
+ private void init() {
+ updateDays();
+ reevaluateDowntime();
+ updateAlarms();
+ }
+
+ private void updateDays() {
+ mDays.clear();
+ if (mConfig != null) {
+ final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
+ for (int i = 0; days != null && i < days.length; i++) {
+ mDays.add(days[i]);
+ }
+ }
+ }
+
+ private boolean isInDowntime(long time) {
+ if (mConfig == null || mDays.size() == 0) return false;
+ final long start = getTime(time, mConfig.sleepStartHour, mConfig.sleepStartMinute);
+ long end = getTime(time, mConfig.sleepEndHour, mConfig.sleepEndMinute);
+ if (start == end) return false;
+ if (end < start) {
+ end = addDays(end, 1);
+ }
+ return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end);
+ }
+
+ private boolean isInDowntime(int daysOffset, long time, long start, long end) {
+ final int day = ((getDayOfWeek(time) + daysOffset - 1) % Calendar.SATURDAY) + 1;
+ start = addDays(start, daysOffset);
+ end = addDays(end, daysOffset);
+ return mDays.contains(day) && time >= start && time < end;
+ }
+
+ private void reevaluateDowntime() {
+ final boolean inDowntime = isInDowntime(System.currentTimeMillis());
+ if (DEBUG) Slog.d(TAG, "inDowntime=" + inDowntime);
+ if (inDowntime == mInDowntime) return;
+ Slog.i(TAG, (inDowntime ? "Entering" : "Exiting" ) + " downtime");
+ mInDowntime = inDowntime;
+ ZenLog.traceDowntime(mInDowntime, getDayOfWeek(System.currentTimeMillis()), mDays);
+ fireDowntimeChanged();
+ }
+
+ private void fireDowntimeChanged() {
+ if (mCallback != null) {
+ mCallback.onDowntimeChanged(mInDowntime);
+ }
+ }
+
+ private void updateAlarms() {
+ if (mConfig == null) return;
+ updateAlarm(ENTER_ACTION, ENTER_CODE, mConfig.sleepStartHour, mConfig.sleepStartMinute);
+ updateAlarm(EXIT_ACTION, EXIT_CODE, mConfig.sleepEndHour, mConfig.sleepEndMinute);
+ }
+
+ private int getDayOfWeek(long time) {
+ mCalendar.setTimeInMillis(time);
+ return mCalendar.get(Calendar.DAY_OF_WEEK);
+ }
+
+ private long getTime(long millis, int hour, int min) {
+ mCalendar.setTimeInMillis(millis);
+ mCalendar.set(Calendar.HOUR_OF_DAY, hour);
+ mCalendar.set(Calendar.MINUTE, min);
+ mCalendar.set(Calendar.SECOND, 0);
+ mCalendar.set(Calendar.MILLISECOND, 0);
+ return mCalendar.getTimeInMillis();
+ }
+
+ private long addDays(long time, int days) {
+ mCalendar.setTimeInMillis(time);
+ mCalendar.add(Calendar.DATE, days);
+ return mCalendar.getTimeInMillis();
+ }
+
+ private void updateAlarm(String action, int requestCode, int hr, int min) {
+ final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final long now = System.currentTimeMillis();
+ mCalendar.setTimeInMillis(now);
+ mCalendar.set(Calendar.HOUR_OF_DAY, hr);
+ mCalendar.set(Calendar.MINUTE, min);
+ mCalendar.set(Calendar.SECOND, 0);
+ mCalendar.set(Calendar.MILLISECOND, 0);
+ long time = mCalendar.getTimeInMillis();
+ if (time <= now) {
+ time = addDays(time, 1);
+ }
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
+ new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
+ alarms.cancel(pendingIntent);
+ if (mConfig.sleepMode != null) {
+ if (DEBUG) Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
+ action, ts(time), time - now, ts(now)));
+ alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+ }
+ }
+
+ private static String ts(long time) {
+ return new Date(time) + " (" + time + ")";
+ }
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final long now = System.currentTimeMillis();
+ if (ENTER_ACTION.equals(action) || EXIT_ACTION.equals(action)) {
+ 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 (DEBUG) Slog.d(TAG, action + " fired at " + now);
+ }
+ reevaluateDowntime();
+ updateAlarms();
+ }
+ };
+
+ public interface Callback {
+ void onDowntimeChanged(boolean inDowntime);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 36be21f..f647037 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -41,6 +41,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -64,7 +65,7 @@ import java.util.Set;
*/
abstract public class ManagedServices {
protected final String TAG = getClass().getSimpleName();
- protected static final boolean DEBUG = true;
+ protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String ENABLED_SERVICES_SEPARATOR = ":";
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 1b59f52..97f0a1e 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -16,8 +16,6 @@
package com.android.server.notification;
-import android.os.IBinder;
-
public interface NotificationDelegate {
void onSetDisabled(int status);
void onClearAll(int callingUid, int callingPid, int userId);
@@ -29,7 +27,6 @@ public interface NotificationDelegate {
int uid, int initialPid, String message, int userId);
void onPanelRevealed();
void onPanelHidden();
- boolean allowDisable(int what, IBinder token, String pkg);
void onNotificationVisibilityChanged(
String[] newlyVisibleKeys, String[] noLongerVisibleKeys);
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d6afe68..45bd812 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -29,7 +29,6 @@ import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
import android.app.Notification;
-import android.app.Notification.Builder;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
@@ -50,7 +49,6 @@ import android.media.AudioManager;
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
-import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -585,11 +583,6 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public boolean allowDisable(int what, IBinder token, String pkg) {
- return mZenModeHelper.allowDisable(what, token, pkg);
- }
-
- @Override
public void onNotificationVisibilityChanged(
String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
// Using ';' as separator since eventlogs uses ',' to separate
@@ -1351,11 +1344,11 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void setZenModeCondition(Uri conditionId) {
+ public void setZenModeCondition(Condition condition) {
enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
final long identity = Binder.clearCallingIdentity();
try {
- mConditionProviders.setZenModeCondition(conditionId, "binderCall");
+ mConditionProviders.setZenModeCondition(condition, "binderCall");
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1535,14 +1528,15 @@ public class NotificationManagerService extends SystemService {
}
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
+ final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
final int userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
final UserHandle user = new UserHandle(userId);
// Limit the number of notifications that any given package except the android
- // package can enqueue. Prevents DOS attacks and deals with leaks.
- if (!isSystemNotification) {
+ // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
+ if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationList) {
int count = 0;
final int N = mNotificationList.size();
@@ -2727,6 +2721,21 @@ public class NotificationManagerService extends SystemService {
Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
}
}
+
+ private boolean isListenerPackage(String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+ // TODO: clean up locking object later
+ synchronized (mNotificationList) {
+ for (final ManagedServiceInfo serviceInfo : mServices) {
+ if (packageName.equals(serviceInfo.component.getPackageName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
public static final class DumpFilter {
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index b22ed2d..f84409e 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -22,16 +22,15 @@ import android.net.Uri;
import android.os.Build;
import android.os.RemoteException;
import android.provider.Settings.Global;
+import android.service.notification.Condition;
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
+import android.util.ArraySet;
import android.util.Slog;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
-import java.util.Arrays;
import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
public class ZenLog {
private static final String TAG = "ZenLog";
@@ -43,22 +42,19 @@ public class ZenLog {
private static final String[] MSGS = new String[SIZE];
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- private static final Set<String> SYSTEM_PACKAGES = new HashSet<String>(Arrays.asList(
- "android",
- "com.android.systemui"
- ));
private static final int TYPE_INTERCEPTED = 1;
private static final int TYPE_ALLOW_DISABLE = 2;
private static final int TYPE_SET_RINGER_MODE = 3;
private static final int TYPE_DOWNTIME = 4;
- private static final int TYPE_ZEN_MODE = 5;
- private static final int TYPE_EXIT_CONDITION = 6;
- private static final int TYPE_SUBSCRIBE = 7;
- private static final int TYPE_UNSUBSCRIBE = 8;
- private static final int TYPE_CONFIG = 9;
- private static final int TYPE_FOLLOW_RINGER_MODE = 10;
- private static final int TYPE_NOT_INTERCEPTED = 11;
+ private static final int TYPE_SET_ZEN_MODE = 5;
+ private static final int TYPE_UPDATE_ZEN_MODE = 6;
+ private static final int TYPE_EXIT_CONDITION = 7;
+ private static final int TYPE_SUBSCRIBE = 8;
+ private static final int TYPE_UNSUBSCRIBE = 9;
+ private static final int TYPE_CONFIG = 10;
+ private static final int TYPE_FOLLOW_RINGER_MODE = 11;
+ private static final int TYPE_NOT_INTERCEPTED = 12;
private static int sNext;
private static int sSize;
@@ -73,26 +69,24 @@ public class ZenLog {
append(TYPE_NOT_INTERCEPTED, record.getKey() + "," + reason);
}
- public static void traceAllowDisable(String pkg, boolean allowDisable, String reason) {
- if (SYSTEM_PACKAGES.contains(pkg)) return;
- append(TYPE_ALLOW_DISABLE, allowDisable + "," + pkg + "," + reason);
- }
-
public static void traceSetRingerMode(int ringerMode) {
append(TYPE_SET_RINGER_MODE, ringerModeToString(ringerMode));
}
- public static void traceDowntime(boolean enter, int day, int[] days) {
- append(TYPE_DOWNTIME, enter + ",day=" + day + ",days=" + (days != null ? Arrays.asList(days)
- : null));
+ public static void traceDowntime(boolean inDowntime, int day, ArraySet<Integer> days) {
+ append(TYPE_DOWNTIME, inDowntime + ",day=" + day + ",days=" + days);
+ }
+
+ public static void traceSetZenMode(int mode, String reason) {
+ append(TYPE_SET_ZEN_MODE, zenModeToString(mode) + "," + reason);
}
public static void traceUpdateZenMode(int fromMode, int toMode) {
- append(TYPE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode));
+ append(TYPE_UPDATE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode));
}
- public static void traceExitCondition(Uri id, ComponentName component, String reason) {
- append(TYPE_EXIT_CONDITION, id + "," + componentToString(component) + "," + reason);
+ public static void traceExitCondition(Condition c, ComponentName component, String reason) {
+ append(TYPE_EXIT_CONDITION, c + "," + componentToString(component) + "," + reason);
}
public static void traceSubscribe(Uri uri, IConditionProvider provider, RemoteException e) {
@@ -122,7 +116,8 @@ public class ZenLog {
case TYPE_ALLOW_DISABLE: return "allow_disable";
case TYPE_SET_RINGER_MODE: return "set_ringer_mode";
case TYPE_DOWNTIME: return "downtime";
- case TYPE_ZEN_MODE: return "zen_mode";
+ case TYPE_SET_ZEN_MODE: return "set_zen_mode";
+ case TYPE_UPDATE_ZEN_MODE: return "update_zen_mode";
case TYPE_EXIT_CONDITION: return "exit_condition";
case TYPE_SUBSCRIBE: return "subscribe";
case TYPE_UNSUBSCRIBE: return "unsubscribe";
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9282283..0b93690 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -20,10 +20,8 @@ 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.AlarmManager;
import android.app.AppOpsManager;
import android.app.Notification;
-import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -37,13 +35,13 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
-import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
import android.telecomm.TelecommManager;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.R;
@@ -57,8 +55,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
import java.util.Objects;
/**
@@ -66,12 +62,7 @@ import java.util.Objects;
*/
public class ZenModeHelper {
private static final String TAG = "ZenModeHelper";
-
- private static final String ACTION_ENTER_ZEN = "enter_zen";
- private static final int REQUEST_CODE_ENTER = 100;
- private static final String ACTION_EXIT_ZEN = "exit_zen";
- private static final int REQUEST_CODE_EXIT = 101;
- private static final String EXTRA_TIME = "time";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
private final Handler mHandler;
@@ -96,10 +87,8 @@ public class ZenModeHelper {
mSettingsObserver.observe();
final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_ENTER_ZEN);
- filter.addAction(ACTION_EXIT_ZEN);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- mContext.registerReceiver(new ZenBroadcastReceiver(), filter);
+ mContext.registerReceiver(mReceiver, filter);
}
public static ZenModeConfig readDefaultConfig(Resources resources) {
@@ -156,7 +145,7 @@ public class ZenModeHelper {
public void requestFromListener(int hints) {
final int newZen = zenFromListenerHint(hints, -1);
if (newZen != -1) {
- setZenMode(newZen);
+ setZenMode(newZen, "listener");
}
}
@@ -179,24 +168,19 @@ public class ZenModeHelper {
return false;
}
}
- // audience has veto power over all following rules
- if (!audienceMatches(record)) {
- ZenLog.traceIntercepted(record, "!audienceMatches");
- return true;
- }
if (isCall(record)) {
if (!mConfig.allowCalls) {
ZenLog.traceIntercepted(record, "!allowCalls");
return true;
}
- return false;
+ return shouldInterceptAudience(record);
}
if (isMessage(record)) {
if (!mConfig.allowMessages) {
ZenLog.traceIntercepted(record, "!allowMessages");
return true;
}
- return false;
+ return shouldInterceptAudience(record);
}
ZenLog.traceIntercepted(record, "!allowed");
return true;
@@ -204,11 +188,20 @@ public class ZenModeHelper {
return false;
}
+ private boolean shouldInterceptAudience(NotificationRecord record) {
+ if (!audienceMatches(record)) {
+ ZenLog.traceIntercepted(record, "!audienceMatches");
+ return true;
+ }
+ return false;
+ }
+
public int getZenMode() {
return mZenMode;
}
- public void setZenMode(int zenModeValue) {
+ public void setZenMode(int zenModeValue, String reason) {
+ ZenLog.traceSetZenMode(zenModeValue, reason);
Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
}
@@ -216,9 +209,6 @@ public class ZenModeHelper {
final int mode = Global.getInt(mContext.getContentResolver(),
Global.ZEN_MODE, Global.ZEN_MODE_OFF);
if (mode != mZenMode) {
- Slog.d(TAG, String.format("updateZenMode: %s -> %s",
- Global.zenModeToString(mZenMode),
- Global.zenModeToString(mode)));
ZenLog.traceUpdateZenMode(mZenMode, mode);
}
mZenMode = mode;
@@ -255,12 +245,12 @@ public class ZenModeHelper {
if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
mPreviousRingerMode = ringerMode;
- Slog.d(TAG, "Silencing ringer");
+ if (DEBUG) Slog.d(TAG, "Silencing ringer");
forcedRingerMode = AudioManager.RINGER_MODE_SILENT;
}
} else {
if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
- Slog.d(TAG, "Unsilencing ringer");
+ if (DEBUG) Slog.d(TAG, "Unsilencing ringer");
forcedRingerMode = mPreviousRingerMode != -1 ? mPreviousRingerMode
: AudioManager.RINGER_MODE_NORMAL;
mPreviousRingerMode = -1;
@@ -274,18 +264,6 @@ public class ZenModeHelper {
dispatchOnZenModeChanged();
}
- public boolean allowDisable(int what, IBinder token, String pkg) {
- // TODO(cwren): delete this API before the next release. Bug:15344099
- boolean allowDisable = true;
- String reason = null;
- if (isDefaultPhoneApp(pkg)) {
- allowDisable = mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
- reason = mZenMode == Global.ZEN_MODE_OFF ? "zenOff" : "allowCalls";
- }
- ZenLog.traceAllowDisable(pkg, allowDisable, reason);
- return allowDisable;
- }
-
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mZenMode=");
pw.println(Global.zenModeToString(mZenMode));
@@ -318,7 +296,6 @@ public class ZenModeHelper {
dispatchOnConfigChanged();
final String val = Integer.toString(mConfig.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- updateAlarms();
updateZenMode();
return true;
}
@@ -339,7 +316,7 @@ public class ZenModeHelper {
}
if (newZen != -1) {
ZenLog.traceFollowRingerMode(ringerMode, mZenMode, newZen);
- setZenMode(newZen);
+ setZenMode(newZen, "ringerMode");
}
}
}
@@ -377,7 +354,7 @@ public class ZenModeHelper {
final TelecommManager telecomm =
(TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null;
- Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
+ if (DEBUG) Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
}
return pkg != null && mDefaultPhoneApp != null
&& pkg.equals(mDefaultPhoneApp.getPackageName());
@@ -409,40 +386,6 @@ public class ZenModeHelper {
}
}
- private void updateAlarms() {
- updateAlarm(ACTION_ENTER_ZEN, REQUEST_CODE_ENTER,
- mConfig.sleepStartHour, mConfig.sleepStartMinute);
- updateAlarm(ACTION_EXIT_ZEN, REQUEST_CODE_EXIT,
- mConfig.sleepEndHour, mConfig.sleepEndMinute);
- }
-
- private void updateAlarm(String action, int requestCode, int hr, int min) {
- final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- final long now = System.currentTimeMillis();
- final Calendar c = Calendar.getInstance();
- c.setTimeInMillis(now);
- c.set(Calendar.HOUR_OF_DAY, hr);
- c.set(Calendar.MINUTE, min);
- c.set(Calendar.SECOND, 0);
- c.set(Calendar.MILLISECOND, 0);
- if (c.getTimeInMillis() <= now) {
- c.add(Calendar.DATE, 1);
- }
- final long time = c.getTimeInMillis();
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
- new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
- alarms.cancel(pendingIntent);
- if (mConfig.sleepMode != null) {
- Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
- action, ts(time), time - now, ts(now)));
- alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
- }
- }
-
- private static String ts(long time) {
- return new Date(time) + " (" + time + ")";
- }
-
private final Runnable mRingerModeChanged = new Runnable() {
@Override
public void run() {
@@ -475,47 +418,12 @@ public class ZenModeHelper {
}
}
- private class ZenBroadcastReceiver extends BroadcastReceiver {
- private final Calendar mCalendar = Calendar.getInstance();
-
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (ACTION_ENTER_ZEN.equals(intent.getAction())) {
- setZenMode(intent, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- } else if (ACTION_EXIT_ZEN.equals(intent.getAction())) {
- setZenMode(intent, Global.ZEN_MODE_OFF);
- } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
- mHandler.post(mRingerModeChanged);
- }
+ mHandler.post(mRingerModeChanged);
}
-
- private void setZenMode(Intent intent, int zenModeValue) {
- final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
- final long now = System.currentTimeMillis();
- Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
- intent.getAction(), ts(schTime), ts(now), now - schTime));
-
- final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
- boolean enter = false;
- final int day = getDayOfWeek(schTime);
- if (days != null) {
- for (int i = 0; i < days.length; i++) {
- if (days[i] == day) {
- enter = true;
- ZenModeHelper.this.setZenMode(zenModeValue);
- break;
- }
- }
- }
- ZenLog.traceDowntime(enter, day, days);
- updateAlarms();
- }
-
- private int getDayOfWeek(long time) {
- mCalendar.setTimeInMillis(time);
- return mCalendar.get(Calendar.DAY_OF_WEEK);
- }
- }
+ };
public static class Callback {
void onConfigChanged() {}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b261ef5..d1e03ec 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,33 +16,23 @@
package com.android.server.pm;
-import com.android.server.SystemService;
-
import android.content.Context;
import android.content.pm.PackageStats;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
+import android.os.Build;
import android.util.Slog;
+import dalvik.system.VMRuntime;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
+import com.android.internal.os.InstallerConnection;
+import com.android.server.SystemService;
public final class Installer extends SystemService {
private static final String TAG = "Installer";
- private static final boolean LOCAL_DEBUG = false;
-
- InputStream mIn;
- OutputStream mOut;
- LocalSocket mSocket;
-
- byte buf[] = new byte[1024];
- int buflen = 0;
+ private final InstallerConnection mInstaller;
public Installer(Context context) {
super(context);
+ mInstaller = new InstallerConnection();
}
@Override
@@ -51,154 +41,6 @@ public final class Installer extends SystemService {
ping();
}
- private boolean connect() {
- if (mSocket != null) {
- return true;
- }
- Slog.i(TAG, "connecting...");
- try {
- mSocket = new LocalSocket();
-
- LocalSocketAddress address = new LocalSocketAddress("installd",
- LocalSocketAddress.Namespace.RESERVED);
-
- mSocket.connect(address);
-
- mIn = mSocket.getInputStream();
- mOut = mSocket.getOutputStream();
- } catch (IOException ex) {
- disconnect();
- return false;
- }
- return true;
- }
-
- private void disconnect() {
- Slog.i(TAG, "disconnecting...");
- try {
- if (mSocket != null)
- mSocket.close();
- } catch (IOException ex) {
- }
- try {
- if (mIn != null)
- mIn.close();
- } catch (IOException ex) {
- }
- try {
- if (mOut != null)
- mOut.close();
- } catch (IOException ex) {
- }
- mSocket = null;
- mIn = null;
- mOut = null;
- }
-
- private boolean readBytes(byte buffer[], int len) {
- int off = 0, count;
- if (len < 0)
- return false;
- while (off != len) {
- try {
- count = mIn.read(buffer, off, len - off);
- if (count <= 0) {
- Slog.e(TAG, "read error " + count);
- break;
- }
- off += count;
- } catch (IOException ex) {
- Slog.e(TAG, "read exception");
- break;
- }
- }
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "read " + len + " bytes");
- }
- if (off == len)
- return true;
- disconnect();
- return false;
- }
-
- private boolean readReply() {
- int len;
- buflen = 0;
- if (!readBytes(buf, 2))
- return false;
- len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
- if ((len < 1) || (len > 1024)) {
- Slog.e(TAG, "invalid reply length (" + len + ")");
- disconnect();
- return false;
- }
- if (!readBytes(buf, len))
- return false;
- buflen = len;
- return true;
- }
-
- private boolean writeCommand(String _cmd) {
- byte[] cmd = _cmd.getBytes();
- int len = cmd.length;
- if ((len < 1) || (len > 1024))
- return false;
- buf[0] = (byte) (len & 0xff);
- buf[1] = (byte) ((len >> 8) & 0xff);
- try {
- mOut.write(buf, 0, 2);
- mOut.write(cmd, 0, len);
- } catch (IOException ex) {
- Slog.e(TAG, "write error");
- disconnect();
- return false;
- }
- return true;
- }
-
- private synchronized String transaction(String cmd) {
- if (!connect()) {
- Slog.e(TAG, "connection failed");
- return "-1";
- }
-
- if (!writeCommand(cmd)) {
- /*
- * If installd died and restarted in the background (unlikely but
- * possible) we'll fail on the next write (this one). Try to
- * reconnect and write the command one more time before giving up.
- */
- Slog.e(TAG, "write command failed? reconnect!");
- if (!connect() || !writeCommand(cmd)) {
- return "-1";
- }
- }
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "send: '" + cmd + "'");
- }
- if (readReply()) {
- String s = new String(buf, 0, buflen);
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "recv: '" + s + "'");
- }
- return s;
- } else {
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "fail");
- }
- return "-1";
- }
- }
-
- private int execute(String cmd) {
- String res = transaction(cmd);
- try {
- return Integer.parseInt(res);
- } catch (NumberFormatException ex) {
- return -1;
- }
- }
-
public int install(String name, int uid, int gid, String seinfo) {
StringBuilder builder = new StringBuilder("install");
builder.append(' ');
@@ -209,11 +51,16 @@ public final class Installer extends SystemService {
builder.append(gid);
builder.append(' ');
builder.append(seinfo != null ? seinfo : "!");
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName,
String instructionSet) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
StringBuilder builder = new StringBuilder("patchoat");
builder.append(' ');
builder.append(apkPath);
@@ -224,37 +71,34 @@ public final class Installer extends SystemService {
builder.append(pkgName);
builder.append(' ');
builder.append(instructionSet);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
- StringBuilder builder = new StringBuilder("patchoat");
- builder.append(' ');
- builder.append(apkPath);
- builder.append(' ');
- builder.append(uid);
- builder.append(isPublic ? " 1" : " 0");
- builder.append(" *"); // No pkgName arg present
- builder.append(' ');
- builder.append(instructionSet);
- return execute(builder.toString());
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
+ return mInstaller.patchoat(apkPath, uid, isPublic, instructionSet);
}
public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
- StringBuilder builder = new StringBuilder("dexopt");
- builder.append(' ');
- builder.append(apkPath);
- builder.append(' ');
- builder.append(uid);
- builder.append(isPublic ? " 1" : " 0");
- builder.append(" *"); // No pkgName arg present
- builder.append(' ');
- builder.append(instructionSet);
- return execute(builder.toString());
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
+ return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet);
}
public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
String instructionSet) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
StringBuilder builder = new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
@@ -265,7 +109,7 @@ public final class Installer extends SystemService {
builder.append(pkgName);
builder.append(' ');
builder.append(instructionSet);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int idmap(String targetApkPath, String overlayApkPath, int uid) {
@@ -276,10 +120,15 @@ public final class Installer extends SystemService {
builder.append(overlayApkPath);
builder.append(' ');
builder.append(uid);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int movedex(String srcPath, String dstPath, String instructionSet) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
StringBuilder builder = new StringBuilder("movedex");
builder.append(' ');
builder.append(srcPath);
@@ -287,16 +136,21 @@ public final class Installer extends SystemService {
builder.append(dstPath);
builder.append(' ');
builder.append(instructionSet);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int rmdex(String codePath, String instructionSet) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
StringBuilder builder = new StringBuilder("rmdex");
builder.append(' ');
builder.append(codePath);
builder.append(' ');
builder.append(instructionSet);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int remove(String name, int userId) {
@@ -305,7 +159,7 @@ public final class Installer extends SystemService {
builder.append(name);
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int rename(String oldname, String newname) {
@@ -314,7 +168,7 @@ public final class Installer extends SystemService {
builder.append(oldname);
builder.append(' ');
builder.append(newname);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int fixUid(String name, int uid, int gid) {
@@ -325,7 +179,7 @@ public final class Installer extends SystemService {
builder.append(uid);
builder.append(' ');
builder.append(gid);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int deleteCacheFiles(String name, int userId) {
@@ -334,7 +188,7 @@ public final class Installer extends SystemService {
builder.append(name);
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int deleteCodeCacheFiles(String name, int userId) {
@@ -343,7 +197,7 @@ public final class Installer extends SystemService {
builder.append(name);
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int createUserData(String name, int uid, int userId, String seinfo) {
@@ -356,21 +210,21 @@ public final class Installer extends SystemService {
builder.append(userId);
builder.append(' ');
builder.append(seinfo != null ? seinfo : "!");
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int createUserConfig(int userId) {
StringBuilder builder = new StringBuilder("mkuserconfig");
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int removeUserDataDirs(int userId) {
StringBuilder builder = new StringBuilder("rmuser");
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int clearUserData(String name, int userId) {
@@ -379,11 +233,11 @@ public final class Installer extends SystemService {
builder.append(name);
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public boolean ping() {
- if (execute("ping") < 0) {
+ if (mInstaller.execute("ping") < 0) {
return false;
} else {
return true;
@@ -391,18 +245,25 @@ public final class Installer extends SystemService {
}
public int pruneDexCache(String cacheSubDir) {
- return execute("prunedexcache " + cacheSubDir);
+ return mInstaller.execute("prunedexcache " + cacheSubDir);
}
public int freeCache(long freeStorageSize) {
StringBuilder builder = new StringBuilder("freecache");
builder.append(' ');
builder.append(String.valueOf(freeStorageSize));
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
+ for (String instructionSet : instructionSets) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+ }
+
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
@@ -423,7 +284,7 @@ public final class Installer extends SystemService {
// just the primary.
builder.append(instructionSets[0]);
- String s = transaction(builder.toString());
+ String s = mInstaller.transact(builder.toString());
String res[] = s.split(" ");
if ((res == null) || (res.length != 5)) {
@@ -441,7 +302,7 @@ public final class Installer extends SystemService {
}
public int moveFiles() {
- return execute("movefiles");
+ return mInstaller.execute("movefiles");
}
/**
@@ -467,7 +328,7 @@ public final class Installer extends SystemService {
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public boolean restoreconData(String pkgName, String seinfo, int uid) {
@@ -478,6 +339,23 @@ public final class Installer extends SystemService {
builder.append(seinfo != null ? seinfo : "!");
builder.append(' ');
builder.append(uid);
- return (execute(builder.toString()) == 0);
+ return (mInstaller.execute(builder.toString()) == 0);
+ }
+
+ /**
+ * Returns true iff. {@code instructionSet} is a valid instruction set.
+ */
+ private static boolean isValidInstructionSet(String instructionSet) {
+ if (instructionSet == null) {
+ return false;
+ }
+
+ for (String abi : Build.SUPPORTED_ABIS) {
+ if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
+ return true;
+ }
+ }
+
+ return false;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c7e3fb7..03cb2e9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_ALL_USERS;
import static android.content.pm.PackageManager.INSTALL_FROM_ADB;
import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static com.android.internal.util.XmlUtils.readBitmapAttribute;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -36,20 +37,26 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.PackageDeleteObserver;
+import android.app.PackageInstallObserver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.IPackageDeleteObserver2;
+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.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
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;
@@ -61,8 +68,10 @@ 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;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -70,13 +79,14 @@ import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
-import com.android.server.pm.PackageInstallerSession.Snapshot;
import com.google.android.collect.Sets;
import libcore.io.IoUtils;
@@ -103,6 +113,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
// TODO: remove outstanding sessions when installer package goes away
// TODO: notify listeners in other users when package has been installed there
+ // TODO: purge expired sessions periodically in addition to at reboot
/** XML constants used in {@link #mSessionsFile} */
private static final String TAG_SESSIONS = "sessions";
@@ -112,6 +123,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
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_SEALED = "sealed";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
@@ -134,6 +146,7 @@ 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;
@@ -160,10 +173,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
+ /** Sessions allocated to legacy users */
+ @GuardedBy("mSessions")
+ private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
+
private static final FilenameFilter sStageFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
- return name.startsWith("vmdl") && name.endsWith(".tmp");
+ return isStageName(name);
}
};
@@ -171,6 +188,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
mContext = context;
mPm = pm;
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mStorage = StorageManager.from(mContext);
mStagingDir = stagingDir;
@@ -185,13 +203,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
synchronized (mSessions) {
readSessionsLocked();
- // Clean up orphaned staging directories
- final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
+ final ArraySet<File> unclaimed = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
+
+ // Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
- stages.remove(session.sessionStageDir);
+ unclaimed.remove(session.internalStageDir);
}
- for (File stage : stages) {
+
+ // Clean up orphaned staging directories
+ for (File stage : unclaimed) {
Slog.w(TAG, "Deleting orphan stage " + stage);
if (stage.isDirectory()) {
FileUtils.deleteContents(stage);
@@ -201,22 +222,64 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- public static boolean isStageFile(File file) {
- return sStageFilter.accept(null, file.getName());
+ public void onSecureContainersAvailable() {
+ synchronized (mSessions) {
+ final ArraySet<String> unclaimed = new ArraySet<>();
+ for (String cid : PackageHelper.getSecureContainerList()) {
+ if (isStageName(cid)) {
+ unclaimed.add(cid);
+ }
+ }
+
+ // Ignore stages claimed by active sessions
+ for (int i = 0; i < mSessions.size(); i++) {
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ final String cid = session.externalStageCid;
+
+ if (unclaimed.remove(cid)) {
+ // Claimed by active session, mount it
+ PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
+ Process.SYSTEM_UID);
+ }
+ }
+
+ // Clean up orphaned staging containers
+ for (String cid : unclaimed) {
+ Slog.w(TAG, "Deleting orphan container " + cid);
+ PackageHelper.destroySdDir(cid);
+ }
+ }
+ }
+
+ public static boolean isStageName(String name) {
+ final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
+ final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
+ final boolean isLegacyContainer = name.startsWith("smdl2tmp");
+ return isFile || isContainer || isLegacyContainer;
}
@Deprecated
- public File allocateSessionDir() throws IOException {
+ public File allocateInternalStageDirLegacy() throws IOException {
synchronized (mSessions) {
try {
final int sessionId = allocateSessionIdLocked();
- return prepareSessionStageDir(sessionId);
+ mLegacySessions.put(sessionId, true);
+ return prepareInternalStageDir(sessionId);
} catch (IllegalStateException e) {
throw new IOException(e);
}
}
}
+ @Deprecated
+ public String allocateExternalStageCidLegacy() {
+ synchronized (mSessions) {
+ final int sessionId = allocateSessionIdLocked();
+ mLegacySessions.put(sessionId, true);
+ return "smdl" + sessionId + ".tmp";
+ }
+ }
+
private void readSessionsLocked() {
if (LOGD) Slog.v(TAG, "readSessionsLocked()");
@@ -241,9 +304,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
Slog.w(TAG, "Abandoning old session first created at "
+ session.createdMillis);
valid = false;
- } else if (!session.sessionStageDir.exists()) {
- Slog.w(TAG, "Abandoning session with missing stage "
- + session.sessionStageDir);
+ } else if (session.internalStageDir != null
+ && !session.internalStageDir.exists()) {
+ Slog.w(TAG, "Abandoning internal session with missing stage "
+ + session.internalStageDir);
valid = false;
} else {
valid = true;
@@ -276,11 +340,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
final int userId = readIntAttribute(in, ATTR_USER_ID);
final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
- final File sessionStageDir = new File(readStringAttribute(in, ATTR_SESSION_STAGE_DIR));
+ 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 sealed = readBooleanAttribute(in, ATTR_SEALED);
- final InstallSessionParams params = new InstallSessionParams(
- InstallSessionParams.MODE_INVALID);
+ final SessionParams params = new SessionParams(
+ SessionParams.MODE_INVALID);
params.mode = readIntAttribute(in, ATTR_MODE);
params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
@@ -292,9 +358,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
- return new PackageInstallerSession(mInternalCallback, mPm, mInstallThread.getLooper(),
- sessionId, userId, installerPackageName, params, createdMillis, sessionStageDir,
- sealed);
+ return new PackageInstallerSession(mInternalCallback, mContext, mPm,
+ mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
+ createdMillis, stageDir, stageCid, sealed);
}
private void writeSessionsLocked() {
@@ -326,8 +392,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
throws IOException {
- final InstallSessionParams params = session.params;
- final Snapshot snapshot = session.snapshot();
+ final SessionParams params = session.params;
out.startTag(null, TAG_SESSION);
@@ -336,9 +401,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
session.installerPackageName);
writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
- writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
- session.sessionStageDir.getAbsolutePath());
- writeBooleanAttribute(out, ATTR_SEALED, snapshot.sealed);
+ if (session.internalStageDir != null) {
+ writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
+ session.internalStageDir.getAbsolutePath());
+ }
+ if (session.externalStageCid != null) {
+ writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.externalStageCid);
+ }
+ writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
writeIntAttribute(out, ATTR_MODE, params.mode);
writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
@@ -366,7 +436,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
@Override
- public int createSession(InstallSessionParams params, String installerPackageName, int userId) {
+ public int createSession(SessionParams params, String installerPackageName, int userId) {
+ try {
+ return createSessionInternal(params, installerPackageName, userId);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
+ throws IOException {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession");
@@ -388,14 +467,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
params.installFlags |= INSTALL_REPLACE_EXISTING;
}
- switch (params.mode) {
- case InstallSessionParams.MODE_FULL_INSTALL:
- case InstallSessionParams.MODE_INHERIT_EXISTING:
- break;
- default:
- throw new IllegalArgumentException("Params must have valid mode set");
- }
-
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -408,13 +479,42 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- // Sanity check that install could fit
- if (params.sizeBytes > 0) {
- try {
- mPm.freeStorage(params.sizeBytes);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
+ // 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.
+ final int resolved = PackageHelper.resolveInstallLocation(mContext,
+ params.appPackageName, params.installLocation, params.sizeBytes,
+ params.installFlags);
+ if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
+ stageInternal = true;
+ } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
+ stageInternal = false;
+ } else {
+ throw new IOException("No storage with enough free space; res=" + resolved);
+ }
+
+ } else if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ // We always stage inheriting sessions on internal storage first,
+ // since we don't want to grow containers until we're sure that
+ // everything looks legit.
+ stageInternal = true;
+ checkInternalStorage(params.sizeBytes);
+
+ // If we have a good hunch we'll end up on external storage, verify
+ // free space there too.
+ final ApplicationInfo info = mPm.getApplicationInfo(params.appPackageName, 0,
+ userId);
+ if (info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ checkExternalStorage(params.sizeBytes);
+
+ throw new UnsupportedOperationException("TODO: finish fleshing out ASEC support");
}
+
+ } else {
+ throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
final int sessionId;
@@ -432,14 +532,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
"Too many historical sessions for UID " + callingUid);
}
+ final long createdMillis = System.currentTimeMillis();
sessionId = allocateSessionIdLocked();
- final long createdMillis = System.currentTimeMillis();
- final File sessionStageDir = prepareSessionStageDir(sessionId);
+ // We're staging to exactly one location
+ File stageDir = null;
+ String stageCid = null;
+ if (stageInternal) {
+ stageDir = prepareInternalStageDir(sessionId);
+ } else {
+ stageCid = prepareExternalStageCid(sessionId, params.sizeBytes);
+ }
- session = new PackageInstallerSession(mInternalCallback, mPm,
+ session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
- createdMillis, sessionStageDir, false);
+ createdMillis, stageDir, stageCid, false);
mSessions.put(sessionId, session);
}
@@ -448,6 +555,30 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
return sessionId;
}
+ 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");
+ }
+ }
+
+ 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 IPackageInstallerSession openSession(int sessionId) {
synchronized (mSessions) {
@@ -458,9 +589,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
if (!isCallingUidOwner(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
- if (session.openCount.getAndIncrement() == 0) {
- mCallbacks.notifySessionOpened(sessionId, session.userId);
- }
+ session.open();
return session;
}
}
@@ -470,7 +599,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
int sessionId;
do {
sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
- if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null) {
+ if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null
+ && !mLegacySessions.get(sessionId, false)) {
return sessionId;
}
} while (n++ < 32);
@@ -478,11 +608,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
throw new IllegalStateException("Failed to allocate session ID");
}
- private File prepareSessionStageDir(int sessionId) {
+ private File prepareInternalStageDir(int sessionId) throws IOException {
final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp");
if (file.exists()) {
- throw new IllegalStateException("Session dir already exists: " + file);
+ throw new IOException("Session dir already exists: " + file);
}
try {
@@ -490,18 +620,36 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
Os.chmod(file.getAbsolutePath(), 0755);
} catch (ErrnoException e) {
// This purposefully throws if directory already exists
- throw new IllegalStateException("Failed to prepare session dir", e);
+ throw new IOException("Failed to prepare session dir", e);
}
if (!SELinux.restorecon(file)) {
- throw new IllegalStateException("Failed to restorecon session dir");
+ throw new IOException("Failed to restorecon session dir");
}
return file;
}
+ private String prepareExternalStageCid(int sessionId, long sizeBytes) throws IOException {
+ if (sizeBytes <= 0) {
+ throw new IOException("Session must provide valid size for ASEC");
+ }
+
+ final String cid = "smdl" + sessionId + ".tmp";
+
+ // Round up to nearest MB, plus another MB for filesystem overhead
+ final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
+
+ if (PackageHelper.createSdDir(sizeMb, cid, PackageManagerService.getEncryptKey(),
+ Process.SYSTEM_UID, true) == null) {
+ throw new IOException("Failed to create ASEC");
+ }
+
+ return cid;
+ }
+
@Override
- public InstallSessionInfo getSessionInfo(int sessionId) {
+ public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
if (!isCallingUidOwner(session)) {
@@ -512,11 +660,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
@Override
- public List<InstallSessionInfo> getAllSessions(int userId) {
+ public List<SessionInfo> getAllSessions(int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions");
enforceCallerCanReadSessions();
- final List<InstallSessionInfo> result = new ArrayList<>();
+ final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
@@ -529,11 +677,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
@Override
- public List<InstallSessionInfo> getMySessions(String installerPackageName, int userId) {
+ public List<SessionInfo> getMySessions(String installerPackageName, int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getMySessions");
mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
- final List<InstallSessionInfo> result = new ArrayList<>();
+ final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
@@ -547,37 +695,26 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
@Override
- public void uninstall(String packageName, int flags, IPackageDeleteObserver2 observer,
- int userId) {
+ public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall");
+ final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
+ statusReceiver, packageName);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
- mPm.deletePackage(packageName, observer, userId, flags);
+ mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
} else {
// Take a short detour to confirm with user
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.fromParts("package", packageName, null));
- intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder());
- try {
- observer.onUserActionRequired(intent);
- } catch (RemoteException ignored) {
- }
+ intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
+ adapter.onUserActionRequired(intent);
}
}
@Override
- public void uninstallSplit(String basePackageName, String overlayName, int flags,
- IPackageDeleteObserver2 observer, int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstallSplit");
-
- // TODO: flesh out once PM has split support
- throw new UnsupportedOperationException();
- }
-
- @Override
public void setPermissionsResult(int sessionId, boolean accepted) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
@@ -636,6 +773,95 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
+ static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
+ private final Context mContext;
+ private final IntentSender mTarget;
+ private final String mPackageName;
+
+ public PackageDeleteObserverAdapter(Context context, IntentSender target,
+ String packageName) {
+ mContext = context;
+ mTarget = target;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public void onUserActionRequired(Intent intent) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_PENDING_USER_ACTION);
+ fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+ try {
+ mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+
+ @Override
+ public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageManager.deleteStatusToPublicStatus(returnCode));
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+ PackageManager.deleteStatusToString(returnCode, msg));
+ fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+ try {
+ mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+ }
+
+ static class PackageInstallObserverAdapter extends PackageInstallObserver {
+ private final Context mContext;
+ private final IntentSender mTarget;
+ private final int mSessionId;
+
+ public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId) {
+ mContext = context;
+ mTarget = target;
+ mSessionId = sessionId;
+ }
+
+ @Override
+ public void onUserActionRequired(Intent intent) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_PENDING_USER_ACTION);
+ fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+ try {
+ mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+
+ @Override
+ public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+ Bundle extras) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageManager.installStatusToPublicStatus(returnCode));
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+ PackageManager.installStatusToString(returnCode, msg));
+ fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+ if (extras != null) {
+ final String existing = extras.getString(
+ PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
+ if (!TextUtils.isEmpty(existing)) {
+ fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, existing);
+ }
+ }
+ try {
+ mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+ }
+
private static class Callbacks extends Handler {
private static final int MSG_SESSION_CREATED = 1;
private static final int MSG_SESSION_OPENED = 2;
@@ -742,6 +968,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
pw.println();
pw.decreaseIndent();
+
+ pw.println("Legacy install sessions:");
+ pw.increaseIndent();
+ pw.println(mLegacySessions.toString());
+ pw.decreaseIndent();
}
}
@@ -750,6 +981,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
}
+ public void onSessionOpened(PackageInstallerSession session) {
+ mCallbacks.notifySessionOpened(session.sessionId, session.userId);
+ }
+
public void onSessionClosed(PackageInstallerSession session) {
mCallbacks.notifySessionClosed(session.sessionId, session.userId);
}
@@ -762,5 +997,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
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
+ // after sealing.
+ writeSessionsLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a3184f0..38a2016 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -18,6 +18,7 @@ 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_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
@@ -25,13 +26,15 @@ import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
@@ -56,9 +59,11 @@ import android.util.MathUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
import libcore.io.Libcore;
@@ -76,25 +81,37 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
- // TODO: handle INSTALL_EXTERNAL, INSTALL_INTERNAL
// TODO: treat INHERIT_EXISTING as installExistingPackage()
private final PackageInstallerService.InternalCallback mCallback;
+ private final Context mContext;
private final PackageManagerService mPm;
private final Handler mHandler;
final int sessionId;
final int userId;
final String installerPackageName;
- final InstallSessionParams params;
+ final SessionParams params;
final long createdMillis;
- final File sessionStageDir;
+
+ /** Internal location where staged data is written. */
+ final File internalStageDir;
+ /** External container where staged data is written. */
+ final String externalStageCid;
+
+ /**
+ * When a {@link SessionParams#MODE_INHERIT_EXISTING} session is installed
+ * into an ASEC, this is the container where the stage is combined with the
+ * existing install.
+ */
+ // TODO: persist this cid once we start splicing
+ String combinedCid;
/** Note that UID is not persisted; it's always derived at runtime. */
final int installerUid;
- AtomicInteger openCount = new AtomicInteger();
+ private final AtomicInteger mOpenCount = new AtomicInteger();
private final Object mLock = new Object();
@@ -115,6 +132,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private int mFinalStatus;
private String mFinalMessage;
+ @GuardedBy("mLock")
+ private File mResolvedStageDir;
+
/**
* Path to the resolved base APK for this session, which may point at an APK
* inside the session (when the session defines the base), or it may point
@@ -159,10 +179,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
};
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
- PackageManagerService pm, Looper looper, int sessionId, int userId,
- String installerPackageName, InstallSessionParams params, long createdMillis,
- File sessionStageDir, boolean sealed) {
+ Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
+ String installerPackageName, SessionParams params, long createdMillis,
+ File internalStageDir, String externalStageCid, boolean sealed) {
mCallback = callback;
+ mContext = context;
mPm = pm;
mHandler = new Handler(looper, mHandlerCallback);
@@ -171,7 +192,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
this.installerPackageName = installerPackageName;
this.params = params;
this.createdMillis = createdMillis;
- this.sessionStageDir = sessionStageDir;
+ this.internalStageDir = internalStageDir;
+ this.externalStageCid = externalStageCid;
+
+ if ((internalStageDir == null) == (externalStageCid == null)) {
+ throw new IllegalArgumentException(
+ "Exactly one of internal or external stage must be set");
+ }
mSealed = sealed;
@@ -188,25 +215,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
computeProgressLocked();
}
- public InstallSessionInfo generateInfo() {
- final InstallSessionInfo info = new InstallSessionInfo();
-
- info.sessionId = sessionId;
- info.installerPackageName = installerPackageName;
- info.resolvedBaseCodePath = mResolvedBaseCodePath;
- info.progress = mProgress;
- info.sealed = mSealed;
- info.open = openCount.get() > 0;
-
- info.mode = params.mode;
- info.sizeBytes = params.sizeBytes;
- info.appPackageName = params.appPackageName;
- info.appIcon = params.appIcon;
- info.appLabel = params.appLabel;
-
+ public SessionInfo generateInfo() {
+ final SessionInfo info = new SessionInfo();
+ synchronized (mLock) {
+ info.sessionId = sessionId;
+ info.installerPackageName = installerPackageName;
+ info.resolvedBaseCodePath = mResolvedBaseCodePath;
+ info.progress = mProgress;
+ info.sealed = mSealed;
+ info.open = mOpenCount.get() > 0;
+
+ info.mode = params.mode;
+ info.sizeBytes = params.sizeBytes;
+ info.appPackageName = params.appPackageName;
+ info.appIcon = params.appIcon;
+ info.appLabel = params.appLabel;
+ }
return info;
}
+ public boolean isSealed() {
+ synchronized (mLock) {
+ return mSealed;
+ }
+ }
+
private void assertNotSealed(String cookie) {
synchronized (mLock) {
if (mSealed) {
@@ -215,6 +248,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * Resolve the actual location where staged data should be written. This
+ * might point at an ASEC mount point, which is why we delay path resolution
+ * until someone actively works with the session.
+ */
+ private File getStageDir() throws IOException {
+ synchronized (mLock) {
+ if (mResolvedStageDir == null) {
+ if (internalStageDir != null) {
+ mResolvedStageDir = internalStageDir;
+ } else {
+ final String path = PackageHelper.getSdDir(externalStageCid);
+ if (path != null) {
+ mResolvedStageDir = new File(path);
+ } else {
+ throw new IOException(
+ "Failed to resolve container path for " + externalStageCid);
+ }
+ }
+ }
+ return mResolvedStageDir;
+ }
+ }
+
@Override
public void setClientProgress(float progress) {
synchronized (mLock) {
@@ -246,9 +303,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
@Override
- public String[] list() {
- assertNotSealed("list");
- return sessionStageDir.list();
+ public String[] getNames() {
+ assertNotSealed("getNames");
+ try {
+ return getStageDir().list();
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
}
@Override
@@ -262,8 +323,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
throws IOException {
- // TODO: relay over to DCS when installing to ASEC
-
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
@@ -280,7 +339,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(sessionStageDir, name);
+ final File target = new File(getStageDir(), name);
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
O_CREAT | O_WRONLY, 0644);
@@ -326,7 +385,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(sessionStageDir, name);
+ final File target = new File(getStageDir(), name);
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
return new ParcelFileDescriptor(targetFd);
@@ -337,9 +396,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
@Override
- public void commit(IPackageInstallObserver2 observer) {
- Preconditions.checkNotNull(observer);
- mHandler.obtainMessage(MSG_COMMIT, observer).sendToTarget();
+ public void commit(IntentSender statusReceiver) {
+ Preconditions.checkNotNull(statusReceiver);
+
+ final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
+ statusReceiver, sessionId);
+ mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
private void commitLocked() throws PackageManagerException {
@@ -357,14 +419,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
mSealed = true;
- // TODO: persist disabled mutations before going forward, since
- // beyond this point we may have hardlinks to the valid install
+ // Persist the fact that we've sealed ourselves to prevent mutations
+ // of any hard links we create below.
+ mCallback.onSessionSealed(this);
+ }
+
+ final File stageDir;
+ try {
+ stageDir = getStageDir();
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+ "Failed to resolve stage dir", e);
}
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
// consistency.
- validateInstallLocked();
+ validateInstallLocked(stageDir);
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
@@ -385,8 +456,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
- if (params.mode == InstallSessionParams.MODE_INHERIT_EXISTING) {
- spliceExistingFilesIntoStage();
+ if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ // TODO: implement splicing into existing ASEC
+ spliceExistingFilesIntoStage(stageDir);
}
// TODO: surface more granular state from dexopt
@@ -396,7 +468,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
- final IPackageInstallObserver2 remoteObserver = mRemoteObserver;
final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) {
@@ -411,8 +482,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
};
- mPm.installStage(mPackageName, this.sessionStageDir, localObserver, params,
- installerPackageName, installerUid, new UserHandle(userId));
+ mPm.installStage(mPackageName, this.internalStageDir, this.externalStageCid, localObserver,
+ params, installerPackageName, installerUid, new UserHandle(userId));
}
/**
@@ -420,25 +491,32 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* consistent package name, version code, and signing certificates.
* <p>
* Renames package files in stage to match split names defined inside.
+ * <p>
+ * Note that upgrade compatibility is still performed by
+ * {@link PackageManagerService}.
*/
- private void validateInstallLocked() throws PackageManagerException {
+ private void validateInstallLocked(File stageDir) throws PackageManagerException {
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
mResolvedBaseCodePath = null;
- final File[] files = sessionStageDir.listFiles();
+ final File[] files = stageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
- final ArraySet<String> seenSplits = new ArraySet<>();
-
// Verify that all staged packages are internally consistent
+ final ArraySet<String> seenSplits = new ArraySet<>();
for (File file : files) {
+
+ // Installers can't stage directories, so it's fine to ignore
+ // entries like "lost+found".
+ if (file.isDirectory()) continue;
+
final ApkLite info;
try {
- info = PackageParser.parseApkLite(file, PackageParser.PARSE_GET_SIGNATURES);
+ info = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Failed to parse " + file + ": " + e);
@@ -473,7 +551,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Invalid filename: " + targetName);
}
- final File targetFile = new File(sessionStageDir, targetName);
+ final File targetFile = new File(stageDir, targetName);
if (!file.equals(targetFile)) {
file.renameTo(targetFile);
}
@@ -484,11 +562,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- // TODO: shift package signature verification to installer; we're
- // currently relying on PMS to do this.
- // TODO: teach about compatible upgrade keysets.
-
- if (params.mode == InstallSessionParams.MODE_FULL_INSTALL) {
+ if (params.mode == SessionParams.MODE_FULL_INSTALL) {
// Full installs must include a base package
if (!seenSplits.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -511,7 +585,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final ApkLite info;
try {
info = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
- PackageParser.PARSE_GET_SIGNATURES);
+ PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Failed to parse existing base " + app.getBaseCodePath() + ": " + e);
@@ -543,7 +617,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Application is already installed; splice existing files that haven't been
* overridden into our stage.
*/
- private void spliceExistingFilesIntoStage() throws PackageManagerException {
+ private void spliceExistingFilesIntoStage(File stageDir) throws PackageManagerException {
final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
int n = 0;
@@ -552,7 +626,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
for (File oldFile : oldFiles) {
if (!PackageParser.isApkFile(oldFile)) continue;
- final File newFile = new File(sessionStageDir, oldFile.getName());
+ final File newFile = new File(stageDir, oldFile.getName());
try {
Os.link(oldFile.getAbsolutePath(), newFile.getAbsolutePath());
n++;
@@ -581,9 +655,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ public void open() {
+ if (mOpenCount.getAndIncrement() == 0) {
+ mCallback.onSessionOpened(this);
+ }
+ }
+
@Override
public void close() {
- if (openCount.decrementAndGet() == 0) {
+ if (mOpenCount.decrementAndGet() == 0) {
mCallback.onSessionClosed(this);
}
}
@@ -614,11 +694,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mSealed = true;
mDestroyed = true;
}
- FileUtils.deleteContents(sessionStageDir);
- sessionStageDir.delete();
+ if (internalStageDir != null) {
+ FileUtils.deleteContents(internalStageDir);
+ internalStageDir.delete();
+ }
+ if (externalStageCid != null) {
+ PackageHelper.destroySdDir(externalStageCid);
+ }
}
void dump(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ dumpLocked(pw);
+ }
+ }
+
+ private void dumpLocked(IndentingPrintWriter pw) {
pw.println("Session " + sessionId + ":");
pw.increaseIndent();
@@ -626,7 +717,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("installerUid", installerUid);
pw.printPair("createdMillis", createdMillis);
- pw.printPair("sessionStageDir", sessionStageDir);
+ pw.printPair("internalStageDir", internalStageDir);
+ pw.printPair("externalStageCid", externalStageCid);
pw.println();
params.dump(pw);
@@ -643,18 +735,4 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.decreaseIndent();
}
-
- Snapshot snapshot() {
- return new Snapshot(this);
- }
-
- static class Snapshot {
- final float clientProgress;
- final boolean sealed;
-
- public Snapshot(PackageInstallerSession session) {
- clientProgress = session.mClientProgress;
- sealed = session.mSealed;
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89bd1d4..aa49b27 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -48,11 +48,6 @@ import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
-import static android.system.OsConstants.S_IRGRP;
-import static android.system.OsConstants.S_IROTH;
-import static android.system.OsConstants.S_IRWXU;
-import static android.system.OsConstants.S_IXGRP;
-import static android.system.OsConstants.S_IXOTH;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER;
import static com.android.internal.util.ArrayUtils.appendInt;
@@ -70,7 +65,6 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
@@ -107,12 +101,12 @@ import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
-import android.content.pm.InstallSessionParams;
import android.content.pm.InstrumentationInfo;
import android.content.pm.ManifestDigest;
import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageParser.ActivityIntentInfo;
@@ -140,6 +134,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
+import android.os.storage.StorageManager;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -211,6 +206,7 @@ import dalvik.system.StaleDexCacheError;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
/**
* Keep track of all those .apks everywhere.
@@ -253,10 +249,6 @@ public class PackageManagerService extends IPackageManager.Stub {
// package apks to install directory.
private static final String INSTALL_PACKAGE_SUFFIX = "-";
- // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
- // that the cpuAbiOverride must be clear.
- private static final String CLEAR_ABI_OVERRIDE = "-";
-
static final int SCAN_MONITOR = 1<<0;
static final int SCAN_NO_DEX = 1<<1;
static final int SCAN_FORCE_DEX = 1<<2;
@@ -311,8 +303,6 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
- static final String mTempContainerPrefix = "smdl2tmp";
-
private static String sPreferredInstructionSet;
final ServiceThread mHandlerThread;
@@ -1394,16 +1384,27 @@ public class PackageManagerService extends IPackageManager.Stub {
* list of process files because dexopt will have been run
* if necessary during zygote startup.
*/
- String bootClassPath = System.getProperty("java.boot.class.path");
+ final String bootClassPath = System.getenv("BOOTCLASSPATH");
+ final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
+
if (bootClassPath != null) {
- String[] paths = splitString(bootClassPath, ':');
- for (int i=0; i<paths.length; i++) {
- alreadyDexOpted.add(paths[i]);
+ String[] bootClassPathElements = splitString(bootClassPath, ':');
+ for (String element : bootClassPathElements) {
+ alreadyDexOpted.add(element);
}
} else {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
+ if (systemServerClassPath != null) {
+ String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
+ for (String element : systemServerClassPathElements) {
+ alreadyDexOpted.add(element);
+ }
+ } else {
+ Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
+ }
+
boolean didDexOptLibraryOrTool = false;
final List<String> allInstructionSets = getAllInstructionSets();
@@ -2920,7 +2921,8 @@ public class PackageManagerService extends IPackageManager.Stub {
findPreferredActivity(intent, resolvedType,
flags, query, 0, false, true, false, userId);
// Add the new activity as the last chosen for this filter
- addPreferredActivityInternal(filter, match, null, activity, false, userId);
+ addPreferredActivityInternal(filter, match, null, activity, false, userId,
+ "Setting last chosen");
}
@Override
@@ -3192,24 +3194,22 @@ 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 && packageNames.contains(intent.getPackage())) {
- return true;
- }
- // 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;
+ 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;
@@ -3282,6 +3282,7 @@ public class PackageManagerService extends IPackageManager.Stub {
intent, resolvedType, flags, userId);
if (resolveInfo != null) {
result.add(resolveInfo);
+ Collections.sort(result, mResolvePrioritySorter);
}
return result;
}
@@ -4091,21 +4092,24 @@ public class PackageManagerService extends IPackageManager.Stub {
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
- && !PackageInstallerService.isStageFile(file);
+ && !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
- // Ignore entries which are not apk's
+ // Ignore entries which are not packages
continue;
}
try {
- scanPackageLI(file, flags | PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
+ scanPackageLI(file, flags | PackageParser.PARSE_MUST_BE_APK,
+ scanMode, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
- // Don't mess around with apps in system partition.
+ // Delete invalid userdata apps
if ((flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
- // Delete the apk
- Slog.w(TAG, "Cleaning up failed install of " + file);
+ Slog.w(TAG, "Deleting invalid package at " + file);
+ if (file.isDirectory()) {
+ FileUtils.deleteContents(file);
+ }
file.delete();
}
}
@@ -5008,7 +5012,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
String cpuAbiOverride = null;
- if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
+ if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
cpuAbiOverride = null;
} else if (abiOverride != null) {
cpuAbiOverride = abiOverride;
@@ -5512,7 +5516,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// Warn if we've set an abiOverride for multi-lib packages..
// By definition, we need to copy both 32 and 64 bit libraries for
// such packages.
- if (pkg.cpuAbiOverride != null && !CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
+ if (pkg.cpuAbiOverride != null
+ && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
}
@@ -5522,7 +5527,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isAsec) {
abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
} else {
- abi32 = copyNativeLibrariesForInternalApp(handle,
+ abi32 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, useIsaSpecificSubdirs);
}
}
@@ -5534,7 +5539,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isAsec) {
abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
} else {
- abi64 = copyNativeLibrariesForInternalApp(handle,
+ abi64 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, useIsaSpecificSubdirs);
}
}
@@ -5573,8 +5578,8 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isAsec) {
copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
} else {
- copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryRoot, abiList,
- useIsaSpecificSubdirs);
+ copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+ nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
}
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
@@ -6457,58 +6462,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private static void createNativeLibrarySubdir(File path) throws IOException {
- if (!path.isDirectory()) {
- path.delete();
-
- if (!path.mkdir()) {
- throw new IOException("Cannot create " + path.getPath());
- }
-
- try {
- Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
- } catch (ErrnoException e) {
- throw new IOException("Cannot chmod native library directory "
- + path.getPath(), e);
- }
- } else if (!SELinux.restorecon(path)) {
- throw new IOException("Cannot set SELinux context for " + path.getPath());
- }
- }
-
- private static int copyNativeLibrariesForInternalApp(NativeLibraryHelper.Handle handle,
- final File nativeLibraryRoot, String[] abiList, boolean useIsaSubdir) throws IOException {
- createNativeLibrarySubdir(nativeLibraryRoot);
-
- /*
- * If this is an internal application or our nativeLibraryPath points to
- * the app-lib directory, unpack the libraries if necessary.
- */
- int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
- if (abi >= 0) {
- /*
- * If we have a matching instruction set, construct a subdir under the native
- * library root that corresponds to this instruction set.
- */
- final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
- final File subDir;
- if (useIsaSubdir) {
- final File isaSubdir = new File(nativeLibraryRoot, instructionSet);
- createNativeLibrarySubdir(isaSubdir);
- subDir = isaSubdir;
- } else {
- subDir = nativeLibraryRoot;
- }
-
- int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, subDir, abiList[abi]);
- if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
- return copyRet;
- }
- }
-
- return abi;
- }
-
private void killApplication(String pkgName, int appId, String reason) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
@@ -7829,19 +7782,19 @@ public class PackageManagerService extends IPackageManager.Stub {
verificationParams.setInstallerUid(uid);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(originFile, false, observer, filteredFlags,
+ msg.obj = new InstallParams(originFile, null, false, observer, filteredFlags,
installerPackageName, verificationParams, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
- void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer,
- InstallSessionParams params, String installerPackageName, int installerUid,
- UserHandle user) {
+ void installStage(String packageName, File stagedDir, String stagedCid,
+ IPackageInstallObserver2 observer, PackageInstaller.SessionParams params,
+ String installerPackageName, int installerUid, UserHandle user) {
final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
params.referrerUri, installerUid, null);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(stageDir, true, observer, params.installFlags,
+ msg.obj = new InstallParams(stagedDir, stagedCid, true, observer, params.installFlags,
installerPackageName, verifParams, user, params.abiOverride);
mHandler.sendMessage(msg);
}
@@ -8541,10 +8494,12 @@ public class PackageManagerService extends IPackageManager.Stub {
* file, or a cluster directory. This location may be untrusted.
*/
final File originFile;
+ final String originCid;
/**
- * Flag indicating that {@link #originFile} has already been staged,
- * meaning downstream users don't need to defensively copy the contents.
+ * Flag indicating that {@link #originFile} or {@link #originCid} has
+ * already been staged, meaning downstream users don't need to
+ * defensively copy the contents.
*/
boolean originStaged;
@@ -8557,11 +8512,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final String packageAbiOverride;
boolean multiArch;
- InstallParams(File originFile, boolean originStaged, IPackageInstallObserver2 observer,
- int flags, String installerPackageName, VerificationParams verificationParams,
- UserHandle user, String packageAbiOverride) {
+ InstallParams(File originFile, String originCid, boolean originStaged,
+ IPackageInstallObserver2 observer, int flags, String installerPackageName,
+ VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
super(user);
- this.originFile = Preconditions.checkNotNull(originFile);
+ this.originFile = originFile;
+ this.originCid = originCid;
this.originStaged = originStaged;
this.observer = observer;
this.flags = flags;
@@ -8572,9 +8528,8 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public String toString() {
- return "InstallParams{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + originFile + "}";
+ return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
+ + " file=" + originFile + " cid=" + originCid + "}";
}
public ManifestDigest getManifestDigest() {
@@ -8643,15 +8598,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return pkgLite.recommendedInstallLocation;
}
- private long getMemoryLowThreshold() {
- final DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
- if (dsm == null) {
- return 0L;
- }
- return dsm.getMemoryLowThreshold();
- }
-
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
@@ -8660,6 +8606,20 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
+
+ // If we're already staged, we've firmly committed to an install location
+ if (originStaged) {
+ if (originFile != null) {
+ flags |= PackageManager.INSTALL_INTERNAL;
+ flags &= ~PackageManager.INSTALL_EXTERNAL;
+ } else if (originCid != null) {
+ flags |= PackageManager.INSTALL_EXTERNAL;
+ flags &= ~PackageManager.INSTALL_INTERNAL;
+ } else {
+ throw new IllegalStateException("Invalid stage location");
+ }
+ }
+
final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
PackageInfoLite pkgLite = null;
@@ -8669,14 +8629,9 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
- final long lowThreshold = getMemoryLowThreshold();
- if (lowThreshold == 0L) {
- Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
- }
-
// Remote call to find out default install location
final String originPath = originFile.getAbsolutePath();
- pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags, lowThreshold,
+ pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
packageAbiOverride);
// Keep track of whether this package is a multiArch package until
// we perform a full scan of it. We need to do this because we might
@@ -8688,14 +8643,21 @@ public class PackageManagerService extends IPackageManager.Stub {
* If we have too little free space, try to free cache
* before giving up.
*/
- if (pkgLite.recommendedInstallLocation
+ if (!originStaged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- final long size = mContainerService.calculateInstalledSize(
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mContext);
+ final long lowThreshold = storage.getStorageLowBytes(
+ Environment.getDataDirectory());
+
+ final long sizeBytes = mContainerService.calculateInstalledSize(
originPath, isForwardLocked(), packageAbiOverride);
- if (mInstaller.freeCache(size + lowThreshold) >= 0) {
+
+ if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
- lowThreshold, packageAbiOverride);
+ packageAbiOverride);
}
+
/*
* The cache free must have deleted the file we
* downloaded to install.
@@ -9225,24 +9187,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- final long lowThreshold;
+ final long sizeBytes = imcs.calculateInstalledSize(originFile.getAbsolutePath(),
+ isFwdLocked(), abiOverride);
- final DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
- if (dsm == null) {
- Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
- lowThreshold = 0L;
- } else {
- if (dsm.isMemoryLow()) {
- Log.w(TAG, "Memory is reported as being too low; aborting package install");
- return false;
- }
-
- lowThreshold = dsm.getMemoryLowThreshold();
- }
-
- return imcs.checkInternalFreeStorage(originFile.getAbsolutePath(), isFwdLocked(),
- lowThreshold);
+ final StorageManager storage = StorageManager.from(mContext);
+ return (sizeBytes <= storage.getStorageBytesUntilLow(Environment.getDataDirectory()));
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
@@ -9254,7 +9203,7 @@ public class PackageManagerService extends IPackageManager.Stub {
resourceFile = originFile;
} else {
try {
- final File tempDir = mInstallerService.allocateSessionDir();
+ final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
@@ -9291,49 +9240,11 @@ public class PackageManagerService extends IPackageManager.Stub {
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
- if (multiArch) {
- // Warn if we've set an abiOverride for multi-lib packages..
- // By definition, we need to copy both 32 and 64 bit libraries for
- // such packages.
- if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
- Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
- }
-
- int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
- if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
- copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot,
- Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
- maybeThrowExceptionForMultiArchCopy("Failure copying 32 bit native libraries", copyRet);
- }
-
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot,
- Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
- maybeThrowExceptionForMultiArchCopy("Failure copying 64 bit native libraries", copyRet);
- }
- } else {
- final String cpuAbiOverride = deriveAbiOverride(this.abiOverride, null /* package setting */);
- String[] abiList = (cpuAbiOverride != null) ?
- new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
-
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
- NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
- abiList = Build.SUPPORTED_32_BIT_ABIS;
- }
-
- int copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot, abiList,
- true /* use isa specific subdirs */);
- if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
- return copyRet;
- }
- }
+ ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot,
+ abiOverride, multiArch);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- } catch (PackageManagerException pme) {
- Slog.e(TAG, "Copying native libraries failed", pme);
- ret = pme.error;
} finally {
IoUtils.closeQuietly(handle);
}
@@ -9508,8 +9419,6 @@ public class PackageManagerService extends IPackageManager.Stub {
* renaming logic.
*/
class AsecInstallArgs extends InstallArgs {
- // TODO: teach about handling cluster directories
-
static final String RES_FILE_NAME = "pkg.apk";
static final String PUBLIC_RES_FILE_NAME = "res.zip";
@@ -9532,12 +9441,17 @@ public class PackageManagerService extends IPackageManager.Stub {
super(null, false, null, (isExternal ? INSTALL_EXTERNAL : 0)
| (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
instructionSets, null, isMultiArch);
+ // Hackily pretend we're still looking at a full code path
+ if (!fullCodePath.endsWith(RES_FILE_NAME)) {
+ fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
+ }
+
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
int sidx = subStr1.lastIndexOf("/");
cid = subStr1.substring(sidx+1, eidx);
- setCachePath(subStr1);
+ setMountPath(subStr1);
}
AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked,
@@ -9546,7 +9460,7 @@ public class PackageManagerService extends IPackageManager.Stub {
| (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
instructionSets, null, isMultiArch);
this.cid = cid;
- setCachePath(PackageHelper.getSdDir(cid));
+ setMountPath(PackageHelper.getSdDir(cid));
}
/** New install from existing */
@@ -9559,12 +9473,22 @@ public class PackageManagerService extends IPackageManager.Stub {
}
void createCopyFile() {
- cid = getTempContainerId();
+ cid = mInstallerService.allocateExternalStageCidLegacy();
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- return imcs.checkExternalFreeStorage(originFile.getAbsolutePath(), isFwdLocked(),
+ final long sizeBytes = imcs.calculateInstalledSize(packagePath, isFwdLocked(),
abiOverride);
+
+ final File target;
+ if (isExternal()) {
+ target = new UserEnvironment(UserHandle.USER_OWNER).getExternalStorageDirectory();
+ } else {
+ target = Environment.getDataDirectory();
+ }
+
+ final StorageManager storage = StorageManager.from(mContext);
+ return (sizeBytes <= storage.getStorageBytesUntilLow(target));
}
private final boolean isExternal() {
@@ -9572,6 +9496,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
+ // TODO: if already staged, we only need to extract native code
if (temp) {
createCopyFile();
} else {
@@ -9582,12 +9507,12 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageHelper.destroySdDir(cid);
}
- final String newCachePath = imcs.copyPackageToContainer(
+ final String newMountPath = imcs.copyPackageToContainer(
originFile.getAbsolutePath(), cid, getEncryptKey(), isExternal(),
isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */));
- if (newCachePath != null) {
- setCachePath(newCachePath);
+ if (newMountPath != null) {
+ setMountPath(newMountPath);
return PackageManager.INSTALL_SUCCEEDED;
} else {
return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
@@ -9616,10 +9541,10 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
boolean mounted = PackageHelper.isContainerMounted(cid);
if (!mounted) {
- String newCachePath = PackageHelper.mountSdDir(cid, getEncryptKey(),
+ String newMountPath = PackageHelper.mountSdDir(cid, getEncryptKey(),
Process.SYSTEM_UID);
- if (newCachePath != null) {
- setCachePath(newCachePath);
+ if (newMountPath != null) {
+ setMountPath(newMountPath);
} else {
return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
}
@@ -9630,7 +9555,7 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
String newCacheId = getNextCodePath(oldCodePath, pkg.packageName, "/" + RES_FILE_NAME);
- String newCachePath = null;
+ String newMountPath = null;
if (PackageHelper.isContainerMounted(cid)) {
// Unmount the container
if (!PackageHelper.unMountSdDir(cid)) {
@@ -9655,46 +9580,59 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (!PackageHelper.isContainerMounted(newCacheId)) {
Slog.w(TAG, "Mounting container " + newCacheId);
- newCachePath = PackageHelper.mountSdDir(newCacheId,
+ newMountPath = PackageHelper.mountSdDir(newCacheId,
getEncryptKey(), Process.SYSTEM_UID);
} else {
- newCachePath = PackageHelper.getSdDir(newCacheId);
+ newMountPath = PackageHelper.getSdDir(newCacheId);
}
- if (newCachePath == null) {
+ if (newMountPath == null) {
Slog.w(TAG, "Failed to get cache path for " + newCacheId);
return false;
}
Log.i(TAG, "Succesfully renamed " + cid +
" to " + newCacheId +
- " at new path: " + newCachePath);
+ " at new path: " + newMountPath);
cid = newCacheId;
- setCachePath(newCachePath);
- // TODO: extend to support split APKs
- pkg.codePath = getCodePath();
- pkg.baseCodePath = getCodePath();
- pkg.splitCodePaths = null;
-
- pkg.applicationInfo.setCodePath(getCodePath());
- pkg.applicationInfo.setBaseCodePath(getCodePath());
- pkg.applicationInfo.setSplitCodePaths(null);
- pkg.applicationInfo.setResourcePath(getResourcePath());
- pkg.applicationInfo.setBaseResourcePath(getResourcePath());
- pkg.applicationInfo.setSplitResourcePaths(null);
+ final File beforeCodeFile = new File(packagePath);
+ setMountPath(newMountPath);
+ final File afterCodeFile = new File(packagePath);
+
+ // Reflect the rename in scanned details
+ pkg.codePath = afterCodeFile.getAbsolutePath();
+ pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+ pkg.baseCodePath);
+ pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+ pkg.splitCodePaths);
+
+ // Reflect the rename in app info
+ pkg.applicationInfo.setCodePath(pkg.codePath);
+ pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
+ pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
+ pkg.applicationInfo.setResourcePath(pkg.codePath);
+ pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
+ pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
return true;
}
- private void setCachePath(String newCachePath) {
- File cachePath = new File(newCachePath);
- legacyNativeLibraryDir = new File(cachePath, LIB_DIR_NAME).getPath();
- packagePath = new File(cachePath, RES_FILE_NAME).getPath();
+ private void setMountPath(String mountPath) {
+ final File mountFile = new File(mountPath);
- if (isFwdLocked()) {
- resourcePath = new File(cachePath, PUBLIC_RES_FILE_NAME).getPath();
+ final File monolithicFile = new File(mountFile, RES_FILE_NAME);
+ if (monolithicFile.exists()) {
+ packagePath = monolithicFile.getAbsolutePath();
+ if (isFwdLocked()) {
+ resourcePath = new File(mountFile, PUBLIC_RES_FILE_NAME).getAbsolutePath();
+ } else {
+ resourcePath = packagePath;
+ }
} else {
+ packagePath = mountFile.getAbsolutePath();
resourcePath = packagePath;
}
+
+ legacyNativeLibraryDir = new File(mountFile, LIB_DIR_NAME).getAbsolutePath();
}
int doPostInstall(int status, int uid) {
@@ -9733,23 +9671,43 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageHelper.destroySdDir(cid);
}
- void cleanUpResourcesLI() {
- String sourceFile = getCodePath();
- // Remove dex file
- if (instructionSets == null) {
- throw new IllegalStateException("instructionSet == null");
- }
- String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- int retCode = mInstaller.rmdex(sourceFile, dexCodeInstructionSet);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove dex file for package: "
- + " at location "
- + sourceFile.toString() + ", retcode=" + retCode);
- // we don't consider this to be a failure of the core package deletion
+ private List<String> getAllCodePaths() {
+ final File codeFile = new File(getCodePath());
+ if (codeFile != null && codeFile.exists()) {
+ try {
+ final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
+ return pkg.getAllCodePaths();
+ } catch (PackageParserException e) {
+ // Ignored; we tried our best
}
}
+ return Collections.EMPTY_LIST;
+ }
+
+ void cleanUpResourcesLI() {
+ // Enumerate all code paths before deleting
+ cleanUpResourcesLI(getAllCodePaths());
+ }
+
+ private void cleanUpResourcesLI(List<String> allCodePaths) {
cleanUp();
+
+ if (!allCodePaths.isEmpty()) {
+ if (instructionSets == null) {
+ throw new IllegalStateException("instructionSet == null");
+ }
+ String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
+ for (String codePath : allCodePaths) {
+ for (String dexCodeInstructionSet : dexCodeInstructionSets) {
+ int retCode = mInstaller.rmdex(codePath, dexCodeInstructionSet);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't remove dex file for package: "
+ + " at location " + codePath + ", retcode=" + retCode);
+ // we don't consider this to be a failure of the core package deletion
+ }
+ }
+ }
+ }
}
boolean matchContainer(String app) {
@@ -9764,16 +9722,19 @@ public class PackageManagerService extends IPackageManager.Stub {
}
boolean doPostDeleteLI(boolean delete) {
- boolean ret = false;
+ if (DEBUG_SD_INSTALL) Slog.i(TAG, "doPostDeleteLI() del=" + delete);
+ final List<String> allCodePaths = getAllCodePaths();
boolean mounted = PackageHelper.isContainerMounted(cid);
if (mounted) {
// Unmount first
- ret = PackageHelper.unMountSdDir(cid);
+ if (PackageHelper.unMountSdDir(cid)) {
+ mounted = false;
+ }
}
- if (ret && delete) {
- cleanUpResourcesLI();
+ if (!mounted && delete) {
+ cleanUpResourcesLI(allCodePaths);
}
- return ret;
+ return !mounted;
}
@Override
@@ -10960,6 +10921,7 @@ public class PackageManagerService extends IPackageManager.Stub {
outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
getAppDexInstructionSets(ps), isMultiArch(ps));
+ if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
return true;
}
@@ -11462,11 +11424,13 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public void addPreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
- addPreferredActivityInternal(filter, match, set, activity, true, userId);
+ addPreferredActivityInternal(filter, match, set, activity, true, userId,
+ "Adding preferred");
}
private void addPreferredActivityInternal(IntentFilter filter, int match,
- ComponentName[] set, ComponentName activity, boolean always, int userId) {
+ ComponentName[] set, ComponentName activity, boolean always, int userId,
+ String opname) {
// writer
int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true, "add preferred activity");
@@ -11488,10 +11452,11 @@ public class PackageManagerService extends IPackageManager.Stub {
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
- Slog.i(TAG, "Adding preferred activity " + activity + " for user " + userId + " :");
+ PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
+ Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user "
+ + userId + ":");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
- mSettings.editPreferredActivitiesLPw(userId).addFilter(
- new PreferredActivity(filter, match, set, activity, always));
+ pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
mSettings.writePackageRestrictionsLPr(userId);
}
}
@@ -11514,7 +11479,6 @@ public class PackageManagerService extends IPackageManager.Stub {
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true, "replace preferred activity");
- final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
@@ -11529,30 +11493,63 @@ public class PackageManagerService extends IPackageManager.Stub {
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
- PreferredIntentResolver pir = mSettings.mPreferredActivities.get(callingUserId);
+ PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
if (pir != null) {
- Intent intent = new Intent(filter.getAction(0)).addCategory(filter.getCategory(0));
- if (filter.countDataSchemes() == 1) {
- Uri.Builder builder = new Uri.Builder();
- builder.scheme(filter.getDataScheme(0));
- intent.setData(builder.build());
- }
- List<PreferredActivity> matches = pir.queryIntent(
- intent, null, true, callingUserId);
- if (DEBUG_PREFERRED) {
- Slog.i(TAG, matches.size() + " preferred matches for " + intent);
- }
- for (int i = 0; i < matches.size(); i++) {
- PreferredActivity pa = matches.get(i);
+ // Get all of the existing entries that exactly match this filter.
+ ArrayList<PreferredActivity> existing = pir.findFilters(filter);
+ if (existing != null && existing.size() == 1) {
+ PreferredActivity cur = existing.get(0);
if (DEBUG_PREFERRED) {
- Slog.i(TAG, "Removing preferred activity "
- + pa.mPref.mComponent + ":");
+ Slog.i(TAG, "Checking replace of preferred:");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
+ if (!cur.mPref.mAlways) {
+ Slog.i(TAG, " -- CUR; not mAlways!");
+ } else {
+ Slog.i(TAG, " -- CUR: mMatch=" + cur.mPref.mMatch);
+ Slog.i(TAG, " -- CUR: mSet="
+ + Arrays.toString(cur.mPref.mSetComponents));
+ Slog.i(TAG, " -- CUR: mComponent=" + cur.mPref.mShortComponent);
+ Slog.i(TAG, " -- NEW: mMatch="
+ + (match&IntentFilter.MATCH_CATEGORY_MASK));
+ Slog.i(TAG, " -- CUR: mSet=" + Arrays.toString(set));
+ Slog.i(TAG, " -- CUR: mComponent=" + activity.flattenToShortString());
+ }
+ }
+ if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity)
+ && cur.mPref.mMatch == (match&IntentFilter.MATCH_CATEGORY_MASK)
+ && cur.mPref.sameSet(set)) {
+ if (DEBUG_PREFERRED) {
+ Slog.i(TAG, "Replacing with same preferred activity "
+ + cur.mPref.mShortComponent + " for user "
+ + userId + ":");
+ filter.dump(new LogPrinter(Log.INFO, TAG), " ");
+ } else {
+ Slog.i(TAG, "Replacing with same preferred activity "
+ + cur.mPref.mShortComponent + " for user "
+ + userId);
+ }
+ return;
+ }
+ }
+
+ if (existing != null) {
+ if (DEBUG_PREFERRED) {
+ Slog.i(TAG, existing.size() + " existing preferred matches for:");
+ filter.dump(new LogPrinter(Log.INFO, TAG), " ");
+ }
+ for (int i = 0; i < existing.size(); i++) {
+ PreferredActivity pa = existing.get(i);
+ if (DEBUG_PREFERRED) {
+ Slog.i(TAG, "Removing existing preferred activity "
+ + pa.mPref.mComponent + ":");
+ pa.dump(new LogPrinter(Log.INFO, TAG), " ");
+ }
+ pir.removeFilter(pa);
}
- pir.removeFilter(pa);
}
}
- addPreferredActivityInternal(filter, match, set, activity, true, callingUserId);
+ addPreferredActivityInternal(filter, match, set, activity, true, userId,
+ "Replacing preferred");
}
}
@@ -12610,7 +12607,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private boolean mMediaMounted = false;
- private String getEncryptKey() {
+ static String getEncryptKey() {
try {
String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
SD_ENCRYPTION_KEYSTORE_NAME);
@@ -12630,30 +12627,6 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.e(TAG, "Failed to retrieve encryption keys with exception: " + ioe);
return null;
}
-
- }
-
- /* package */static String getTempContainerId() {
- int tmpIdx = 1;
- String list[] = PackageHelper.getSecureContainerList();
- if (list != null) {
- for (final String name : list) {
- // Ignore null and non-temporary container entries
- if (name == null || !name.startsWith(mTempContainerPrefix)) {
- continue;
- }
-
- String subStr = name.substring(mTempContainerPrefix.length());
- try {
- int cid = Integer.parseInt(subStr);
- if (cid >= tmpIdx) {
- tmpIdx = cid + 1;
- }
- } catch (NumberFormatException e) {
- }
- }
- }
- return mTempContainerPrefix + tmpIdx;
}
/*
@@ -12711,31 +12684,27 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
private void updateExternalMediaStatusInner(boolean isMounted, boolean reportStatus,
boolean externalStorage) {
- // Collection of uids
- int uidArr[] = null;
- // Collection of stale containers
- HashSet<String> removeCids = new HashSet<String>();
- // Collection of packages on external media with valid containers.
- HashMap<AsecInstallArgs, String> processCids = new HashMap<AsecInstallArgs, String>();
- // Get list of secure containers.
- final String list[] = PackageHelper.getSecureContainerList();
- if (list == null || list.length == 0) {
- Log.i(TAG, "No secure containers on sdcard");
+ ArrayMap<AsecInstallArgs, String> processCids = new ArrayMap<>();
+ int[] uidArr = EmptyArray.INT;
+
+ final String[] list = PackageHelper.getSecureContainerList();
+ if (ArrayUtils.isEmpty(list)) {
+ Log.i(TAG, "No secure containers found");
} else {
// Process list of secure containers and categorize them
// as active or stale based on their package internal state.
- int uidList[] = new int[list.length];
- int num = 0;
+
// reader
synchronized (mPackages) {
for (String cid : list) {
+ // Leave stages untouched for now; installer service owns them
+ if (PackageInstallerService.isStageName(cid)) continue;
+
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Processing container " + cid);
String pkgName = getAsecPackageName(cid);
if (pkgName == null) {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Container : " + cid + " stale");
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + " with no package name");
continue;
}
if (DEBUG_SD_INSTALL)
@@ -12743,8 +12712,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps == null) {
- Log.i(TAG, "Deleting container with no matching settings " + cid);
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + " with no matching settings");
continue;
}
@@ -12760,7 +12728,8 @@ public class PackageManagerService extends IPackageManager.Stub {
getAppDexInstructionSets(ps), isForwardLocked(ps), isMultiArch(ps));
// The package status is changed only if the code path
// matches between settings and the container id.
- if (ps.codePathString != null && ps.codePathString.equals(args.getCodePath())) {
+ if (ps.codePathString != null
+ && ps.codePathString.startsWith(args.getCodePath())) {
if (DEBUG_SD_INSTALL) {
Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName
+ " at code path: " + ps.codePathString);
@@ -12770,35 +12739,25 @@ public class PackageManagerService extends IPackageManager.Stub {
processCids.put(args, ps.codePathString);
final int uid = ps.appId;
if (uid != -1) {
- uidList[num++] = uid;
+ uidArr = ArrayUtils.appendInt(uidArr, uid);
}
} else {
- Log.i(TAG, "Deleting stale container for " + cid);
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + ": expected codePath="
+ + ps.codePathString);
}
}
}
- if (num > 0) {
- // Sort uid list
- Arrays.sort(uidList, 0, num);
- // Throw away duplicates
- uidArr = new int[num];
- uidArr[0] = uidList[0];
- int di = 0;
- for (int i = 1; i < num; i++) {
- if (uidList[i - 1] != uidList[i]) {
- uidArr[di++] = uidList[i];
- }
- }
- }
+ Arrays.sort(uidArr);
}
+
// Process packages with valid entries.
if (isMounted) {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, removeCids);
+ loadMediaPackages(processCids, uidArr);
startCleaningPackages();
+ mInstallerService.onSecureContainersAvailable();
} else {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Unloading packages");
@@ -12806,8 +12765,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
int size = pkgList.size();
if (size > 0) {
// Send broadcasts here
@@ -12832,11 +12791,10 @@ public class PackageManagerService extends IPackageManager.Stub {
* the cid is added to list of removeCids. We currently don't delete stale
* containers.
*/
- private void loadMediaPackages(HashMap<AsecInstallArgs, String> processCids, int uidArr[],
- HashSet<String> removeCids) {
+ private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr) {
ArrayList<String> pkgList = new ArrayList<String>();
Set<AsecInstallArgs> keys = processCids.keySet();
- boolean doGc = false;
+
for (AsecInstallArgs args : keys) {
String codePath = processCids.get(args);
if (DEBUG_SD_INSTALL)
@@ -12850,7 +12808,7 @@ public class PackageManagerService extends IPackageManager.Stub {
continue;
}
// Check code path here.
- if (codePath == null || !codePath.equals(args.getCodePath())) {
+ if (codePath == null || !codePath.startsWith(args.getCodePath())) {
Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()
+ " does not match one in settings " + codePath);
continue;
@@ -12864,7 +12822,6 @@ public class PackageManagerService extends IPackageManager.Stub {
parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
}
- doGc = true;
synchronized (mInstallLock) {
PackageParser.Package pkg = null;
try {
@@ -12894,9 +12851,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} finally {
if (retCode != PackageManager.INSTALL_SUCCEEDED) {
- // Don't destroy container here. Wait till gc clears things
- // up.
- removeCids.add(args.cid);
+ Log.w(TAG, "Container " + args.cid + " is stale, retCode=" + retCode);
}
}
}
@@ -12931,21 +12886,6 @@ public class PackageManagerService extends IPackageManager.Stub {
if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
}
- // Force gc to avoid any stale parser references that we might have.
- if (doGc) {
- Runtime.getRuntime().gc();
- }
- // List stale containers and destroy stale temporary containers.
- if (removeCids != null) {
- for (String cid : removeCids) {
- if (cid.startsWith(mTempContainerPrefix)) {
- Log.i(TAG, "Destroying stale temporary container " + cid);
- PackageHelper.destroySdDir(cid);
- } else {
- Log.w(TAG, "Container " + cid + " is stale");
- }
- }
- }
}
/*
@@ -12969,7 +12909,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* that we always have to post this message if status has been requested no
* matter what.
*/
- private void unloadMediaPackages(HashMap<AsecInstallArgs, String> processCids, int uidArr[],
+ private void unloadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int uidArr[],
final boolean reportStatus) {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "unloading media packages");
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index f437372..69c1909 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -44,10 +44,10 @@ public class PreferredComponent {
// Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
public boolean mAlways;
- private final String[] mSetPackages;
- private final String[] mSetClasses;
- private final String[] mSetComponents;
- private final String mShortComponent;
+ final String[] mSetPackages;
+ final String[] mSetClasses;
+ final String[] mSetComponents;
+ final String mShortComponent;
private String mParseError;
private final Callbacks mCallbacks;
@@ -193,7 +193,12 @@ public class PreferredComponent {
}
public boolean sameSet(List<ResolveInfo> query, int priority) {
- if (mSetPackages == null) return false;
+ if (mSetPackages == null) {
+ return query == null;
+ }
+ if (query == null) {
+ return false;
+ }
final int NQ = query.size();
final int NS = mSetPackages.length;
int numMatch = 0;
@@ -215,6 +220,27 @@ public class PreferredComponent {
return numMatch == NS;
}
+ public boolean sameSet(ComponentName[] comps) {
+ if (mSetPackages == null) return false;
+ final int NQ = comps.length;
+ final int NS = mSetPackages.length;
+ int numMatch = 0;
+ for (int i=0; i<NQ; i++) {
+ ComponentName cn = comps[i];
+ boolean good = false;
+ for (int j=0; j<NS; j++) {
+ if (mSetPackages[j].equals(cn.getPackageName())
+ && mSetClasses[j].equals(cn.getClassName())) {
+ numMatch++;
+ good = true;
+ break;
+ }
+ }
+ if (!good) return false;
+ }
+ return numMatch == NS;
+ }
+
public void dump(PrintWriter out, String prefix, Object ident) {
out.print(prefix); out.print(
Integer.toHexString(System.identityHashCode(ident)));
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 2b4a24a..46497242 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -406,7 +406,6 @@ final class Notifier {
private void sendNextBroadcast() {
final int powerState;
- final int goToSleepReason;
synchronized (mLock) {
if (mBroadcastedPowerState == POWER_STATE_UNKNOWN) {
// Broadcasted power state is unknown. Send wake up.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 39c6e0e..9734bd4 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -56,10 +56,9 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.SystemService;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.os.Parcel;
-import android.os.ServiceManager;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.util.EventLog;
@@ -709,7 +708,6 @@ public final class PowerManagerService extends com.android.server.SystemService
if (mLowPowerModeEnabled != lowPowerModeEnabled) {
mLowPowerModeEnabled = lowPowerModeEnabled;
powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
- setSurfaceFlingerLowPowerMode(lowPowerModeEnabled ? 1 : 0);
mLowPowerModeEnabled = lowPowerModeEnabled;
BackgroundThread.getHandler().post(new Runnable() {
@Override
@@ -820,17 +818,12 @@ public final class PowerManagerService extends com.android.server.SystemService
+ " [" + wakeLock.mTag + "], flags=0x" + Integer.toHexString(flags));
}
- mWakeLocks.remove(index);
- notifyWakeLockReleasedLocked(wakeLock);
- wakeLock.mLock.unlinkToDeath(wakeLock, 0);
-
if ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0) {
mRequestWaitForNegativeProximity = true;
}
- applyWakeLockFlagsOnReleaseLocked(wakeLock);
- mDirty |= DIRTY_WAKE_LOCKS;
- updatePowerStateLocked();
+ wakeLock.mLock.unlinkToDeath(wakeLock, 0);
+ removeWakeLockLocked(wakeLock, index);
}
}
@@ -846,15 +839,19 @@ public final class PowerManagerService extends com.android.server.SystemService
return;
}
- mWakeLocks.remove(index);
- notifyWakeLockReleasedLocked(wakeLock);
-
- applyWakeLockFlagsOnReleaseLocked(wakeLock);
- mDirty |= DIRTY_WAKE_LOCKS;
- updatePowerStateLocked();
+ removeWakeLockLocked(wakeLock, index);
}
}
+ private void removeWakeLockLocked(WakeLock wakeLock, int index) {
+ mWakeLocks.remove(index);
+ notifyWakeLockReleasedLocked(wakeLock);
+
+ applyWakeLockFlagsOnReleaseLocked(wakeLock);
+ mDirty |= DIRTY_WAKE_LOCKS;
+ updatePowerStateLocked();
+ }
+
private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0
&& isScreenLock(wakeLock)) {
@@ -865,7 +862,8 @@ public final class PowerManagerService extends com.android.server.SystemService
}
}
- private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws, String historyTag) {
+ private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws, String historyTag,
+ int callingUid) {
synchronized (mLock) {
int index = findWakeLockIndexLocked(lock);
if (index < 0) {
@@ -873,7 +871,8 @@ public final class PowerManagerService extends com.android.server.SystemService
Slog.d(TAG, "updateWakeLockWorkSourceInternal: lock=" + Objects.hashCode(lock)
+ " [not found], ws=" + ws);
}
- throw new IllegalArgumentException("Wake lock not active");
+ throw new IllegalArgumentException("Wake lock not active: " + lock
+ + " from uid " + callingUid);
}
WakeLock wakeLock = mWakeLocks.get(index);
@@ -975,21 +974,26 @@ public final class PowerManagerService extends com.android.server.SystemService
return false;
}
- mNotifier.onUserActivity(event, uid);
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "userActivity");
+ try {
+ mNotifier.onUserActivity(event, uid);
- if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
- if (eventTime > mLastUserActivityTimeNoChangeLights
- && eventTime > mLastUserActivityTime) {
- mLastUserActivityTimeNoChangeLights = eventTime;
- mDirty |= DIRTY_USER_ACTIVITY;
- return true;
- }
- } else {
- if (eventTime > mLastUserActivityTime) {
- mLastUserActivityTime = eventTime;
- mDirty |= DIRTY_USER_ACTIVITY;
- return true;
+ if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
+ if (eventTime > mLastUserActivityTimeNoChangeLights
+ && eventTime > mLastUserActivityTime) {
+ mLastUserActivityTimeNoChangeLights = eventTime;
+ mDirty |= DIRTY_USER_ACTIVITY;
+ return true;
+ }
+ } else {
+ if (eventTime > mLastUserActivityTime) {
+ mLastUserActivityTime = eventTime;
+ mDirty |= DIRTY_USER_ACTIVITY;
+ return true;
+ }
}
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return false;
}
@@ -1012,25 +1016,30 @@ public final class PowerManagerService extends com.android.server.SystemService
return false;
}
- switch (mWakefulness) {
- case WAKEFULNESS_ASLEEP:
- Slog.i(TAG, "Waking up from sleep (uid " + uid +")...");
- break;
- case WAKEFULNESS_DREAMING:
- Slog.i(TAG, "Waking up from dream (uid " + uid +")...");
- break;
- case WAKEFULNESS_DOZING:
- Slog.i(TAG, "Waking up from dozing (uid " + uid +")...");
- break;
- }
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
+ try {
+ switch (mWakefulness) {
+ case WAKEFULNESS_ASLEEP:
+ Slog.i(TAG, "Waking up from sleep (uid " + uid +")...");
+ break;
+ case WAKEFULNESS_DREAMING:
+ Slog.i(TAG, "Waking up from dream (uid " + uid +")...");
+ break;
+ case WAKEFULNESS_DOZING:
+ Slog.i(TAG, "Waking up from dozing (uid " + uid +")...");
+ break;
+ }
- mLastWakeTime = eventTime;
- mDirty |= DIRTY_WAKEFULNESS;
- mWakefulness = WAKEFULNESS_AWAKE;
- setInteractiveStateLocked(true, 0);
+ mLastWakeTime = eventTime;
+ mDirty |= DIRTY_WAKEFULNESS;
+ mWakefulness = WAKEFULNESS_AWAKE;
+ setInteractiveStateLocked(true, 0);
- userActivityNoUpdateLocked(
- eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+ userActivityNoUpdateLocked(
+ eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
return true;
}
@@ -1058,53 +1067,58 @@ public final class PowerManagerService extends com.android.server.SystemService
return false;
}
- switch (reason) {
- case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
- Slog.i(TAG, "Going to sleep due to device administration policy "
- + "(uid " + uid +")...");
- break;
- case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
- Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
- break;
- case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
- Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
- break;
- case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
- Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
- break;
- case PowerManager.GO_TO_SLEEP_REASON_HDMI:
- Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
- break;
- default:
- Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
- reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
- break;
- }
-
- mLastSleepTime = eventTime;
- mDirty |= DIRTY_WAKEFULNESS;
- mWakefulness = WAKEFULNESS_DOZING;
- mSandmanSummoned = true;
- setInteractiveStateLocked(false, reason);
-
- // Report the number of wake locks that will be cleared by going to sleep.
- int numWakeLocksCleared = 0;
- final int numWakeLocks = mWakeLocks.size();
- for (int i = 0; i < numWakeLocks; i++) {
- final WakeLock wakeLock = mWakeLocks.get(i);
- switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
- case PowerManager.FULL_WAKE_LOCK:
- case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
- case PowerManager.SCREEN_DIM_WAKE_LOCK:
- numWakeLocksCleared += 1;
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
+ try {
+ switch (reason) {
+ case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
+ Slog.i(TAG, "Going to sleep due to device administration policy "
+ + "(uid " + uid +")...");
+ break;
+ case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
+ Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
+ break;
+ case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
+ Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
+ break;
+ case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
+ Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
+ break;
+ case PowerManager.GO_TO_SLEEP_REASON_HDMI:
+ Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
+ break;
+ default:
+ Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
+ reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
break;
}
- }
- EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);
- // Skip dozing if requested.
- if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
- reallyGoToSleepNoUpdateLocked(eventTime, uid);
+ mLastSleepTime = eventTime;
+ mDirty |= DIRTY_WAKEFULNESS;
+ mWakefulness = WAKEFULNESS_DOZING;
+ mSandmanSummoned = true;
+ setInteractiveStateLocked(false, reason);
+
+ // Report the number of wake locks that will be cleared by going to sleep.
+ int numWakeLocksCleared = 0;
+ final int numWakeLocks = mWakeLocks.size();
+ for (int i = 0; i < numWakeLocks; i++) {
+ final WakeLock wakeLock = mWakeLocks.get(i);
+ switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.FULL_WAKE_LOCK:
+ case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ case PowerManager.SCREEN_DIM_WAKE_LOCK:
+ numWakeLocksCleared += 1;
+ break;
+ }
+ }
+ EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);
+
+ // Skip dozing if requested.
+ if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
+ reallyGoToSleepNoUpdateLocked(eventTime, uid);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
}
@@ -1127,12 +1141,17 @@ public final class PowerManagerService extends com.android.server.SystemService
return false;
}
- Slog.i(TAG, "Nap time (uid " + uid +")...");
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap");
+ try {
+ Slog.i(TAG, "Nap time (uid " + uid +")...");
- mDirty |= DIRTY_WAKEFULNESS;
- mWakefulness = WAKEFULNESS_DREAMING;
- mSandmanSummoned = true;
- setInteractiveStateLocked(true, 0);
+ mDirty |= DIRTY_WAKEFULNESS;
+ mWakefulness = WAKEFULNESS_DREAMING;
+ mSandmanSummoned = true;
+ setInteractiveStateLocked(true, 0);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
return true;
}
@@ -1148,11 +1167,16 @@ public final class PowerManagerService extends com.android.server.SystemService
return false;
}
- Slog.i(TAG, "Sleeping (uid " + uid +")...");
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallyGoToSleep");
+ try {
+ Slog.i(TAG, "Sleeping (uid " + uid +")...");
- mDirty |= DIRTY_WAKEFULNESS;
- mWakefulness = WAKEFULNESS_ASLEEP;
- setInteractiveStateLocked(false, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+ mDirty |= DIRTY_WAKEFULNESS;
+ mWakefulness = WAKEFULNESS_ASLEEP;
+ setInteractiveStateLocked(false, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
return true;
}
@@ -1189,40 +1213,45 @@ public final class PowerManagerService extends com.android.server.SystemService
Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
}
- // Phase 0: Basic state updates.
- updateIsPoweredLocked(mDirty);
- updateStayOnLocked(mDirty);
-
- // Phase 1: Update wakefulness.
- // Loop because the wake lock and user activity computations are influenced
- // by changes in wakefulness.
- final long now = SystemClock.uptimeMillis();
- int dirtyPhase2 = 0;
- for (;;) {
- int dirtyPhase1 = mDirty;
- dirtyPhase2 |= dirtyPhase1;
- mDirty = 0;
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
+ try {
+ // Phase 0: Basic state updates.
+ updateIsPoweredLocked(mDirty);
+ updateStayOnLocked(mDirty);
- updateWakeLockSummaryLocked(dirtyPhase1);
- updateUserActivitySummaryLocked(now, dirtyPhase1);
- if (!updateWakefulnessLocked(dirtyPhase1)) {
- break;
+ // Phase 1: Update wakefulness.
+ // Loop because the wake lock and user activity computations are influenced
+ // by changes in wakefulness.
+ final long now = SystemClock.uptimeMillis();
+ int dirtyPhase2 = 0;
+ for (;;) {
+ int dirtyPhase1 = mDirty;
+ dirtyPhase2 |= dirtyPhase1;
+ mDirty = 0;
+
+ updateWakeLockSummaryLocked(dirtyPhase1);
+ updateUserActivitySummaryLocked(now, dirtyPhase1);
+ if (!updateWakefulnessLocked(dirtyPhase1)) {
+ break;
+ }
}
- }
- // Phase 2: Update dreams and display power state.
- updateDreamLocked(dirtyPhase2);
- updateDisplayPowerStateLocked(dirtyPhase2);
+ // Phase 2: Update dreams and display power state.
+ updateDreamLocked(dirtyPhase2);
+ updateDisplayPowerStateLocked(dirtyPhase2);
- // Phase 3: Send notifications, if needed.
- if (mDisplayReady) {
- finishInteractiveStateChangeLocked();
- }
+ // Phase 3: Send notifications, if needed.
+ if (mDisplayReady) {
+ finishInteractiveStateChangeLocked();
+ }
- // Phase 4: Update suspend blocker.
- // Because we might release the last suspend blocker here, we need to make sure
- // we finished everything else first!
- updateSuspendBlockerLocked();
+ // Phase 4: Update suspend blocker.
+ // Because we might release the last suspend blocker here, we need to make sure
+ // we finished everything else first!
+ updateSuspendBlockerLocked();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
}
/**
@@ -1993,7 +2022,12 @@ public final class PowerManagerService extends com.android.server.SystemService
Slog.d(TAG, "Setting HAL auto-suspend mode to " + enable);
}
mHalAutoSuspendModeEnabled = enable;
- nativeSetAutoSuspend(enable);
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
+ try {
+ nativeSetAutoSuspend(enable);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
}
}
@@ -2003,7 +2037,12 @@ public final class PowerManagerService extends com.android.server.SystemService
Slog.d(TAG, "Setting HAL interactive mode to " + enable);
}
mHalInteractiveModeEnabled = enable;
- nativeSetInteractive(enable);
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")");
+ try {
+ nativeSetInteractive(enable);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
}
}
@@ -2198,21 +2237,6 @@ public final class PowerManagerService extends com.android.server.SystemService
nativeSendPowerHint(hintId, data);
}
- private static void setSurfaceFlingerLowPowerMode(int enabled) {
- try {
- final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- data.writeInt(enabled);
- flinger.transact(1016, data, null, 0);
- data.recycle();
- }
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to reduce refresh rate", ex);
- }
- }
-
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
@@ -2605,20 +2629,23 @@ public final class PowerManagerService extends com.android.server.SystemService
private final class SuspendBlockerImpl implements SuspendBlocker {
private final String mName;
+ private final String mTraceName;
private int mReferenceCount;
public SuspendBlockerImpl(String name) {
mName = name;
+ mTraceName = "SuspendBlocker (" + name + ")";
}
@Override
protected void finalize() throws Throwable {
try {
if (mReferenceCount != 0) {
- Log.wtf(TAG, "Suspend blocker \"" + mName
+ Slog.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was finalized without being released!");
mReferenceCount = 0;
nativeReleaseSuspendBlocker(mName);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
}
} finally {
super.finalize();
@@ -2633,6 +2660,7 @@ public final class PowerManagerService extends com.android.server.SystemService
if (DEBUG_SPEW) {
Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
}
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
nativeAcquireSuspendBlocker(mName);
}
}
@@ -2647,8 +2675,9 @@ public final class PowerManagerService extends com.android.server.SystemService
Slog.d(TAG, "Releasing suspend blocker \"" + mName + "\".");
}
nativeReleaseSuspendBlocker(mName);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
} else if (mReferenceCount < 0) {
- Log.wtf(TAG, "Suspend blocker \"" + mName
+ Slog.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was released without being acquired!", new Throwable());
mReferenceCount = 0;
}
@@ -2664,6 +2693,8 @@ public final class PowerManagerService extends com.android.server.SystemService
}
private final class ScreenOnBlockerImpl implements ScreenOnBlocker {
+ private static final String TRACE_NAME = "ScreenOnBlocker";
+
private int mNestCount;
public boolean isHeld() {
@@ -2676,9 +2707,12 @@ public final class PowerManagerService extends com.android.server.SystemService
public void acquire() {
synchronized (this) {
mNestCount += 1;
- if (DEBUG) {
+ if (DEBUG || true) {
Slog.d(TAG, "Screen on blocked: mNestCount=" + mNestCount);
}
+ if (mNestCount == 1) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_NAME, 0);
+ }
}
}
@@ -2686,16 +2720,16 @@ public final class PowerManagerService extends com.android.server.SystemService
public void release() {
synchronized (this) {
mNestCount -= 1;
- if (mNestCount < 0) {
- Log.wtf(TAG, "Screen on blocker was released without being acquired!",
- new Throwable());
- mNestCount = 0;
- }
if (mNestCount == 0) {
+ if (DEBUG || true) {
+ Slog.d(TAG, "Screen on unblocked: mNestCount=" + mNestCount);
+ }
mHandler.sendEmptyMessage(MSG_SCREEN_ON_BLOCKER_RELEASED);
- }
- if (DEBUG) {
- Slog.d(TAG, "Screen on unblocked: mNestCount=" + mNestCount);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_NAME, 0);
+ } else if (mNestCount < 0) {
+ Slog.wtf(TAG, "Screen on blocker was released without being acquired!",
+ new Throwable());
+ mNestCount = 0;
}
}
}
@@ -2802,9 +2836,10 @@ public final class PowerManagerService extends com.android.server.SystemService
ws = null;
}
+ final int callingUid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- updateWakeLockWorkSourceInternal(lock, ws, historyTag);
+ updateWakeLockWorkSourceInternal(lock, ws, historyTag, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 21905f0..f85e2d9 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -22,7 +22,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -39,18 +38,14 @@ import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
/**
* A note on locking: We rely on the fact that calls onto mBar are oneway or
* if they are local, that they just enqueue messages to not deadlock.
*/
-public class StatusBarManagerService extends IStatusBarService.Stub
- implements WindowManagerService.OnHardKeyboardStatusChangeListener
-{
+public class StatusBarManagerService extends IStatusBarService.Stub {
private static final String TAG = "StatusBarManagerService";
private static final boolean SPEW = false;
@@ -95,7 +90,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub
public StatusBarManagerService(Context context, WindowManagerService windowManager) {
mContext = context;
mWindowManager = windowManager;
- mWindowManager.setOnHardKeyboardStatusChangeListener(this);
final Resources res = context.getResources();
mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));
@@ -190,10 +184,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub
@Override
public void disable(int what, IBinder token, String pkg) {
- if (!mNotificationDelegate.allowDisable(what, token, pkg)) {
- if (SPEW) Slog.d(TAG, "Blocking disable request from " + pkg);
- return;
- }
disableInternal(mCurrentUserId, what, token, pkg);
}
@@ -394,29 +384,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub
}
@Override
- public void setHardKeyboardEnabled(final boolean enabled) {
- mHandler.post(new Runnable() {
- public void run() {
- mWindowManager.setHardKeyboardEnabled(enabled);
- }
- });
- }
-
- @Override
- public void onHardKeyboardStatusChange(final boolean available, final boolean enabled) {
- mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.setHardKeyboardStatus(available, enabled);
- } catch (RemoteException ex) {
- }
- }
- }
- });
- }
-
- @Override
public void toggleRecentApps() {
if (mBar != null) {
try {
@@ -510,11 +477,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub
switches[2] = mMenuVisible ? 1 : 0;
switches[3] = mImeWindowVis;
switches[4] = mImeBackDisposition;
- switches[7] = mShowImeSwitcher ? 1 : 0;
+ switches[5] = mShowImeSwitcher ? 1 : 0;
binders.add(mImeToken);
}
- switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0;
- switches[6] = mWindowManager.isHardKeyboardEnabled() ? 1 : 0;
}
/**
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index eb38f4a..468a344 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -453,6 +453,8 @@ public class DeviceStorageMonitorService extends SystemService {
notification.icon = com.android.internal.R.drawable.stat_notify_disk_full;
notification.tickerText = title;
notification.flags |= Notification.FLAG_NO_CLEAR;
+ notification.color = context.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
notification.setLatestEventInfo(context, title, details, intent);
notification.visibility = Notification.VISIBILITY_PUBLIC;
notification.category = Notification.CATEGORY_SYSTEM;
diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java
index 4af8f2c..fcfaaea 100644
--- a/services/core/java/com/android/server/tv/PersistentDataStore.java
+++ b/services/core/java/com/android/server/tv/PersistentDataStore.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.Intent;
import android.media.tv.TvContentRating;
import android.media.tv.TvInputManager;
+import android.os.Environment;
import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -44,6 +45,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -68,7 +70,8 @@ final class PersistentDataStore {
// The atomic file used to safely read or write the file.
private final AtomicFile mAtomicFile;
- private final List<TvContentRating> mBlockedRatings = new ArrayList<TvContentRating>();
+ private final List<TvContentRating> mBlockedRatings =
+ Collections.synchronizedList(new ArrayList<TvContentRating>());
private boolean mBlockedRatingsChanged;
@@ -81,8 +84,13 @@ final class PersistentDataStore {
public PersistentDataStore(Context context, int userId) {
mContext = context;
- mAtomicFile = new AtomicFile(new File("/data/system/tv/" + userId
- + "/tv-input-manager-state.xml"));
+ File userDir = Environment.getUserSystemDirectory(userId);
+ if (!userDir.exists()) {
+ if (!userDir.mkdirs()) {
+ throw new IllegalStateException("User dir cannot be created: " + userDir);
+ }
+ }
+ mAtomicFile = new AtomicFile(new File(userDir, "tv-input-manager-state.xml"));
}
public boolean isParentalControlsEnabled() {
@@ -101,9 +109,11 @@ final class PersistentDataStore {
public boolean isRatingBlocked(TvContentRating rating) {
loadIfNeeded();
- for (TvContentRating blcokedRating : mBlockedRatings) {
- if (rating.contains(blcokedRating)) {
- return true;
+ synchronized (mBlockedRatings) {
+ for (TvContentRating blcokedRating : mBlockedRatings) {
+ if (rating.contains(blcokedRating)) {
+ return true;
+ }
}
}
return false;
@@ -265,10 +275,12 @@ final class PersistentDataStore {
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, TAG_TV_INPUT_MANAGER_STATE);
serializer.startTag(null, TAG_BLOCKED_RATINGS);
- for (TvContentRating rating : mBlockedRatings) {
- serializer.startTag(null, TAG_RATING);
- serializer.attribute(null, ATTR_STRING, rating.flattenToString());
- serializer.endTag(null, TAG_RATING);
+ synchronized (mBlockedRatings) {
+ for (TvContentRating rating : mBlockedRatings) {
+ serializer.startTag(null, TAG_RATING);
+ serializer.attribute(null, ATTR_STRING, rating.flattenToString());
+ serializer.endTag(null, TAG_RATING);
+ }
}
serializer.endTag(null, TAG_BLOCKED_RATINGS);
serializer.startTag(null, TAG_PARENTAL_CONTROLS);
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index e7dd82d..c7d95aa 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -19,12 +19,11 @@ package com.android.server.tv;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvStreamConfig;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Message;
-import android.view.Surface;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.view.Surface;
import java.util.LinkedList;
import java.util.Queue;
@@ -64,12 +63,12 @@ final class TvInputHal implements Handler.Callback {
int generation);
private static native void nativeClose(long ptr);
- private Object mLock = new Object();
+ private final Object mLock = new Object();
private long mPtr = 0;
private final Callback mCallback;
private final Handler mHandler;
- private SparseIntArray mStreamConfigGenerations = new SparseIntArray();
- private SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();;
+ private final SparseIntArray mStreamConfigGenerations = new SparseIntArray();
+ private final SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();
public TvInputHal(Callback callback) {
mCallback = callback;
@@ -153,7 +152,7 @@ final class TvInputHal implements Handler.Callback {
// Handler.Callback implementation
- private Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
+ private final Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
@Override
public boolean handleMessage(Message msg) {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 78ea6c9..fa991c2 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -20,14 +20,12 @@ import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
import android.content.Context;
-import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiHotplugEventListener;
-import android.hardware.hdmi.IHdmiInputChangeListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
import android.media.AudioDevicePort;
import android.media.AudioFormat;
@@ -39,7 +37,6 @@ import android.media.AudioPort;
import android.media.AudioPortConfig;
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
-import android.media.tv.TvContract;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvStreamConfig;
@@ -77,7 +74,6 @@ import java.util.Map;
class TvInputHardwareManager implements TvInputHal.Callback {
private static final String TAG = TvInputHardwareManager.class.getSimpleName();
- private final Context mContext;
private final Listener mListener;
private final TvInputHal mHal = new TvInputHal(this);
private final SparseArray<Connection> mConnections = new SparseArray<>();
@@ -107,7 +103,6 @@ class TvInputHardwareManager implements TvInputHal.Callback {
private final Object mLock = new Object();
public TvInputHardwareManager(Context context, Listener listener) {
- mContext = context;
mListener = listener;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mHal.init();
@@ -285,7 +280,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
return -1;
}
- public void addHdmiTvInput(int logicalAddress, TvInputInfo info) {
+ public void addHdmiTvInput(int id, TvInputInfo info) {
if (info.getType() != TvInputInfo.TYPE_HDMI) {
throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
}
@@ -295,13 +290,13 @@ class TvInputHardwareManager implements TvInputHal.Callback {
if (parentIndex < 0) {
throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
}
- String oldInputId = mHdmiInputIdMap.get(logicalAddress);
+ String oldInputId = mHdmiInputIdMap.get(id);
if (oldInputId != null) {
Slog.w(TAG, "Trying to override previous registration: old = "
- + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
- + info + ":" + logicalAddress);
+ + mInputMap.get(oldInputId) + ":" + id + ", new = "
+ + info + ":" + id);
}
- mHdmiInputIdMap.put(logicalAddress, info.getId());
+ mHdmiInputIdMap.put(id, info.getId());
mInputMap.put(info.getId(), info);
}
}
@@ -963,7 +958,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
Object obj = null;
switch (status) {
case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
- if (!mHdmiDeviceList.contains(deviceInfo)) {
+ if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
mHdmiDeviceList.add(deviceInfo);
} else {
Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
@@ -974,7 +969,8 @@ class TvInputHardwareManager implements TvInputHal.Callback {
break;
}
case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
- if (!mHdmiDeviceList.remove(deviceInfo)) {
+ HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
+ if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
return;
}
@@ -983,13 +979,14 @@ class TvInputHardwareManager implements TvInputHal.Callback {
break;
}
case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
- if (!mHdmiDeviceList.remove(deviceInfo)) {
+ HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
+ if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
return;
}
mHdmiDeviceList.add(deviceInfo);
messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
- String inputId = mHdmiInputIdMap.get(deviceInfo.getLogicalAddress());
+ String inputId = mHdmiInputIdMap.get(deviceInfo.getId());
SomeArgs args = SomeArgs.obtain();
args.arg1 = inputId;
args.arg2 = deviceInfo;
@@ -1006,6 +1003,15 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
}
}
+
+ private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
+ for (HdmiDeviceInfo info : mHdmiDeviceList) {
+ if (info.getId() == id) {
+ return info;
+ }
+ }
+ return null;
+ }
}
private final class HdmiSystemAudioModeChangeListener extends
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 3b5e79d..6481944 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -283,7 +283,6 @@ public final class TvInputManagerService extends SystemService {
userState.inputMap = inputMap;
userState.ratingSystemXmlUriSet.clear();
- userState.ratingSystemXmlUriSet.add(TvContentRating.SYSTEM_CONTENT_RATING_SYSTEM_XML);
for (TvInputState state : userState.inputMap.values()) {
Uri ratingSystemXmlUri = state.mInfo.getRatingSystemXmlUri();
if (ratingSystemXmlUri != null) {
@@ -691,6 +690,10 @@ public final class TvInputManagerService extends SystemService {
private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
if (sessionState.mSession != null) {
+ UserState userState = getUserStateLocked(userId);
+ if (sessionToken == userState.mainSessionToken) {
+ setMainLocked(sessionToken, false, callingUid, userId);
+ }
try {
sessionState.mSession.release();
} catch (RemoteException e) {
@@ -704,6 +707,9 @@ public final class TvInputManagerService extends SystemService {
private void removeSessionStateLocked(IBinder sessionToken, int userId) {
UserState userState = getUserStateLocked(userId);
if (sessionToken == userState.mainSessionToken) {
+ if (DEBUG) {
+ Slog.d(TAG, "mainSessionToken=null");
+ }
userState.mainSessionToken = null;
}
@@ -740,6 +746,25 @@ public final class TvInputManagerService extends SystemService {
mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget();
}
+ private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+ if (sessionState.mHardwareSessionToken != null) {
+ sessionState = getSessionStateLocked(sessionState.mHardwareSessionToken,
+ Process.SYSTEM_UID, userId);
+ }
+ ServiceState serviceState = getServiceStateLocked(sessionState.mInfo.getComponent(),
+ userId);
+ if (!serviceState.mIsHardware) {
+ return;
+ }
+ ITvInputSession session = getSessionLocked(sessionState);
+ try {
+ session.setMain(isMain);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in setMain", e);
+ }
+ }
+
private void notifyInputAddedLocked(UserState userState, String inputId) {
if (DEBUG) {
Slog.d(TAG, "notifyInputAdded: inputId = " + inputId);
@@ -1050,6 +1075,9 @@ public final class TvInputManagerService extends SystemService {
@Override
public void releaseSession(IBinder sessionToken, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "releaseSession(): " + sessionToken);
+ }
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "releaseSession");
@@ -1065,6 +1093,9 @@ public final class TvInputManagerService extends SystemService {
@Override
public void setMainSession(IBinder sessionToken, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "setMainSession(): " + sessionToken);
+ }
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "setMainSession");
@@ -1075,50 +1106,19 @@ public final class TvInputManagerService extends SystemService {
if (userState.mainSessionToken == sessionToken) {
return;
}
-
- SessionState newMainSessionState = getSessionStateLocked(
- sessionToken, callingUid, resolvedUserId);
- if (newMainSessionState.mHardwareSessionToken != null) {
- newMainSessionState = getSessionStateLocked(
- newMainSessionState.mHardwareSessionToken,
- Process.SYSTEM_UID, resolvedUserId);
- }
- ServiceState newMainServiceState = getServiceStateLocked(
- newMainSessionState.mInfo.getComponent(), resolvedUserId);
- ITvInputSession newMainSession = getSessionLocked(newMainSessionState);
-
- ServiceState oldMainServiceState = null;
- ITvInputSession oldMainSession = null;
- if (userState.mainSessionToken != null) {
- SessionState oldMainSessionState = getSessionStateLocked(
- userState.mainSessionToken, Process.SYSTEM_UID, resolvedUserId);
- if (oldMainSessionState.mHardwareSessionToken != null) {
- oldMainSessionState = getSessionStateLocked(
- oldMainSessionState.mHardwareSessionToken,
- Process.SYSTEM_UID, resolvedUserId);
- }
- oldMainServiceState = getServiceStateLocked(
- oldMainSessionState.mInfo.getComponent(), resolvedUserId);
- oldMainSession = getSessionLocked(oldMainSessionState);
+ if (DEBUG) {
+ Slog.d(TAG, "mainSessionToken=" + sessionToken);
}
-
+ IBinder oldMainSessionToken = userState.mainSessionToken;
userState.mainSessionToken = sessionToken;
// Inform the new main session first.
- // See {@link TvInputService#onSetMainSession}.
- if (newMainServiceState.mIsHardware) {
- try {
- newMainSession.setMainSession(true);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in setMainSession", e);
- }
+ // See {@link TvInputService.Session#onSetMain}.
+ if (sessionToken != null) {
+ setMainLocked(sessionToken, true, callingUid, userId);
}
- if (oldMainSession != null && oldMainServiceState.mIsHardware) {
- try {
- oldMainSession.setMainSession(false);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in setMainSession", e);
- }
+ if (oldMainSessionToken != null) {
+ setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId);
}
}
} finally {
@@ -1223,7 +1223,7 @@ public final class TvInputManagerService extends SystemService {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
channelUri, params);
- if (TvContract.isChannelUriForPassthroughTvInput(channelUri)) {
+ if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
// Do not log the watch history for passthrough inputs.
return;
}
@@ -1800,11 +1800,8 @@ public final class TvInputManagerService extends SystemService {
UserState userState = getUserStateLocked(mUserId);
for (SessionState sessionState : userState.sessionStateMap.values()) {
if (mSessionToken == sessionState.mHardwareSessionToken) {
- try {
- sessionState.mSession.release();
- } catch (RemoteException e) {
- Slog.e(TAG, "error in release", e);
- }
+ releaseSessionLocked(sessionState.mSessionToken, Process.SYSTEM_UID,
+ mUserId);
try {
sessionState.mClient.onSessionReleased(sessionState.mSeq);
} catch (RemoteException e) {
@@ -1962,11 +1959,11 @@ public final class TvInputManagerService extends SystemService {
}
@Override
- public void addHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) {
+ public void addHdmiTvInput(int id, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
synchronized (mLock) {
- mTvInputHardwareManager.addHdmiTvInput(logicalAddress, inputInfo);
+ mTvInputHardwareManager.addHdmiTvInput(id, inputInfo);
addTvInputLocked(inputInfo);
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3fcd067..b2575e6 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -252,6 +252,15 @@ class AppWindowToken extends WindowToken {
return false;
}
+ void removeAllWindows() {
+ for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+ WindowState win = allAppWindows.get(winNdx);
+ if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) Slog.w(WindowManagerService.TAG,
+ "removeAllWindows: removing win=" + win);
+ win.mService.removeWindowLocked(win.mSession, win);
+ }
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index dfb1200..238c77e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -155,7 +155,7 @@ public class TaskStack {
final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
- if (winAnimator.isAnimating() && !winAnimator.isDummyAnimation()) {
+ if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 32e8afe..4edd5cf 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -88,6 +88,8 @@ public class WindowAnimator {
boolean mInitialized = false;
boolean mKeyguardGoingAway;
+ boolean mKeyguardGoingAwayToNotificationShade;
+ boolean mKeyguardGoingAwayDisableWindowAnimations;
// forceHiding states.
static final int KEYGUARD_NOT_SHOWN = 0;
@@ -389,7 +391,11 @@ public class WindowAnimator {
if (unForceHiding != null) {
boolean startKeyguardExit = true;
for (int i=unForceHiding.size()-1; i>=0; i--) {
- Animation a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding);
+ Animation a = null;
+ if (!mKeyguardGoingAwayDisableWindowAnimations) {
+ a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding,
+ mKeyguardGoingAwayToNotificationShade);
+ }
if (a != null) {
final WindowStateAnimator winAnimator = unForceHiding.get(i);
winAnimator.setAnimation(a);
@@ -405,9 +411,14 @@ public class WindowAnimator {
}
// Wallpaper is going away in un-force-hide motion, animate it as well.
- if (!wallpaperInUnForceHiding && wallpaper != null) {
- WindowStateAnimator animator = wallpaper.mWinAnimator;
- animator.setAnimation(mPolicy.createForceHideWallpaperExitAnimation());
+ if (!wallpaperInUnForceHiding && wallpaper != null
+ && !mKeyguardGoingAwayDisableWindowAnimations) {
+ Animation a = mPolicy.createForceHideWallpaperExitAnimation(
+ mKeyguardGoingAwayToNotificationShade);
+ if (a != null) {
+ WindowStateAnimator animator = wallpaper.mWinAnimator;
+ animator.setAnimation(a);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cd9f050..b57a090 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -51,6 +51,7 @@ import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
import android.animation.ValueAnimator;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -58,6 +59,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
@@ -428,7 +430,7 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Stores for each user whether screencapture is disabled
* This array is essentially a cache for all userId for
- * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+ * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled}
*/
SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<Boolean>();
@@ -537,8 +539,23 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>();
boolean mHardKeyboardAvailable;
- boolean mHardKeyboardEnabled;
+ boolean mShowImeWithHardKeyboard;
OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
+ SettingsObserver mSettingsObserver;
+
+ private final class SettingsObserver extends ContentObserver {
+ public SettingsObserver() {
+ super(new Handler());
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateShowImeWithHardKeyboard();
+ }
+ }
final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>();
@@ -838,6 +855,9 @@ public class WindowManagerService extends IWindowManager.Stub
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter);
+ mSettingsObserver = new SettingsObserver();
+ updateShowImeWithHardKeyboard();
+
mHoldingScreenWakeLock = mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG);
mHoldingScreenWakeLock.setReferenceCounted(false);
@@ -2315,9 +2335,12 @@ public class WindowManagerService extends IWindowManager.Stub
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (token.appWindowToken != null) {
- Slog.i(TAG, "Non-null appWindowToken for system window of type=" + type);
- // app token should be null for any other window types.
- token.appWindowToken = null;
+ Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type);
+ // It is not valid to use an app token with other system types; we will
+ // instead make a new token for it (as if null had been passed in for the token).
+ attrs.token = null;
+ token = new WindowToken(this, null, -1, false);
+ addToken = true;
}
win = new WindowState(this, session, client, token,
@@ -3592,7 +3615,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
final Task oldTask = mTaskIdToTask.get(atoken.groupId);
- removeAppFromTaskLocked(atoken);
+ oldTask.removeAppToken(atoken);
atoken.groupId = groupId;
Task newTask = mTaskIdToTask.get(groupId);
@@ -4634,6 +4657,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
void removeAppFromTaskLocked(AppWindowToken wtoken) {
+ wtoken.removeAllWindows();
+
final Task task = mTaskIdToTask.get(wtoken.groupId);
if (task != null) {
if (!task.removeAppToken(wtoken)) {
@@ -5243,13 +5268,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void keyguardGoingAway() {
+ public void keyguardGoingAway(boolean disableWindowAnimations,
+ boolean keyguardGoingToNotificationShade) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
synchronized (mWindowMap) {
mAnimator.mKeyguardGoingAway = true;
+ mAnimator.mKeyguardGoingAwayToNotificationShade = keyguardGoingToNotificationShade;
+ mAnimator.mKeyguardGoingAwayDisableWindowAnimations = disableWindowAnimations;
requestTraversalLocked();
}
}
@@ -5264,6 +5292,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
if (mKeyguardWaitingForActivityDrawn) {
mPolicy.notifyActivityDrawnForKeyguardLw();
+ mKeyguardWaitingForActivityDrawn = false;
}
}
}
@@ -7009,11 +7038,10 @@ public class WindowManagerService extends IWindowManager.Stub
boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
if (hardKeyboardAvailable != mHardKeyboardAvailable) {
mHardKeyboardAvailable = hardKeyboardAvailable;
- mHardKeyboardEnabled = hardKeyboardAvailable;
mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
}
- if (!mHardKeyboardEnabled) {
+ if (mShowImeWithHardKeyboard) {
config.keyboard = Configuration.KEYBOARD_NOKEYS;
}
@@ -7033,18 +7061,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public boolean isHardKeyboardEnabled() {
+ public void updateShowImeWithHardKeyboard() {
+ boolean showImeWithHardKeyboard = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0,
+ mCurrentUserId) == 1;
synchronized (mWindowMap) {
- return mHardKeyboardEnabled;
- }
- }
-
- public void setHardKeyboardEnabled(boolean enabled) {
- synchronized (mWindowMap) {
- if (mHardKeyboardEnabled != enabled) {
- mHardKeyboardEnabled = enabled;
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
- }
+ mShowImeWithHardKeyboard = showImeWithHardKeyboard;
}
}
@@ -7056,15 +7078,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
void notifyHardKeyboardStatusChange() {
- final boolean available, enabled;
+ final boolean available;
final OnHardKeyboardStatusChangeListener listener;
synchronized (mWindowMap) {
listener = mHardKeyboardStatusChangeListener;
available = mHardKeyboardAvailable;
- enabled = mHardKeyboardEnabled;
}
if (listener != null) {
- listener.onHardKeyboardStatusChange(available, enabled);
+ listener.onHardKeyboardStatusChange(available);
}
}
@@ -11047,7 +11068,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
public interface OnHardKeyboardStatusChangeListener {
- public void onHardKeyboardStatusChange(boolean available, boolean enabled);
+ public void onHardKeyboardStatusChange(boolean available);
}
void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 61c50d6..7bf090a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1211,6 +1211,12 @@ class WindowStateAnimator {
|| w.mDecorFrame.isEmpty()) {
// The universe background isn't cropped, nor windows without policy decor.
w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
+ } else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.mAnimating) {
+ // If we're animating, the wallpaper crop should only be updated at the end of the
+ // animation.
+ mTmpClipRect.set(w.mSystemDecorRect);
+ applyDecorRect(w.mDecorFrame);
+ w.mSystemDecorRect.union(mTmpClipRect);
} else {
// Crop to the system decor specified by policy.
applyDecorRect(w.mDecorFrame);
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ce2ca9b..39b70a8 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,6 +41,8 @@ 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);
+int register_android_server_Watchdog(JNIEnv* env);
};
using namespace android;
@@ -77,6 +79,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
register_android_server_hdmi_HdmiMhlController(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
+ register_android_server_fingerprint_FingerprintService(env);
+ register_android_server_Watchdog(env);
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/Android.mk b/services/devicepolicy/Android.mk
index a55d138..7020f17 100644
--- a/services/devicepolicy/Android.mk
+++ b/services/devicepolicy/Android.mk
@@ -7,6 +7,6 @@ LOCAL_MODULE := services.devicepolicy
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
-LOCAL_JAVA_LIBRARIES := conscrypt
+LOCAL_JAVA_LIBRARIES := conscrypt services.core
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fc96991..21ba31e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18,17 +18,6 @@ package com.android.server.devicepolicy;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
-import android.app.admin.DevicePolicyManagerInternal;
-import com.android.internal.R;
-import com.android.internal.os.storage.ExternalStorageFormatter;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.JournaledFile;
-import com.android.internal.util.XmlUtils;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
@@ -40,6 +29,7 @@ import android.app.PendingIntent;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -58,6 +48,7 @@ import android.net.ConnectivityManager;
import android.net.Uri;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
+import android.hardware.usb.UsbManager;
import android.net.ProxyInfo;
import android.os.Binder;
import android.os.Bundle;
@@ -88,6 +79,16 @@ import android.util.SparseArray;
import android.util.Xml;
import android.view.IWindowManager;
+import com.android.internal.R;
+import com.android.internal.os.storage.ExternalStorageFormatter;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
+import com.android.internal.util.XmlUtils;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.org.conscrypt.TrustedCertificateStore;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
import org.xmlpull.v1.XmlPullParser;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -145,10 +146,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
private static final String ATTR_SETUP_COMPLETE = "setup-complete";
+ private static final Set<String> DEVICE_OWNER_USER_RESTRICTIONS;
+ static {
+ DEVICE_OWNER_USER_RESTRICTIONS = new HashSet();
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_USB_FILE_TRANSFER);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_TETHERING);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_FACTORY_RESET);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_ADD_USER);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_ADJUST_VOLUME);
+ DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SMS);
+ }
+
final Context mContext;
final UserManager mUserManager;
final PowerManager.WakeLock mWakeLock;
+ final LocalService mLocalService;
+
IPowerManager mIPowerManager;
IWindowManager mIWindowManager;
NotificationManager mNotificationManager;
@@ -774,6 +792,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
PackageManager.FEATURE_DEVICE_ADMIN);
mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM");
+ mLocalService = new LocalService();
if (!mHasFeature) {
// Skip the rest of the initialization
return;
@@ -794,7 +813,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
filter.addDataScheme("package");
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
- LocalServices.addService(DevicePolicyManagerInternal.class, new LocalService());
+ LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService);
}
/**
@@ -1515,6 +1534,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
.setContentIntent(notifyIntent)
.setPriority(Notification.PRIORITY_HIGH)
.setShowWhen(false)
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
.build();
// If this is a boot intent, this will fire for each user. But if this is a storage changed
@@ -1881,44 +1902,71 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) {
- ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (activeAdmin.crossProfileWidgetProviders == null) {
- activeAdmin.crossProfileWidgetProviders = new ArrayList<>();
+ final int userId = UserHandle.getCallingUserId();
+ List<String> changedProviders = null;
+
+ synchronized (this) {
+ ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (activeAdmin.crossProfileWidgetProviders == null) {
+ activeAdmin.crossProfileWidgetProviders = new ArrayList<>();
+ }
+ List<String> providers = activeAdmin.crossProfileWidgetProviders;
+ if (!providers.contains(packageName)) {
+ providers.add(packageName);
+ changedProviders = new ArrayList<>(providers);
+ saveSettingsLocked(userId);
+ }
}
- if (activeAdmin.crossProfileWidgetProviders.add(packageName)) {
- saveSettingsLocked(UserHandle.getCallingUserId());
+
+ if (changedProviders != null) {
+ mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders);
return true;
}
+
return false;
}
@Override
public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) {
- ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (activeAdmin.crossProfileWidgetProviders == null) {
- return false;
+ final int userId = UserHandle.getCallingUserId();
+ List<String> changedProviders = null;
+
+ synchronized (this) {
+ ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (activeAdmin.crossProfileWidgetProviders == null) {
+ return false;
+ }
+ List<String> providers = activeAdmin.crossProfileWidgetProviders;
+ if (providers.remove(packageName)) {
+ changedProviders = new ArrayList<>(providers);
+ saveSettingsLocked(userId);
+ }
}
- if (activeAdmin.crossProfileWidgetProviders.remove(packageName)) {
- saveSettingsLocked(UserHandle.getCallingUserId());
+
+ if (changedProviders != null) {
+ mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders);
return true;
}
+
return false;
}
@Override
public List<String> getCrossProfileWidgetProviders(ComponentName admin) {
- ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (activeAdmin.crossProfileWidgetProviders == null
- || activeAdmin.crossProfileWidgetProviders.isEmpty()) {
- return null;
- }
- if (Binder.getCallingUid() == Process.myUid()) {
- return new ArrayList<>(activeAdmin.crossProfileWidgetProviders);
- } else {
- return activeAdmin.crossProfileWidgetProviders;
+ synchronized (this) {
+ ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (activeAdmin.crossProfileWidgetProviders == null
+ || activeAdmin.crossProfileWidgetProviders.isEmpty()) {
+ return null;
+ }
+ if (Binder.getCallingUid() == Process.myUid()) {
+ return new ArrayList<>(activeAdmin.crossProfileWidgetProviders);
+ } else {
+ return activeAdmin.crossProfileWidgetProviders;
+ }
}
}
@@ -3417,8 +3465,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
- mUserManager.setUserRestrictions(new Bundle(),
- new UserHandle(UserHandle.USER_OWNER));
+ clearUserRestrictions(new UserHandle(UserHandle.USER_OWNER));
if (mDeviceOwner != null) {
mDeviceOwner.clearDeviceOwner();
mDeviceOwner.writeOwnerFile();
@@ -3481,7 +3528,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
- mUserManager.setUserRestrictions(new Bundle(), callingUser);
+ clearUserRestrictions(callingUser);
if (mDeviceOwner != null) {
mDeviceOwner.removeProfileOwner(callingUser.getIdentifier());
mDeviceOwner.writeOwnerFile();
@@ -3492,6 +3539,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ private void clearUserRestrictions(UserHandle userHandle) {
+ AudioManager audioManager =
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ Bundle userRestrictions = mUserManager.getUserRestrictions();
+ mUserManager.setUserRestrictions(new Bundle(), userHandle);
+ if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
+ audioManager.setMasterMute(false);
+ }
+ if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
+ audioManager.setMicrophoneMute(false);
+ }
+ }
+
@Override
public boolean hasUserSetupCompleted() {
if (!mHasFeature) {
@@ -4030,11 +4090,66 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who == null) {
throw new NullPointerException("ComponentName is null");
}
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin activeAdmin =
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ boolean isDeviceOwner = isDeviceOwner(activeAdmin.info.getPackageName());
+ if (!isDeviceOwner && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
+ throw new SecurityException("Profile owners cannot set user restriction " + key);
+ }
long id = Binder.clearCallingIdentity();
try {
+ AudioManager audioManager =
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ boolean alreadyRestricted = mUserManager.hasUserRestriction(key);
+
+ if (enabled && !alreadyRestricted) {
+ if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
+ userHandle.getIdentifier());
+ } else if (UserManager.DISALLOW_USB_FILE_TRANSFER.equals(key)) {
+ UsbManager manager =
+ (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+ manager.setCurrentFunction("none", false);
+ } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF,
+ userHandle.getIdentifier());
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
+ userHandle.getIdentifier());
+ } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
+ Settings.Global.putStringForUser(mContext.getContentResolver(),
+ Settings.Global.ADB_ENABLED, "0", userHandle.getIdentifier());
+ } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
+ Settings.Global.putStringForUser(mContext.getContentResolver(),
+ Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
+ userHandle.getIdentifier());
+ Settings.Global.putStringForUser(mContext.getContentResolver(),
+ Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
+ userHandle.getIdentifier());
+ } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
+ userHandle.getIdentifier());
+ } else if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+ audioManager.setMicrophoneMute(true);
+ } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+ audioManager.setMasterMute(true);
+ }
+ }
+
mUserManager.setUserRestriction(key, enabled, userHandle);
+
+ if (!enabled && alreadyRestricted) {
+ if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+ audioManager.setMicrophoneMute(false);
+ } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+ audioManager.setMasterMute(false);
+ }
+ }
+
} finally {
restoreCallingIdentity(id);
}
@@ -4378,44 +4493,39 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* This function can only be called by the device owner.
* @param components The list of components allowed to enter lock task mode.
*/
- public void setLockTaskPackages(String[] packages) throws SecurityException {
- // Get the package names of the caller.
- int uid = Binder.getCallingUid();
- String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
-
+ public void setLockTaskPackages(ComponentName who, String[] packages) throws SecurityException {
synchronized (this) {
- // Check whether any of the package name is the device owner.
- for (int i=0; i<packageNames.length; i++) {
- String packageName = packageNames[i];
- int userHandle = UserHandle.getUserId(uid);
- if (isDeviceOwner(packageName)) {
-
- // If a package name is the device owner,
- // we update the component list.
- DevicePolicyData policy = getUserData(userHandle);
- policy.mLockTaskPackages.clear();
- if (packages != null) {
- for (int j = 0; j < packages.length; j++) {
- String pkg = packages[j];
- policy.mLockTaskPackages.add(pkg);
- }
- }
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- // Store the settings persistently.
- saveSettingsLocked(userHandle);
- return;
+ int userHandle = Binder.getCallingUserHandle().getIdentifier();
+ DevicePolicyData policy = getUserData(userHandle);
+ policy.mLockTaskPackages.clear();
+ if (packages != null) {
+ for (int j = 0; j < packages.length; j++) {
+ String pkg = packages[j];
+ policy.mLockTaskPackages.add(pkg);
}
}
+
+ // Store the settings persistently.
+ saveSettingsLocked(userHandle);
}
- throw new SecurityException();
}
/**
* This function returns the list of components allowed to start the task lock mode.
*/
- public String[] getLockTaskPackages() {
+ public String[] getLockTaskPackages(ComponentName who) {
synchronized (this) {
- int userHandle = UserHandle.USER_OWNER;
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ int userHandle = Binder.getCallingUserHandle().getIdentifier();
DevicePolicyData policy = getUserData(userHandle);
return policy.mLockTaskPackages.toArray(new String[0]);
}
@@ -4453,15 +4563,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
final DevicePolicyData policy = getUserData(userHandle);
Bundle adminExtras = new Bundle();
- adminExtras.putBoolean(DeviceAdminReceiver.EXTRA_LOCK_TASK_ENTERING, isEnabled);
adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg);
for (ActiveAdmin admin : policy.mAdminList) {
boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
boolean ownsProfile = (getProfileOwner(userHandle) != null
&& getProfileOwner(userHandle).equals(admin.info.getPackageName()));
if (ownsDevice || ownsProfile) {
- sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_CHANGED,
- adminExtras, null);
+ if (isEnabled) {
+ sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_ENTERING,
+ adminExtras, null);
+ } else {
+ sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_EXITING);
+ }
}
}
}
@@ -4591,26 +4704,51 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private final class LocalService extends DevicePolicyManagerInternal {
+ private List<OnCrossProfileWidgetProvidersChangeListener> mWidgetProviderListeners;
+
@Override
public List<String> getCrossProfileWidgetProviders(int profileId) {
- ComponentName ownerComponent = mDeviceOwner.getProfileOwnerComponent(profileId);
- if (ownerComponent == null) {
- return Collections.emptyList();
- }
+ synchronized (DevicePolicyManagerService.this) {
+ ComponentName ownerComponent = mDeviceOwner.getProfileOwnerComponent(profileId);
+ if (ownerComponent == null) {
+ return Collections.emptyList();
+ }
- DevicePolicyData policy = getUserData(profileId);
- ActiveAdmin admin = policy.mAdminMap.get(ownerComponent);
+ DevicePolicyData policy = getUserData(profileId);
+ ActiveAdmin admin = policy.mAdminMap.get(ownerComponent);
- if (admin == null) {
- return Collections.emptyList();
+ if (admin == null || admin.crossProfileWidgetProviders == null
+ || admin.crossProfileWidgetProviders.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ return admin.crossProfileWidgetProviders;
}
+ }
- if (admin.crossProfileWidgetProviders == null
- || admin.crossProfileWidgetProviders.isEmpty()) {
- return Collections.emptyList();
+ @Override
+ public void addOnCrossProfileWidgetProvidersChangeListener(
+ OnCrossProfileWidgetProvidersChangeListener listener) {
+ synchronized (DevicePolicyManagerService.this) {
+ if (mWidgetProviderListeners == null) {
+ mWidgetProviderListeners = new ArrayList<>();
+ }
+ if (!mWidgetProviderListeners.contains(listener)) {
+ mWidgetProviderListeners.add(listener);
+ }
}
+ }
- return admin.crossProfileWidgetProviders;
+ private void notifyCrossProfileProvidersChanged(int userId, List<String> packages) {
+ final List<OnCrossProfileWidgetProvidersChangeListener> listeners;
+ synchronized (DevicePolicyManagerService.this) {
+ listeners = new ArrayList<>(mWidgetProviderListeners);
+ }
+ final int listenerCount = listeners.size();
+ for (int i = 0; i < listenerCount; i++) {
+ OnCrossProfileWidgetProvidersChangeListener listener = listeners.get(i);
+ listener.onCrossProfileWidgetProvidersChanged(userId, packages);
+ }
}
}
}
diff --git a/services/print/Android.mk b/services/print/Android.mk
index 33604b7..00eb2e4 100644
--- a/services/print/Android.mk
+++ b/services/print/Android.mk
@@ -7,4 +7,6 @@ LOCAL_MODULE := services.print
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 7400dde..6a56de0 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -783,7 +783,9 @@ public final class PrintManagerService extends SystemService {
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
- .setShowWhen(true);
+ .setShowWhen(true)
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color));
NotificationManager notificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/services/restrictions/Android.mk b/services/restrictions/Android.mk
index fcf8626..57d1c46 100644
--- a/services/restrictions/Android.mk
+++ b/services/restrictions/Android.mk
@@ -7,4 +7,6 @@ LOCAL_MODULE := services.restrictions
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usage/Android.mk b/services/usage/Android.mk
index d4b7fa8..f1cbe98 100644
--- a/services/usage/Android.mk
+++ b/services/usage/Android.mk
@@ -7,4 +7,6 @@ LOCAL_MODULE := services.usage
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index c0923ca..c078cb2 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -732,6 +732,8 @@ public class UsbDeviceManager {
"com.android.settings.UsbSettings"));
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
intent, 0, null, UserHandle.CURRENT);
+ notification.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
notification.setLatestEventInfo(mContext, title, message, pi);
notification.visibility = Notification.VISIBILITY_PUBLIC;
mNotificationManager.notifyAsUser(null, id, notification,
@@ -768,6 +770,8 @@ public class UsbDeviceManager {
"com.android.settings.DevelopmentSettings"));
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
intent, 0, null, UserHandle.CURRENT);
+ notification.color = mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color);
notification.setLatestEventInfo(mContext, title, message, pi);
notification.visibility = Notification.VISIBILITY_PUBLIC;
mAdbNotificationShown = true;
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index bcbae60..5860fc7 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -314,14 +314,22 @@ public class UsbHostManager {
mConnectedUsbCard = cardsParser.getNumCardRecords() - 1;
mConnectedUsbDeviceNum = 0;
- if (!waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
- return;
- }
-
mConnectedHasPlayback = devicesParser.hasPlaybackDevices(mConnectedUsbCard);
mConnectedHasCapture = devicesParser.hasCaptureDevices(mConnectedUsbCard);
mConnectedHasMIDI = devicesParser.hasMIDIDevices(mConnectedUsbCard);
+ // Playback device file needed/present?
+ if (mConnectedHasPlayback &&
+ !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
+ return;
+ }
+
+ // Capture device file needed/present?
+ if (mConnectedHasCapture &&
+ !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, true)) {
+ return;
+ }
+
if (DEBUG_AUDIO) {
Slog.d(TAG,
"usb: hasPlayback:" + mConnectedHasPlayback + " hasCapture:" + mConnectedHasCapture);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index cd3684b..a173a5c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -132,14 +132,33 @@ public class VoiceInteractionManagerService extends SystemService {
public void initForUser(int userHandle) {
if (DEBUG) Slog.i(TAG, "initForUser user=" + userHandle);
- ComponentName curInteractor = getCurInteractor(userHandle);
+ String curInteractorStr = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle);
ComponentName curRecognizer = getCurRecognizer(userHandle);
+ VoiceInteractionServiceInfo curInteractorInfo = null;
+ if (curInteractorStr == null && curRecognizer != null) {
+ // If there is no interactor setting, that means we are upgrading
+ // from an older platform version. If the current recognizer is not
+ // set or matches the preferred recognizer, then we want to upgrade
+ // the user to have the default voice interaction service enabled.
+ curInteractorInfo = findAvailInteractor(userHandle, curRecognizer);
+ if (curInteractorInfo != null) {
+ // Looks good! We'll apply this one. To make it happen, we clear the
+ // recognizer so that we don't think we have anything set and will
+ // re-apply the settings.
+ curRecognizer = null;
+ }
+ }
+
if (curRecognizer != null) {
// If we already have at least a recognizer, then we probably want to
// leave things as they are... unless something has disappeared.
IPackageManager pm = AppGlobals.getPackageManager();
ServiceInfo interactorInfo = null;
ServiceInfo recognizerInfo = null;
+ ComponentName curInteractor = !TextUtils.isEmpty(curInteractorStr)
+ ? ComponentName.unflattenFromString(curInteractorStr) : null;
try {
recognizerInfo = pm.getServiceInfo(curRecognizer, 0, userHandle);
if (curInteractor != null) {
@@ -154,32 +173,27 @@ public class VoiceInteractionManagerService extends SystemService {
}
// Initializing settings, look for an interactor first.
- curInteractor = findAvailInteractor(userHandle);
- if (curInteractor != null) {
- try {
- VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo(
- mContext.getPackageManager(), curInteractor, userHandle);
- if (info.getParseError() == null) {
- setCurInteractor(curInteractor, userHandle);
- if (info.getRecognitionService() != null) {
- // Eventually it will be an error to not specify this.
- curRecognizer = new ComponentName(info.getServiceInfo().packageName,
- info.getRecognitionService());
- setCurRecognizer(curRecognizer, userHandle);
- return;
- }
- } else {
- Slog.w(TAG, "Bad interaction service " + curInteractor + ": "
- + info.getParseError());
- }
- } catch (PackageManager.NameNotFoundException e) {
- } catch (RemoteException e) {
+ if (curInteractorInfo == null) {
+ curInteractorInfo = findAvailInteractor(userHandle, null);
+ }
+ if (curInteractorInfo != null) {
+ // Eventually it will be an error to not specify this.
+ setCurInteractor(new ComponentName(curInteractorInfo.getServiceInfo().packageName,
+ curInteractorInfo.getServiceInfo().name), userHandle);
+ if (curInteractorInfo.getRecognitionService() != null) {
+ setCurRecognizer(
+ new ComponentName(curInteractorInfo.getServiceInfo().packageName,
+ curInteractorInfo.getRecognitionService()), userHandle);
+ return;
}
}
// No voice interactor, we'll just set up a simple recognizer.
curRecognizer = findAvailRecognizer(null, userHandle);
if (curRecognizer != null) {
+ if (curInteractorInfo == null) {
+ setCurInteractor(null, userHandle);
+ }
setCurRecognizer(curRecognizer, userHandle);
}
}
@@ -234,7 +248,7 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- ComponentName findAvailInteractor(int userHandle) {
+ VoiceInteractionServiceInfo findAvailInteractor(int userHandle, ComponentName recognizer) {
List<ResolveInfo> available =
mContext.getPackageManager().queryIntentServicesAsUser(
new Intent(VoiceInteractionService.SERVICE_INTERFACE), 0, userHandle);
@@ -246,23 +260,41 @@ public class VoiceInteractionManagerService extends SystemService {
} else {
// Find first system package. We never want to allow third party services to
// be automatically selected, because those require approval of the user.
- ServiceInfo serviceInfo = null;
+ VoiceInteractionServiceInfo foundInfo = null;
for (int i=0; i<numAvailable; i++) {
ServiceInfo cur = available.get(i).serviceInfo;
if ((cur.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
- if (serviceInfo == null) {
- serviceInfo = cur;
- } else {
- Slog.w(TAG, "more than one voice interaction service, picking first "
- + new ComponentName(serviceInfo.packageName, serviceInfo.name)
- + " over "
- + new ComponentName(cur.packageName, cur.name));
+ ComponentName comp = new ComponentName(cur.packageName, cur.name);
+ try {
+ VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo(
+ mContext.getPackageManager(), comp, userHandle);
+ if (info.getParseError() == null) {
+ if (recognizer == null || info.getServiceInfo().packageName.equals(
+ recognizer.getPackageName())) {
+ if (foundInfo == null) {
+ foundInfo = info;
+ } else {
+ Slog.w(TAG, "More than one voice interaction service, "
+ + "picking first "
+ + new ComponentName(
+ foundInfo.getServiceInfo().packageName,
+ foundInfo.getServiceInfo().name)
+ + " over "
+ + new ComponentName(cur.packageName, cur.name));
+ }
+ }
+ } else {
+ Slog.w(TAG, "Bad interaction service " + comp + ": "
+ + info.getParseError());
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Failure looking up interaction service " + comp);
+ } catch (RemoteException e) {
}
}
}
- return serviceInfo != null ?
- new ComponentName(serviceInfo.packageName, serviceInfo.name) : null;
+ return foundInfo;
}
}