summaryrefslogtreecommitdiffstats
path: root/services/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java')
-rw-r--r--services/java/Android.mk4
-rw-r--r--services/java/com/android/server/AlarmManagerService.java27
-rw-r--r--services/java/com/android/server/AppWidgetService.java11
-rw-r--r--services/java/com/android/server/BackupManagerService.java132
-rw-r--r--services/java/com/android/server/BatteryService.java34
-rw-r--r--services/java/com/android/server/BootReceiver.java4
-rw-r--r--services/java/com/android/server/ConnectivityService.java444
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java23
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java62
-rw-r--r--services/java/com/android/server/DropBoxManagerService.java11
-rw-r--r--services/java/com/android/server/EntropyService.java1
-rw-r--r--services/java/com/android/server/InputApplication.java (renamed from services/java/com/android/server/status/StatusBarException.java)22
-rw-r--r--services/java/com/android/server/InputDevice.java1025
-rw-r--r--services/java/com/android/server/InputManager.java536
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java65
-rw-r--r--services/java/com/android/server/InputWindow.java83
-rw-r--r--services/java/com/android/server/InputWindowList.java89
-rw-r--r--services/java/com/android/server/Installer.java78
-rw-r--r--services/java/com/android/server/IntentResolver.java26
-rw-r--r--services/java/com/android/server/KeyInputQueue.java1386
-rw-r--r--services/java/com/android/server/LoadAverageService.java2
-rw-r--r--services/java/com/android/server/LocationManagerService.java444
-rw-r--r--services/java/com/android/server/MasterClearReceiver.java27
-rw-r--r--services/java/com/android/server/MountService.java843
-rw-r--r--services/java/com/android/server/NativeDaemonConnector.java26
-rw-r--r--services/java/com/android/server/NetworkManagementService.java381
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java162
-rw-r--r--services/java/com/android/server/PackageManagerService.java1644
-rw-r--r--services/java/com/android/server/PowerManagerService.java924
-rw-r--r--services/java/com/android/server/ProcessStats.java364
-rw-r--r--services/java/com/android/server/SensorService.java282
-rw-r--r--services/java/com/android/server/StatusBarManagerService.java471
-rw-r--r--services/java/com/android/server/SystemServer.java86
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java20
-rw-r--r--services/java/com/android/server/UiModeManagerService.java2
-rw-r--r--services/java/com/android/server/UsbObserver.java207
-rwxr-xr-xservices/java/com/android/server/VibratorService.java24
-rw-r--r--services/java/com/android/server/ViewServer.java208
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java10
-rw-r--r--services/java/com/android/server/Watchdog.java429
-rw-r--r--services/java/com/android/server/WifiService.java303
-rw-r--r--services/java/com/android/server/WindowManagerService.java2854
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java6572
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java (renamed from services/java/com/android/server/am/HistoryRecord.java)150
-rw-r--r--services/java/com/android/server/am/ActivityResult.java4
-rw-r--r--services/java/com/android/server/am/ActivityStack.java3557
-rw-r--r--services/java/com/android/server/am/AppErrorDialog.java3
-rw-r--r--services/java/com/android/server/am/AppNotRespondingDialog.java2
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java111
-rw-r--r--services/java/com/android/server/am/BroadcastRecord.java68
-rw-r--r--services/java/com/android/server/am/ConnectionRecord.java4
-rw-r--r--services/java/com/android/server/am/ContentProviderRecord.java4
-rw-r--r--services/java/com/android/server/am/LaunchWarningWindow.java36
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java6
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java65
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java173
-rw-r--r--services/java/com/android/server/am/StrictModeViolationDialog.java98
-rw-r--r--services/java/com/android/server/am/UriPermission.java54
-rw-r--r--services/java/com/android/server/am/UriPermissionOwner.java166
-rw-r--r--services/java/com/android/server/am/UsageStatsService.java131
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java35
-rw-r--r--services/java/com/android/server/location/GeocoderProxy.java126
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java1621
-rw-r--r--services/java/com/android/server/location/GpsXtraDownloader.java172
-rw-r--r--services/java/com/android/server/location/LocationProviderInterface.java57
-rw-r--r--services/java/com/android/server/location/LocationProviderProxy.java471
-rw-r--r--services/java/com/android/server/location/MockProvider.java234
-rw-r--r--services/java/com/android/server/location/PassiveProvider.java153
-rw-r--r--services/java/com/android/server/status/AnimatedImageView.java85
-rw-r--r--services/java/com/android/server/status/CloseDragHandle.java51
-rw-r--r--services/java/com/android/server/status/DateView.java89
-rw-r--r--services/java/com/android/server/status/ExpandedView.java58
-rw-r--r--services/java/com/android/server/status/FixedSizeDrawable.java66
-rw-r--r--services/java/com/android/server/status/IconData.java122
-rw-r--r--services/java/com/android/server/status/IconMerger.java133
-rw-r--r--services/java/com/android/server/status/LatestItemView.java34
-rw-r--r--services/java/com/android/server/status/NotificationData.java44
-rw-r--r--services/java/com/android/server/status/NotificationLinearLayout.java29
-rw-r--r--services/java/com/android/server/status/NotificationViewList.java276
-rw-r--r--services/java/com/android/server/status/StatusBarIcon.java186
-rw-r--r--services/java/com/android/server/status/StatusBarNotification.java27
-rw-r--r--services/java/com/android/server/status/StatusBarPolicy.java1390
-rw-r--r--services/java/com/android/server/status/StatusBarService.java1884
-rw-r--r--services/java/com/android/server/status/StatusBarView.java192
-rw-r--r--services/java/com/android/server/status/StorageNotification.java395
-rw-r--r--services/java/com/android/server/status/Ticker.java246
-rw-r--r--services/java/com/android/server/status/TickerView.java38
-rw-r--r--services/java/com/android/server/status/TrackingPatternView.java70
-rw-r--r--services/java/com/android/server/status/TrackingView.java63
-rw-r--r--services/java/com/android/server/status/UsbStorageActivity.java272
-rwxr-xr-xservices/java/com/android/server/status/package.html5
91 files changed, 16341 insertions, 17263 deletions
diff --git a/services/java/Android.mk b/services/java/Android.mk
index 934712c..c756d29 100644
--- a/services/java/Android.mk
+++ b/services/java/Android.mk
@@ -13,7 +13,9 @@ LOCAL_MODULE:= services
LOCAL_JAVA_LIBRARIES := android.policy
+LOCAL_NO_EMMA_INSTRUMENT := true
+LOCAL_NO_EMMA_COMPILE := true
+
include $(BUILD_JAVA_LIBRARY)
include $(BUILD_DROIDDOC)
-
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 8852cc3..4931cc7 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -38,9 +38,11 @@ import android.text.TextUtils;
import android.text.format.Time;
import android.util.EventLog;
import android.util.Slog;
+import android.util.TimeUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@@ -464,25 +466,28 @@ class AlarmManagerService extends IAlarmManager.Stub {
synchronized (mLock) {
pw.println("Current Alarm Manager state:");
if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
+ final long now = System.currentTimeMillis();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
pw.println(" ");
pw.print(" Realtime wakeup (now=");
- pw.print(System.currentTimeMillis()); pw.println("):");
+ pw.print(sdf.format(new Date(now))); pw.println("):");
if (mRtcWakeupAlarms.size() > 0) {
- dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP");
+ dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP", now);
}
if (mRtcAlarms.size() > 0) {
- dumpAlarmList(pw, mRtcAlarms, " ", "RTC");
+ dumpAlarmList(pw, mRtcAlarms, " ", "RTC", now);
}
}
if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
+ final long now = SystemClock.elapsedRealtime();
pw.println(" ");
pw.print(" Elapsed realtime wakeup (now=");
- pw.print(SystemClock.elapsedRealtime()); pw.println("):");
+ TimeUtils.formatDuration(now, pw); pw.println("):");
if (mElapsedRealtimeWakeupAlarms.size() > 0) {
- dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP");
+ dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP", now);
}
if (mElapsedRealtimeAlarms.size() > 0) {
- dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED");
+ dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now);
}
}
@@ -507,12 +512,13 @@ class AlarmManagerService extends IAlarmManager.Stub {
}
}
- private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, String label) {
+ private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
+ String prefix, String label, long now) {
for (int i=list.size()-1; i>=0; i--) {
Alarm a = list.get(i);
pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
pw.print(": "); pw.println(a);
- a.dump(pw, prefix + " ");
+ a.dump(pw, prefix + " ", now);
}
}
@@ -627,10 +633,9 @@ class AlarmManagerService extends IAlarmManager.Stub {
return sb.toString();
}
- public void dump(PrintWriter pw, String prefix)
- {
+ public void dump(PrintWriter pw, String prefix, long now) {
pw.print(prefix); pw.print("type="); pw.print(type);
- pw.print(" when="); pw.print(when);
+ pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
pw.print(" repeatInterval="); pw.print(repeatInterval);
pw.print(" count="); pw.println(count);
pw.print(prefix); pw.print("operation="); pw.println(operation);
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index dc5fd30..731fb22 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
@@ -1099,7 +1100,7 @@ class AppWidgetService extends IAppWidgetService.Stub
if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
added = true;
- } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
added = false;
} else {
@@ -1160,7 +1161,9 @@ class AppWidgetService extends IAppWidgetService.Stub
for (int i=0; i<N; i++) {
ResolveInfo ri = broadcastReceivers.get(i);
ActivityInfo ai = ri.activityInfo;
-
+ if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ continue;
+ }
if (pkgName.equals(ai.packageName)) {
addProviderLocked(ri);
}
@@ -1179,6 +1182,9 @@ class AppWidgetService extends IAppWidgetService.Stub
for (int i=0; i<N; i++) {
ResolveInfo ri = broadcastReceivers.get(i);
ActivityInfo ai = ri.activityInfo;
+ if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ continue;
+ }
if (pkgName.equals(ai.packageName)) {
ComponentName component = new ComponentName(ai.packageName, ai.name);
Provider p = lookupProviderLocked(component);
@@ -1204,6 +1210,7 @@ class AppWidgetService extends IAppWidgetService.Stub
// If it's currently showing, call back with the new AppWidgetProviderInfo.
for (int j=0; j<M; j++) {
AppWidgetId id = p.instances.get(j);
+ id.views = null;
if (id.host != null && id.host.callbacks != null) {
try {
id.host.callbacks.providerChanged(id.appWidgetId, p.info);
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6e307a5..a7e02a5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -17,8 +17,8 @@
package com.android.server;
import android.app.ActivityManagerNative;
-import android.app.ActivityThread;
import android.app.AlarmManager;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IBackupAgent;
@@ -54,6 +54,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -399,7 +400,7 @@ class BackupManagerService extends IBackupManager.Stub {
public BackupManagerService(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
- mPackageManagerBinder = ActivityThread.getPackageManager();
+ mPackageManagerBinder = AppGlobals.getPackageManager();
mActivityManager = ActivityManagerNative.getDefault();
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -421,7 +422,7 @@ class BackupManagerService extends IBackupManager.Stub {
Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
// If Encrypted file systems is enabled or disabled, this call will return the
// correct directory.
- mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
+ mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
mBaseStateDir.mkdirs();
mDataDir = Environment.getDownloadCacheDirectory();
@@ -504,7 +505,7 @@ class BackupManagerService extends IBackupManager.Stub {
parseLeftoverJournals();
// Power management
- mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
+ mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
// Start the backup passes going
setBackupEnabled(areEnabled);
@@ -663,12 +664,12 @@ class BackupManagerService extends IBackupManager.Stub {
// backup.
RandomAccessFile in = null;
try {
- Slog.i(TAG, "Found stale backup journal, scheduling:");
+ Slog.i(TAG, "Found stale backup journal, scheduling");
in = new RandomAccessFile(f, "r");
while (true) {
String packageName = in.readUTF();
- Slog.i(TAG, " + " + packageName);
- dataChanged(packageName);
+ Slog.i(TAG, " " + packageName);
+ dataChangedImpl(packageName);
}
} catch (EOFException e) {
// no more data; we're done
@@ -740,7 +741,7 @@ class BackupManagerService extends IBackupManager.Stub {
int uid = mBackupParticipants.keyAt(i);
HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
for (ApplicationInfo app: participants) {
- dataChanged(app.packageName);
+ dataChangedImpl(app.packageName);
}
}
}
@@ -896,7 +897,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (!mEverStoredApps.contains(pkg.packageName)) {
if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
+ " never backed up; scheduling");
- dataChanged(pkg.packageName);
+ dataChangedImpl(pkg.packageName);
}
}
}
@@ -1327,7 +1328,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (status != BackupConstants.TRANSPORT_OK) {
Slog.w(TAG, "Backup pass unsuccessful, restaging");
for (BackupRequest req : mQueue) {
- dataChanged(req.appInfo.packageName);
+ dataChangedImpl(req.appInfo.packageName);
}
// We also want to reset the backup schedule based on whatever
@@ -1363,6 +1364,7 @@ class BackupManagerService extends IBackupManager.Stub {
? IApplicationThread.BACKUP_MODE_FULL
: IApplicationThread.BACKUP_MODE_INCREMENTAL;
try {
+ mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
agent = bindToAgentSynchronous(request.appInfo, mode);
if (agent != null) {
int result = processOneBackup(request, agent, transport);
@@ -1378,6 +1380,8 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ mWakelock.setWorkSource(null);
+
return BackupConstants.TRANSPORT_OK;
}
@@ -1997,25 +2001,66 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ private void dataChangedImpl(String packageName) {
+ HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
+ dataChangedImpl(packageName, targets);
+ }
- // ----- IBackupManager binder interface -----
-
- public void dataChanged(String packageName) {
+ private void dataChangedImpl(String packageName, HashSet<ApplicationInfo> targets) {
// Record that we need a backup pass for the caller. Since multiple callers
// may share a uid, we need to note all candidates within that uid and schedule
// a backup pass for each of them.
EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
+ if (targets == null) {
+ Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mQueueLock) {
+ // Note that this client has made data changes that need to be backed up
+ for (ApplicationInfo app : targets) {
+ // validate the caller-supplied package name against the known set of
+ // packages associated with this uid
+ if (app.packageName.equals(packageName)) {
+ // Add the caller to the set of pending backups. If there is
+ // one already there, then overwrite it, but no harm done.
+ BackupRequest req = new BackupRequest(app, false);
+ if (mPendingBackups.put(app, req) == null) {
+ // Journal this request in case of crash. The put()
+ // operation returned null when this package was not already
+ // in the set; we want to avoid touching the disk redundantly.
+ writeToJournalLocked(packageName);
+
+ if (DEBUG) {
+ int numKeys = mPendingBackups.size();
+ Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
+ for (BackupRequest b : mPendingBackups.values()) {
+ Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Note: packageName is currently unused, but may be in the future
+ private HashSet<ApplicationInfo> dataChangedTargets(String packageName) {
// If the caller does not hold the BACKUP permission, it can only request a
// backup of its own data.
- HashSet<ApplicationInfo> targets;
if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
- targets = mBackupParticipants.get(Binder.getCallingUid());
- } else {
- // a caller with full permission can ask to back up any participating app
- // !!! TODO: allow backup of ANY app?
- targets = new HashSet<ApplicationInfo>();
+ synchronized (mBackupParticipants) {
+ return mBackupParticipants.get(Binder.getCallingUid());
+ }
+ }
+
+ // a caller with full permission can ask to back up any participating app
+ // !!! TODO: allow backup of ANY app?
+ HashSet<ApplicationInfo> targets = new HashSet<ApplicationInfo>();
+ synchronized (mBackupParticipants) {
int N = mBackupParticipants.size();
for (int i = 0; i < N; i++) {
HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
@@ -2024,37 +2069,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
}
- if (targets != null) {
- synchronized (mQueueLock) {
- // Note that this client has made data changes that need to be backed up
- for (ApplicationInfo app : targets) {
- // validate the caller-supplied package name against the known set of
- // packages associated with this uid
- if (app.packageName.equals(packageName)) {
- // Add the caller to the set of pending backups. If there is
- // one already there, then overwrite it, but no harm done.
- BackupRequest req = new BackupRequest(app, false);
- if (mPendingBackups.put(app, req) == null) {
- // Journal this request in case of crash. The put()
- // operation returned null when this package was not already
- // in the set; we want to avoid touching the disk redundantly.
- writeToJournalLocked(packageName);
-
- if (DEBUG) {
- int numKeys = mPendingBackups.size();
- Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
- for (BackupRequest b : mPendingBackups.values()) {
- Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
- }
- }
- }
- }
- }
- }
- } else {
- Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
- + " uid=" + Binder.getCallingUid());
- }
+ return targets;
}
private void writeToJournalLocked(String str) {
@@ -2072,6 +2087,23 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // ----- IBackupManager binder interface -----
+
+ public void dataChanged(final String packageName) {
+ final HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
+ if (targets == null) {
+ Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ mBackupHandler.post(new Runnable() {
+ public void run() {
+ dataChangedImpl(packageName, targets);
+ }
+ });
+ }
+
// Clear the given package's backup data from the current transport
public void clearBackupData(String packageName) {
if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 5cf61bd..fc4e06f 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -26,6 +26,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
import android.os.Binder;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.DropBoxManager;
import android.os.RemoteException;
@@ -210,9 +211,6 @@ class BatteryService extends Binder {
boolean logOutlier = false;
long dischargeDuration = 0;
- shutdownIfNoPower();
- shutdownIfOverTemp();
-
mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
if (mAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
@@ -221,6 +219,19 @@ class BatteryService extends Binder {
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}
+
+ // Let the battery stats keep track of the current level.
+ try {
+ mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
+ mPlugType, mBatteryLevel, mBatteryTemperature,
+ mBatteryVoltage);
+ } catch (RemoteException e) {
+ // Should never happen.
+ }
+
+ shutdownIfNoPower();
+ shutdownIfOverTemp();
+
if (mBatteryStatus != mLastBatteryStatus ||
mBatteryHealth != mLastBatteryHealth ||
mBatteryPresent != mLastBatteryPresent ||
@@ -263,16 +274,6 @@ class BatteryService extends Binder {
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
}
- if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) {
- // If the battery level has changed and we are on battery, update the current level.
- // This is used for discharge cycle tracking so this shouldn't be updated while the
- // battery is charging.
- try {
- mBatteryStats.recordCurrentLevel(mBatteryLevel);
- } catch (RemoteException e) {
- // Should never happen.
- }
- }
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
@@ -342,11 +343,6 @@ class BatteryService extends Binder {
Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- try {
- mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel);
- } catch (RemoteException e) {
- // Should never happen.
- }
int icon = getIcon(mBatteryLevel);
@@ -389,7 +385,7 @@ class BatteryService extends Binder {
dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump");
dumpStream = new FileOutputStream(dumpFile);
batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
- dumpStream.getFD().sync();
+ FileUtils.sync(dumpStream);
// add dump file to drop box
db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java
index f409751..b9ff8d0 100644
--- a/services/java/com/android/server/BootReceiver.java
+++ b/services/java/com/android/server/BootReceiver.java
@@ -165,7 +165,9 @@ public class BootReceiver extends BroadcastReceiver {
if (prefs != null) {
long lastTime = prefs.getLong(filename, 0);
if (lastTime == fileTime) return; // Already logged this particular file
- prefs.edit().putLong(filename, fileTime).commit();
+ // 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 + ")");
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 9edce20..7c9b547 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -49,6 +49,7 @@ import com.android.server.connectivity.Tethering;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.GregorianCalendar;
import java.util.List;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -58,7 +59,7 @@ import java.net.UnknownHostException;
*/
public class ConnectivityService extends IConnectivityManager.Stub {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final String TAG = "ConnectivityService";
// how long to wait before switching back to a radio's default network
@@ -92,12 +93,75 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private Context mContext;
private int mNetworkPreference;
private int mActiveDefaultNetwork = -1;
+ // 0 is full bad, 100 is full good
+ private int mDefaultInetCondition = 0;
+ private int mDefaultInetConditionPublished = 0;
+ private boolean mInetConditionChangeInFlight = false;
+ private int mDefaultConnectionSequence = 0;
private int mNumDnsEntries;
private boolean mTestMode;
private static ConnectivityService sServiceInstance;
+ private static final int ENABLED = 1;
+ private static final int DISABLED = 0;
+
+ // Share the event space with NetworkStateTracker (which can't see this
+ // internal class but sends us events). If you change these, change
+ // NetworkStateTracker.java too.
+ private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
+ private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
+
+ /**
+ * used internally as a delayed event to make us switch back to the
+ * default network
+ */
+ private static final int EVENT_RESTORE_DEFAULT_NETWORK =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 1;
+
+ /**
+ * used internally to change our mobile data enabled flag
+ */
+ private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 2;
+
+ /**
+ * used internally to change our network preference setting
+ * arg1 = networkType to prefer
+ */
+ private static final int EVENT_SET_NETWORK_PREFERENCE =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 3;
+
+ /**
+ * used internally to synchronize inet condition reports
+ * arg1 = networkType
+ * arg2 = condition (0 bad, 100 good)
+ */
+ private static final int EVENT_INET_CONDITION_CHANGE =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 4;
+
+ /**
+ * used internally to mark the end of inet condition hold periods
+ * arg1 = networkType
+ */
+ private static final int EVENT_INET_CONDITION_HOLD_END =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 5;
+
+ /**
+ * used internally to set the background data preference
+ * arg1 = TRUE for enabled, FALSE for disabled
+ */
+ private static final int EVENT_SET_BACKGROUND_DATA =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 6;
+
+ /**
+ * used internally to set enable/disable cellular data
+ * arg1 = ENBALED or DISABLED
+ */
+ private static final int EVENT_SET_MOBILE_DATA =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 7;
+
private Handler mHandler;
// list of DeathRecipients used to make sure features are turned off when
@@ -107,6 +171,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private boolean mSystemReady;
private Intent mInitialBroadcast;
+ // used in DBG mode to track inet condition reports
+ private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
+ private ArrayList mInetLog;
+
private static class NetworkAttributes {
/**
* Class for holding settings read from resources.
@@ -327,6 +395,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mTethering.getTetherableWifiRegexs().length != 0) &&
mTethering.getUpstreamIfaceRegexs().length != 0);
+ if (DBG) {
+ mInetLog = new ArrayList();
+ }
}
@@ -334,30 +405,36 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* Sets the preferred network.
* @param preference the new preference
*/
- public synchronized void setNetworkPreference(int preference) {
+ public void setNetworkPreference(int preference) {
enforceChangePermission();
+
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
+ }
+
+ public int getNetworkPreference() {
+ enforceAccessPermission();
+ int preference;
+ synchronized(this) {
+ preference = mNetworkPreference;
+ }
+ return preference;
+ }
+
+ private void handleSetNetworkPreference(int preference) {
if (ConnectivityManager.isNetworkTypeValid(preference) &&
mNetAttributes[preference] != null &&
mNetAttributes[preference].isDefault()) {
if (mNetworkPreference != preference) {
- persistNetworkPreference(preference);
- mNetworkPreference = preference;
+ final ContentResolver cr = mContext.getContentResolver();
+ Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, preference);
+ synchronized(this) {
+ mNetworkPreference = preference;
+ }
enforcePreference();
}
}
}
- public int getNetworkPreference() {
- enforceAccessPermission();
- return mNetworkPreference;
- }
-
- private void persistNetworkPreference(int networkPreference) {
- final ContentResolver cr = mContext.getContentResolver();
- Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
- networkPreference);
- }
-
private int getPersistedNetworkPreference() {
final ContentResolver cr = mContext.getContentResolver();
@@ -576,8 +653,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetRequestersPids[usedNetworkType].add(currentPid);
}
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
f), getRestoreDefaultNetworkDelay());
@@ -585,7 +661,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
!network.isTeardownRequested()) {
if (ni.isConnected() == true) {
// add the pid-specific dns
- handleDnsConfigurationChange();
+ handleDnsConfigurationChange(networkType);
if (DBG) Slog.d(TAG, "special network already active");
return Phone.APN_ALREADY_ACTIVE;
}
@@ -603,8 +679,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
synchronized(this) {
mFeatureUsers.add(f);
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
f), getRestoreDefaultNetworkDelay());
return network.startUsingNetworkFeature(feature,
@@ -802,15 +877,18 @@ public class ConnectivityService extends IConnectivityManager.Stub {
android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
"ConnectivityService");
- if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
-
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKGROUND_DATA,
- allowBackgroundDataUsage ? 1 : 0);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_BACKGROUND_DATA,
+ (allowBackgroundDataUsage ? ENABLED : DISABLED), 0));
+ }
- Intent broadcast = new Intent(
- ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
- mContext.sendBroadcast(broadcast);
+ private void handleSetBackgroundData(boolean enabled) {
+ if (enabled != getBackgroundDataSetting()) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.BACKGROUND_DATA, enabled ? 1 : 0);
+ Intent broadcast = new Intent(
+ ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+ mContext.sendBroadcast(broadcast);
+ }
}
/**
@@ -827,10 +905,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
/**
* @see ConnectivityManager#setMobileDataEnabled(boolean)
*/
- public synchronized void setMobileDataEnabled(boolean enabled) {
+ public void setMobileDataEnabled(boolean enabled) {
enforceChangePermission();
if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")");
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
+ (enabled ? ENABLED : DISABLED), 0));
+ }
+
+ private void handleSetMobileData(boolean enabled) {
if (getMobileDataEnabled() == enabled) return;
Settings.Secure.putInt(mContext.getContentResolver(),
@@ -838,7 +921,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (enabled) {
if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
- if (DBG) Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
+ if (DBG) {
+ Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
+ }
mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
}
} else {
@@ -920,7 +1005,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -939,13 +1023,21 @@ public class ConnectivityService extends IConnectivityManager.Stub {
newNet = tryFailover(prevNetType);
if (newNet != null) {
NetworkInfo switchTo = newNet.getNetworkInfo();
+ if (!switchTo.isConnected()) {
+ // if the other net is connected they've already reset this and perhaps even gotten
+ // a positive report we don't want to overwrite, but if not we need to clear this now
+ // to turn our cellular sig strength white
+ mDefaultInetConditionPublished = 0;
+ }
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
+ mDefaultInetConditionPublished = 0; // we're not connected anymore
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
}
+ intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
// do this before we broadcast the change
- handleConnectivityChange();
+ handleConnectivityChange(prevNetType);
sendStickyBroadcast(intent);
/*
@@ -977,10 +1069,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (mNetAttributes[checkType] == null) continue;
if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_MOBILE &&
noMobileData) {
- if (DBG) {
- Slog.d(TAG, "not failing over to mobile type " + checkType +
- " because Mobile Data Disabled");
- }
+ Slog.e(TAG, "not failing over to mobile type " + checkType +
+ " because Mobile Data Disabled");
continue;
}
if (mNetAttributes[checkType].isDefault()) {
@@ -1028,6 +1118,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
newNet = null; // not officially avail.. try anyway, but
// report no failover
}
+ } else {
+ Slog.e(TAG, "Network failover failing.");
}
}
@@ -1035,8 +1127,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
private void sendConnectedBroadcast(NetworkInfo info) {
- Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ sendGeneralBroadcast(info, ConnectivityManager.CONNECTIVITY_ACTION);
+ }
+
+ private void sendInetConditionBroadcast(NetworkInfo info) {
+ sendGeneralBroadcast(info, ConnectivityManager.INET_CONDITION_ACTION);
+ }
+
+ private void sendGeneralBroadcast(NetworkInfo info, String bcastType) {
+ Intent intent = new Intent(bcastType);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -1049,6 +1148,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
info.getExtraInfo());
}
+ intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
sendStickyBroadcast(intent);
}
@@ -1062,19 +1162,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
String reason = info.getReason();
String extraInfo = info.getExtraInfo();
- if (DBG) {
- String reasonText;
- if (reason == null) {
- reasonText = ".";
- } else {
- reasonText = " (" + reason + ").";
- }
- Slog.v(TAG, "Attempt to connect to " + info.getTypeName() +
- " failed" + reasonText);
+ String reasonText;
+ if (reason == null) {
+ reasonText = ".";
+ } else {
+ reasonText = " (" + reason + ").";
}
+ Slog.e(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
@@ -1095,15 +1191,20 @@ public class ConnectivityService extends IConnectivityManager.Stub {
newNet = tryFailover(info.getType());
if (newNet != null) {
NetworkInfo switchTo = newNet.getNetworkInfo();
+ if (!switchTo.isConnected()) {
+ // if the other net is connected they've already reset this and perhaps
+ // even gotten a positive report we don't want to overwrite, but if not
+ // we need to clear this now to turn our cellular sig strength white
+ mDefaultInetConditionPublished = 0;
+ }
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
+ mDefaultInetConditionPublished = 0;
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
}
- // do this before we broadcast the change
- handleConnectivityChange();
-
+ intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
sendStickyBroadcast(intent);
/*
* If the failover network is already connected, then immediately send
@@ -1171,10 +1272,18 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
mActiveDefaultNetwork = type;
+ // this will cause us to come up initially as unconnected and switching
+ // to connected after our normal pause unless somebody reports us as reall
+ // disconnected
+ mDefaultInetConditionPublished = 0;
+ mDefaultConnectionSequence++;
+ mInetConditionChangeInFlight = false;
+ // Don't do this - if we never sign in stay, grey
+ //reportNetworkCondition(mActiveDefaultNetwork, 100);
}
thisNet.setTeardownRequested(false);
thisNet.updateNetworkSettings();
- handleConnectivityChange();
+ handleConnectivityChange(type);
sendConnectedBroadcast(info);
}
@@ -1201,38 +1310,29 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
- * After any kind of change in the connectivity state of any network,
- * make sure that anything that depends on the connectivity state of
- * more than one network is set up correctly. We're mainly concerned
- * with making sure that the list of DNS servers is set up according
- * to which networks are connected, and ensuring that the right routing
- * table entries exist.
+ * After a change in the connectivity state of any network, We're mainly
+ * concerned with making sure that the list of DNS servers is setupup
+ * according to which networks are connected, and ensuring that the
+ * right routing table entries exist.
*/
- private void handleConnectivityChange() {
+ private void handleConnectivityChange(int netType) {
/*
* If a non-default network is enabled, add the host routes that
- * will allow it's DNS servers to be accessed. Only
- * If both mobile and wifi are enabled, add the host routes that
- * will allow MMS traffic to pass on the mobile network. But
- * remove the default route for the mobile network, so that there
- * will be only one default route, to ensure that all traffic
- * except MMS will travel via Wi-Fi.
+ * will allow it's DNS servers to be accessed.
*/
- handleDnsConfigurationChange();
+ handleDnsConfigurationChange(netType);
- for (int netType : mPriorityList) {
- if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
- if (mNetAttributes[netType].isDefault()) {
- mNetTrackers[netType].addDefaultRoute();
- } else {
- mNetTrackers[netType].addPrivateDnsRoutes();
- }
+ if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
+ if (mNetAttributes[netType].isDefault()) {
+ mNetTrackers[netType].addDefaultRoute();
} else {
- if (mNetAttributes[netType].isDefault()) {
- mNetTrackers[netType].removeDefaultRoute();
- } else {
- mNetTrackers[netType].removePrivateDnsRoutes();
- }
+ mNetTrackers[netType].addPrivateDnsRoutes();
+ }
+ } else {
+ if (mNetAttributes[netType].isDefault()) {
+ mNetTrackers[netType].removeDefaultRoute();
+ } else {
+ mNetTrackers[netType].removePrivateDnsRoutes();
}
}
}
@@ -1303,41 +1403,36 @@ public class ConnectivityService extends IConnectivityManager.Stub {
SystemProperties.set("net.dnschange", "" + (n+1));
}
- private void handleDnsConfigurationChange() {
+ private void handleDnsConfigurationChange(int netType) {
// add default net's dns entries
- for (int x = mPriorityList.length-1; x>= 0; x--) {
- int netType = mPriorityList[x];
- NetworkStateTracker nt = mNetTrackers[netType];
- if (nt != null && nt.getNetworkInfo().isConnected() &&
- !nt.isTeardownRequested()) {
- String[] dnsList = nt.getNameServers();
- if (mNetAttributes[netType].isDefault()) {
- int j = 1;
- for (String dns : dnsList) {
- if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
- if (DBG) {
- Slog.d(TAG, "adding dns " + dns + " for " +
- nt.getNetworkInfo().getTypeName());
- }
- SystemProperties.set("net.dns" + j++, dns);
+ NetworkStateTracker nt = mNetTrackers[netType];
+ if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
+ String[] dnsList = nt.getNameServers();
+ if (mNetAttributes[netType].isDefault()) {
+ int j = 1;
+ for (String dns : dnsList) {
+ if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+ if (DBG) {
+ Slog.d(TAG, "adding dns " + dns + " for " +
+ nt.getNetworkInfo().getTypeName());
}
+ SystemProperties.set("net.dns" + j++, dns);
}
- for (int k=j ; k<mNumDnsEntries; k++) {
- if (DBG) Slog.d(TAG, "erasing net.dns" + k);
- SystemProperties.set("net.dns" + k, "");
- }
- mNumDnsEntries = j;
- } else {
- // set per-pid dns for attached secondary nets
- List pids = mNetRequestersPids[netType];
- for (int y=0; y< pids.size(); y++) {
- Integer pid = (Integer)pids.get(y);
- writePidDns(dnsList, pid.intValue());
- }
+ }
+ for (int k=j ; k<mNumDnsEntries; k++) {
+ if (DBG) Slog.d(TAG, "erasing net.dns" + k);
+ SystemProperties.set("net.dns" + k, "");
+ }
+ mNumDnsEntries = j;
+ } else {
+ // set per-pid dns for attached secondary nets
+ List pids = mNetRequestersPids[netType];
+ for (int y=0; y< pids.size(); y++) {
+ Integer pid = (Integer)pids.get(y);
+ writePidDns(dnsList, pid.intValue());
}
}
}
-
bumpDns();
}
@@ -1394,6 +1489,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
pw.println();
mTethering.dump(fd, pw, args);
+
+ if (mInetLog != null) {
+ pw.println();
+ pw.println("Inet condition reports:");
+ for(int i = 0; i < mInetLog.size(); i++) {
+ pw.println(mInetLog.get(i));
+ }
+ }
}
// must be stateless - things change under us.
@@ -1468,9 +1571,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
handleNotificationChange(msg.arg1 == 1, msg.arg2,
(Notification) msg.obj);
+ break;
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
- handleDnsConfigurationChange();
+ info = (NetworkInfo) msg.obj;
+ type = info.getType();
+ handleDnsConfigurationChange(type);
break;
case NetworkStateTracker.EVENT_ROAMING_CHANGED:
@@ -1480,10 +1586,42 @@ public class ConnectivityService extends IConnectivityManager.Stub {
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
// fill me in
break;
- case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
+ case EVENT_RESTORE_DEFAULT_NETWORK:
FeatureUser u = (FeatureUser)msg.obj;
u.expire();
break;
+ case EVENT_INET_CONDITION_CHANGE:
+ {
+ int netType = msg.arg1;
+ int condition = msg.arg2;
+ handleInetConditionChange(netType, condition);
+ break;
+ }
+ case EVENT_INET_CONDITION_HOLD_END:
+ {
+ int netType = msg.arg1;
+ int sequence = msg.arg2;
+ handleInetConditionHoldEnd(netType, sequence);
+ break;
+ }
+ case EVENT_SET_NETWORK_PREFERENCE:
+ {
+ int preference = msg.arg1;
+ handleSetNetworkPreference(preference);
+ break;
+ }
+ case EVENT_SET_BACKGROUND_DATA:
+ {
+ boolean enabled = (msg.arg1 == ENABLED);
+ handleSetBackgroundData(enabled);
+ break;
+ }
+ case EVENT_SET_MOBILE_DATA:
+ {
+ boolean enabled = (msg.arg1 == ENABLED);
+ handleSetMobileData(enabled);
+ break;
+ }
}
}
}
@@ -1567,4 +1705,92 @@ public class ConnectivityService extends IConnectivityManager.Stub {
Settings.Secure.TETHER_SUPPORTED, defaultVal) != 0);
return tetherEnabledInSettings && mTetheringConfigValid;
}
+
+ // 100 percent is full good, 0 is full bad.
+ public void reportInetCondition(int networkType, int percentage) {
+ if (DBG) Slog.d(TAG, "reportNetworkCondition(" + networkType + ", " + percentage + ")");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR,
+ "ConnectivityService");
+
+ if (DBG) {
+ int pid = getCallingPid();
+ int uid = getCallingUid();
+ String s = pid + "(" + uid + ") reports inet is " +
+ (percentage > 50 ? "connected" : "disconnected") + " (" + percentage + ") on " +
+ "network Type " + networkType + " at " + GregorianCalendar.getInstance().getTime();
+ mInetLog.add(s);
+ while(mInetLog.size() > INET_CONDITION_LOG_MAX_SIZE) {
+ mInetLog.remove(0);
+ }
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+ }
+
+ private void handleInetConditionChange(int netType, int condition) {
+ if (DBG) {
+ Slog.d(TAG, "Inet connectivity change, net=" +
+ netType + ", condition=" + condition +
+ ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
+ }
+ if (mActiveDefaultNetwork == -1) {
+ if (DBG) Slog.d(TAG, "no active default network - aborting");
+ return;
+ }
+ if (mActiveDefaultNetwork != netType) {
+ if (DBG) Slog.d(TAG, "given net not default - aborting");
+ return;
+ }
+ mDefaultInetCondition = condition;
+ int delay;
+ if (mInetConditionChangeInFlight == false) {
+ if (DBG) Slog.d(TAG, "starting a change hold");
+ // setup a new hold to debounce this
+ if (mDefaultInetCondition > 50) {
+ delay = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
+ } else {
+ delay = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
+ }
+ mInetConditionChangeInFlight = true;
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_INET_CONDITION_HOLD_END,
+ mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
+ } else {
+ // we've set the new condition, when this hold ends that will get
+ // picked up
+ if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
+ }
+ }
+
+ private void handleInetConditionHoldEnd(int netType, int sequence) {
+ if (DBG) {
+ Slog.d(TAG, "Inet hold end, net=" + netType +
+ ", condition =" + mDefaultInetCondition +
+ ", published condition =" + mDefaultInetConditionPublished);
+ }
+ mInetConditionChangeInFlight = false;
+
+ if (mActiveDefaultNetwork == -1) {
+ if (DBG) Slog.d(TAG, "no active default network - aborting");
+ return;
+ }
+ if (mDefaultConnectionSequence != sequence) {
+ if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
+ return;
+ }
+ if (mDefaultInetConditionPublished == mDefaultInetCondition) {
+ if (DBG) Slog.d(TAG, "no change in condition - aborting");
+ return;
+ }
+ NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
+ if (networkInfo.isConnected() == false) {
+ if (DBG) Slog.d(TAG, "default network not connected - aborting");
+ return;
+ }
+ mDefaultInetConditionPublished = mDefaultInetCondition;
+ sendInetConditionBroadcast(networkInfo);
+ return;
+ }
}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 19d146d..1538003 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
import com.android.internal.content.PackageMonitor;
+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;
@@ -41,6 +42,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -55,6 +57,7 @@ import android.view.WindowManagerPolicy;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -70,6 +73,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final Context mContext;
final MyPackageMonitor mMonitor;
+ final PowerManager.WakeLock mWakeLock;
IPowerManager mIPowerManager;
@@ -215,6 +219,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mContext = context;
mMonitor = new MyPackageMonitor();
mMonitor.register(context, true);
+ mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE))
+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM");
}
private IPowerManager getIPowerManager() {
@@ -451,6 +457,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (XmlPullParserException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (FileNotFoundException e) {
+ // Don't be noisy, this is normal if we haven't defined any policies.
} catch (IOException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (IndexOutOfBoundsException e) {
@@ -859,10 +867,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
void wipeDataLocked(int flags) {
- try {
- RecoverySystem.rebootWipeUserData(mContext);
- } catch (IOException e) {
- Slog.w(TAG, "Failed requesting data wipe", e);
+ if ((flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0) {
+ Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
+ intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
+ mWakeLock.acquire(10000);
+ mContext.startService(intent);
+ } else {
+ try {
+ RecoverySystem.rebootWipeUserData(mContext);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed requesting data wipe", e);
+ }
}
}
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 4a0df59..0b1a4a3 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -69,10 +69,12 @@ class DeviceStorageMonitorService extends Binder {
private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes
private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
+ private static final int DEFAULT_FULL_THRESHOLD_BYTES = 1024*1024; // 1MB
private long mFreeMem; // on /data
private long mLastReportedFreeMem;
private long mLastReportedFreeMemTime;
private boolean mLowMemFlag=false;
+ private boolean mMemFullFlag=false;
private Context mContext;
private ContentResolver mContentResolver;
private long mTotalMemory; // on /data
@@ -87,9 +89,13 @@ class DeviceStorageMonitorService extends Binder {
private boolean mClearingCache;
private Intent mStorageLowIntent;
private Intent mStorageOkIntent;
+ private Intent mStorageFullIntent;
+ private Intent mStorageNotFullIntent;
private CachePackageDataObserver mClearCacheObserver;
private static final int _TRUE = 1;
private static final int _FALSE = 0;
+ private long mMemLowThreshold;
+ private int mMemFullThreshold;
/**
* This string is used for ServiceManager access to this class.
@@ -103,7 +109,7 @@ class DeviceStorageMonitorService extends Binder {
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- //dont handle an invalid message
+ //don't handle an invalid message
if (msg.what != DEVICE_MEMORY_WHAT) {
Slog.e(TAG, "Will not process invalid message");
return;
@@ -184,7 +190,7 @@ class DeviceStorageMonitorService extends Binder {
try {
if (localLOGV) Slog.i(TAG, "Clearing cache");
IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
- freeStorageAndNotify(getMemThreshold(), mClearCacheObserver);
+ freeStorageAndNotify(mMemLowThreshold, mClearCacheObserver);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
mClearingCache = false;
@@ -209,8 +215,7 @@ class DeviceStorageMonitorService extends Binder {
if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
//post intent to NotificationManager to display icon if necessary
- long memThreshold = getMemThreshold();
- if (mFreeMem < memThreshold) {
+ if (mFreeMem < mMemLowThreshold) {
if (!mLowMemFlag) {
if (checkCache) {
// See if clearing cache helps
@@ -235,6 +240,17 @@ class DeviceStorageMonitorService extends Binder {
mLowMemFlag = false;
}
}
+ if (mFreeMem < mMemFullThreshold) {
+ if (!mMemFullFlag) {
+ sendFullNotification();
+ mMemFullFlag = true;
+ }
+ } else {
+ if (mMemFullFlag) {
+ cancelFullNotification();
+ mMemFullFlag = false;
+ }
+ }
}
if(localLOGV) Slog.i(TAG, "Posting Message again");
//keep posting messages to itself periodically
@@ -264,6 +280,20 @@ class DeviceStorageMonitorService extends Binder {
return mTotalMemory*value;
}
+ /*
+ * just query settings to retrieve the memory full threshold.
+ * Preferred this over using a ContentObserver since Settings.Secure caches the value
+ * any way
+ */
+ private int getMemFullThreshold() {
+ int value = Settings.Secure.getInt(
+ mContentResolver,
+ Settings.Secure.SYS_STORAGE_FULL_THRESHOLD_BYTES,
+ DEFAULT_FULL_THRESHOLD_BYTES);
+ if(localLOGV) Slog.v(TAG, "Full Threshold Bytes="+value);
+ return value;
+ }
+
/**
* Constructor to run service. initializes the disk space threshold value
* and posts an empty message to kickstart the process.
@@ -283,6 +313,13 @@ class DeviceStorageMonitorService extends Binder {
mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
+ mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
+ mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ // cache storage thresholds
+ mMemLowThreshold = getMemThreshold();
+ mMemFullThreshold = getMemFullThreshold();
checkMemory(true);
}
@@ -332,6 +369,23 @@ class DeviceStorageMonitorService extends Binder {
mContext.sendBroadcast(mStorageOkIntent);
}
+ /**
+ * Send a notification when storage is full.
+ */
+ private final void sendFullNotification() {
+ if(localLOGV) Slog.i(TAG, "Sending memory full notification");
+ mContext.sendStickyBroadcast(mStorageFullIntent);
+ }
+
+ /**
+ * Cancels memory full notification and sends "not full" intent.
+ */
+ private final void cancelFullNotification() {
+ if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
+ mContext.removeStickyBroadcast(mStorageFullIntent);
+ mContext.sendBroadcast(mStorageNotFullIntent);
+ }
+
public void updateMemory() {
int callingUid = getCallingUid();
if(callingUid != Process.SYSTEM_UID) {
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 0de11c6..0e45145 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -26,6 +26,7 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Debug;
import android.os.DropBoxManager;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.StatFs;
@@ -36,6 +37,7 @@ import android.util.Slog;
import com.android.internal.os.IDropBoxManagerService;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -179,7 +181,11 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
// the data in uncompressed form.
temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
- output = new FileOutputStream(temp);
+ int bufferSize = mBlockSize;
+ if (bufferSize > 4096) bufferSize = 4096;
+ if (bufferSize < 512) bufferSize = 512;
+ FileOutputStream foutput = new FileOutputStream(temp);
+ output = new BufferedOutputStream(foutput, bufferSize);
if (read == buffer.length && ((flags & DropBoxManager.IS_GZIPPED) == 0)) {
output = new GZIPOutputStream(output);
flags = flags | DropBoxManager.IS_GZIPPED;
@@ -196,6 +202,7 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
read = input.read(buffer);
if (read <= 0) {
+ FileUtils.sync(foutput);
output.close(); // Get a final size measurement
output = null;
} else {
@@ -690,8 +697,6 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
// was lost. Tombstones are expunged by age (see above).
if (mAllFiles.blocks > mCachedQuotaBlocks) {
- Slog.i(TAG, "Usage (" + mAllFiles.blocks + ") > Quota (" + mCachedQuotaBlocks + ")");
-
// Find a fair share amount of space to limit each tag
int unsqueezed = mAllFiles.blocks, squeezed = 0;
TreeSet<FileList> tags = new TreeSet<FileList>(mFilesByTag.values());
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
index 81ae26f..0f1fc78 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyService.java
@@ -139,6 +139,7 @@ public class EntropyService extends Binder {
out.println(SystemProperties.get("ro.bootloader"));
out.println(SystemProperties.get("ro.hardware"));
out.println(SystemProperties.get("ro.revision"));
+ out.println(new Object().hashCode());
out.println(System.currentTimeMillis());
out.println(System.nanoTime());
} catch (IOException e) {
diff --git a/services/java/com/android/server/status/StatusBarException.java b/services/java/com/android/server/InputApplication.java
index be58f59..38420d4 100644
--- a/services/java/com/android/server/status/StatusBarException.java
+++ b/services/java/com/android/server/InputApplication.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -14,10 +14,20 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.server;
-public class StatusBarException extends RuntimeException {
- StatusBarException(String msg) {
- super(msg);
- }
+/**
+ * Describes input-related application properties for use by the input dispatcher.
+ *
+ * @hide
+ */
+public final class InputApplication {
+ // Application name.
+ public String name;
+
+ // Dispatching timeout.
+ public long dispatchingTimeoutNanos;
+
+ // The application window token.
+ public Object token;
}
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
deleted file mode 100644
index 414b69f..0000000
--- a/services/java/com/android/server/InputDevice.java
+++ /dev/null
@@ -1,1025 +0,0 @@
-/*
- * Copyright (C) 2007 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.util.Slog;
-import android.view.Display;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.WindowManagerPolicy;
-
-import java.io.PrintWriter;
-
-public class InputDevice {
- static final boolean DEBUG_POINTERS = false;
- static final boolean DEBUG_HACKS = false;
-
- /** Amount that trackball needs to move in order to generate a key event. */
- static final int TRACKBALL_MOVEMENT_THRESHOLD = 6;
-
- /** Maximum number of pointers we will track and report. */
- static final int MAX_POINTERS = 10;
-
- /**
- * Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value.
- */
- private static final int JUMPY_EPSILON_DIVISOR = 212;
-
- /** Number of jumpy points to drop for touchscreens that need it. */
- private static final int JUMPY_TRANSITION_DROPS = 3;
- private static final int JUMPY_DROP_LIMIT = 3;
-
- final int id;
- final int classes;
- final String name;
- final AbsoluteInfo absX;
- final AbsoluteInfo absY;
- final AbsoluteInfo absPressure;
- final AbsoluteInfo absSize;
-
- long mKeyDownTime = 0;
- int mMetaKeysState = 0;
-
- // For use by KeyInputQueue for keeping track of the current touch
- // data in the old non-multi-touch protocol.
- final int[] curTouchVals = new int[MotionEvent.NUM_SAMPLE_DATA * 2];
-
- final MotionState mAbs = new MotionState(0, 0);
- final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD,
- TRACKBALL_MOVEMENT_THRESHOLD);
-
- static class MotionState {
- int xPrecision;
- int yPrecision;
- float xMoveScale;
- float yMoveScale;
- MotionEvent currentMove = null;
- boolean changed = false;
- boolean everChanged = false;
- long mDownTime = 0;
-
- // The currently assigned pointer IDs, corresponding to the last data.
- int[] mPointerIds = new int[MAX_POINTERS];
-
- // This is the last generated pointer data, ordered to match
- // mPointerIds.
- boolean mSkipLastPointers;
- int mLastNumPointers = 0;
- final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // This is the next set of pointer data being generated. It is not
- // in any known order, and will be propagated in to mLastData
- // as part of mapping it to the appropriate pointer IDs.
- // Note that we have one extra sample of data here, to help clients
- // avoid doing bounds checking.
- int mNextNumPointers = 0;
- final int[] mNextData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
- + MotionEvent.NUM_SAMPLE_DATA];
-
- // Used to determine whether we dropped bad data, to avoid doing
- // it repeatedly.
- final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS];
-
- // Used to count the number of jumpy points dropped.
- private int mJumpyPointsDropped = 0;
-
- // Used to perform averaging of reported coordinates, to smooth
- // the data and filter out transients during a release.
- static final int HISTORY_SIZE = 5;
- int[] mHistoryDataStart = new int[MAX_POINTERS];
- int[] mHistoryDataEnd = new int[MAX_POINTERS];
- final int[] mHistoryData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
- * HISTORY_SIZE];
- final int[] mAveragedData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // Temporary data structures for doing the pointer ID mapping.
- final int[] mLast2Next = new int[MAX_POINTERS];
- final int[] mNext2Last = new int[MAX_POINTERS];
- final long[] mNext2LastDistance = new long[MAX_POINTERS];
-
- // Temporary data structure for generating the final motion data.
- final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // This is not used here, but can be used by callers for state tracking.
- int mAddingPointerOffset = 0;
- final boolean[] mDown = new boolean[MAX_POINTERS];
-
- void dumpIntArray(PrintWriter pw, int[] array) {
- pw.print("[");
- for (int i=0; i<array.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(array[i]);
- }
- pw.print("]");
- }
-
- void dumpBooleanArray(PrintWriter pw, boolean[] array) {
- pw.print("[");
- for (int i=0; i<array.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(array[i] ? "true" : "false");
- }
- pw.print("]");
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("xPrecision="); pw.print(xPrecision);
- pw.print(" yPrecision="); pw.println(yPrecision);
- pw.print(prefix); pw.print("xMoveScale="); pw.print(xMoveScale);
- pw.print(" yMoveScale="); pw.println(yMoveScale);
- if (currentMove != null) {
- pw.print(prefix); pw.print("currentMove="); pw.println(currentMove);
- }
- if (changed || mDownTime != 0) {
- pw.print(prefix); pw.print("changed="); pw.print(changed);
- pw.print(" mDownTime="); pw.println(mDownTime);
- }
- pw.print(prefix); pw.print("mPointerIds="); dumpIntArray(pw, mPointerIds);
- pw.println("");
- if (mSkipLastPointers || mLastNumPointers != 0) {
- pw.print(prefix); pw.print("mSkipLastPointers="); pw.print(mSkipLastPointers);
- pw.print(" mLastNumPointers="); pw.println(mLastNumPointers);
- pw.print(prefix); pw.print("mLastData="); dumpIntArray(pw, mLastData);
- pw.println("");
- }
- if (mNextNumPointers != 0) {
- pw.print(prefix); pw.print("mNextNumPointers="); pw.println(mNextNumPointers);
- pw.print(prefix); pw.print("mNextData="); dumpIntArray(pw, mNextData);
- pw.println("");
- }
- pw.print(prefix); pw.print("mDroppedBadPoint=");
- dumpBooleanArray(pw, mDroppedBadPoint); pw.println("");
- pw.print(prefix); pw.print("mAddingPointerOffset="); pw.println(mAddingPointerOffset);
- pw.print(prefix); pw.print("mDown=");
- dumpBooleanArray(pw, mDown); pw.println("");
- }
-
- MotionState(int mx, int my) {
- xPrecision = mx;
- yPrecision = my;
- xMoveScale = mx != 0 ? (1.0f/mx) : 1.0f;
- yMoveScale = my != 0 ? (1.0f/my) : 1.0f;
- for (int i=0; i<MAX_POINTERS; i++) {
- mPointerIds[i] = i;
- }
- }
-
- /**
- * Special hack for devices that have bad screen data: if one of the
- * points has moved more than a screen height from the last position,
- * then drop it.
- */
- void dropBadPoint(InputDevice dev) {
- // We should always have absY, but let's be paranoid.
- if (dev.absY == null) {
- return;
- }
- // Don't do anything if a finger is going down or up. We run
- // here before assigning pointer IDs, so there isn't a good
- // way to do per-finger matching.
- if (mNextNumPointers != mLastNumPointers) {
- return;
- }
-
- // We consider a single movement across more than a 7/16 of
- // the long size of the screen to be bad. This was a magic value
- // determined by looking at the maximum distance it is feasible
- // to actually move in one sample.
- final int maxDy = ((dev.absY.maxValue-dev.absY.minValue)*7)/16;
-
- // Look through all new points and see if any are farther than
- // acceptable from all previous points.
- for (int i=mNextNumPointers-1; i>=0; i--) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- //final int x = mNextData[ioff + MotionEvent.SAMPLE_X];
- final int y = mNextData[ioff + MotionEvent.SAMPLE_Y];
- if (DEBUG_HACKS) Slog.v("InputDevice", "Looking at next point #" + i + ": y=" + y);
- boolean dropped = false;
- if (!mDroppedBadPoint[i] && mLastNumPointers > 0) {
- dropped = true;
- int closestDy = -1;
- int closestY = -1;
- // We will drop this new point if it is sufficiently
- // far away from -all- last points.
- for (int j=mLastNumPointers-1; j>=0; j--) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- //int dx = x - mLastData[joff + MotionEvent.SAMPLE_X];
- int dy = y - mLastData[joff + MotionEvent.SAMPLE_Y];
- //if (dx < 0) dx = -dx;
- if (dy < 0) dy = -dy;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Comparing with last point #" + j
- + ": y=" + mLastData[joff] + " dy=" + dy);
- if (dy < maxDy) {
- dropped = false;
- break;
- } else if (closestDy < 0 || dy < closestDy) {
- closestDy = dy;
- closestY = mLastData[joff + MotionEvent.SAMPLE_Y];
- }
- }
- if (dropped) {
- dropped = true;
- Slog.i("InputDevice", "Dropping bad point #" + i
- + ": newY=" + y + " closestDy=" + closestDy
- + " maxDy=" + maxDy);
- mNextData[ioff + MotionEvent.SAMPLE_Y] = closestY;
- break;
- }
- }
- mDroppedBadPoint[i] = dropped;
- }
- }
-
- void dropJumpyPoint(InputDevice dev) {
- // We should always have absY, but let's be paranoid.
- if (dev.absY == null) {
- return;
- }
- final int jumpyEpsilon = dev.absY.range / JUMPY_EPSILON_DIVISOR;
-
- final int nextNumPointers = mNextNumPointers;
- final int lastNumPointers = mLastNumPointers;
- final int[] nextData = mNextData;
- final int[] lastData = mLastData;
-
- if (nextNumPointers != mLastNumPointers) {
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Different pointer count " + lastNumPointers +
- " -> " + nextNumPointers);
- for (int i = 0; i < nextNumPointers; i++) {
- int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- Slog.d("InputDevice", "Pointer " + i + " (" +
- mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
- mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
- }
- }
-
- // Just drop the first few events going from 1 to 2 pointers.
- // They're bad often enough that they're not worth considering.
- if (lastNumPointers == 1 && nextNumPointers == 2
- && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- mNextNumPointers = 1;
- mJumpyPointsDropped++;
- } else if (lastNumPointers == 2 && nextNumPointers == 1
- && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- // The event when we go from 2 -> 1 tends to be messed up too
- System.arraycopy(lastData, 0, nextData, 0,
- lastNumPointers * MotionEvent.NUM_SAMPLE_DATA);
- mNextNumPointers = lastNumPointers;
- mJumpyPointsDropped++;
-
- if (DEBUG_HACKS) {
- for (int i = 0; i < mNextNumPointers; i++) {
- int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- Slog.d("InputDevice", "Pointer " + i + " replaced (" +
- mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
- mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
- }
- }
- } else {
- mJumpyPointsDropped = 0;
-
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Transition - drop limit reset");
- }
- }
- return;
- }
-
- // A 'jumpy' point is one where the coordinate value for one axis
- // has jumped to the other pointer's location. No need to do anything
- // else if we only have one pointer.
- if (nextNumPointers < 2) {
- return;
- }
-
- int badPointerIndex = -1;
- int badPointerReplaceXWith = 0;
- int badPointerReplaceYWith = 0;
- int badPointerDistance = Integer.MIN_VALUE;
- for (int i = nextNumPointers - 1; i >= 0; i--) {
- boolean dropx = false;
- boolean dropy = false;
-
- // Limit how many times a jumpy point can get dropped.
- if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- final int x = nextData[ioff + MotionEvent.SAMPLE_X];
- final int y = nextData[ioff + MotionEvent.SAMPLE_Y];
-
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")");
- }
-
- // Check if a touch point is too close to another's coordinates
- for (int j = 0; j < nextNumPointers && !dropx && !dropy; j++) {
- if (j == i) {
- continue;
- }
-
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- final int xOther = nextData[joff + MotionEvent.SAMPLE_X];
- final int yOther = nextData[joff + MotionEvent.SAMPLE_Y];
-
- dropx = Math.abs(x - xOther) <= jumpyEpsilon;
- dropy = Math.abs(y - yOther) <= jumpyEpsilon;
- }
-
- if (dropx) {
- int xreplace = lastData[MotionEvent.SAMPLE_X];
- int yreplace = lastData[MotionEvent.SAMPLE_Y];
- int distance = Math.abs(yreplace - y);
- for (int j = 1; j < lastNumPointers; j++) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- int lasty = lastData[joff + MotionEvent.SAMPLE_Y];
- int currDist = Math.abs(lasty - y);
- if (currDist < distance) {
- xreplace = lastData[joff + MotionEvent.SAMPLE_X];
- yreplace = lasty;
- distance = currDist;
- }
- }
-
- int badXDelta = Math.abs(xreplace - x);
- if (badXDelta > badPointerDistance) {
- badPointerDistance = badXDelta;
- badPointerIndex = i;
- badPointerReplaceXWith = xreplace;
- badPointerReplaceYWith = yreplace;
- }
- } else if (dropy) {
- int xreplace = lastData[MotionEvent.SAMPLE_X];
- int yreplace = lastData[MotionEvent.SAMPLE_Y];
- int distance = Math.abs(xreplace - x);
- for (int j = 1; j < lastNumPointers; j++) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- int lastx = lastData[joff + MotionEvent.SAMPLE_X];
- int currDist = Math.abs(lastx - x);
- if (currDist < distance) {
- xreplace = lastx;
- yreplace = lastData[joff + MotionEvent.SAMPLE_Y];
- distance = currDist;
- }
- }
-
- int badYDelta = Math.abs(yreplace - y);
- if (badYDelta > badPointerDistance) {
- badPointerDistance = badYDelta;
- badPointerIndex = i;
- badPointerReplaceXWith = xreplace;
- badPointerReplaceYWith = yreplace;
- }
- }
- }
- }
- if (badPointerIndex >= 0) {
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Replacing bad pointer " + badPointerIndex +
- " with (" + badPointerReplaceXWith + ", " + badPointerReplaceYWith +
- ")");
- }
-
- final int offset = badPointerIndex * MotionEvent.NUM_SAMPLE_DATA;
- nextData[offset + MotionEvent.SAMPLE_X] = badPointerReplaceXWith;
- nextData[offset + MotionEvent.SAMPLE_Y] = badPointerReplaceYWith;
- mJumpyPointsDropped++;
- } else {
- mJumpyPointsDropped = 0;
- }
- }
-
- /**
- * Special hack for devices that have bad screen data: aggregate and
- * compute averages of the coordinate data, to reduce the amount of
- * jitter seen by applications.
- */
- int[] generateAveragedData(int upOrDownPointer, int lastNumPointers,
- int nextNumPointers) {
- final int numPointers = mLastNumPointers;
- final int[] rawData = mLastData;
- if (DEBUG_HACKS) Slog.v("InputDevice", "lastNumPointers=" + lastNumPointers
- + " nextNumPointers=" + nextNumPointers
- + " numPointers=" + numPointers);
- for (int i=0; i<numPointers; i++) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- // We keep the average data in offsets based on the pointer
- // ID, so we don't need to move it around as fingers are
- // pressed and released.
- final int p = mPointerIds[i];
- final int poff = p * MotionEvent.NUM_SAMPLE_DATA * HISTORY_SIZE;
- if (i == upOrDownPointer && lastNumPointers != nextNumPointers) {
- if (lastNumPointers < nextNumPointers) {
- // This pointer is going down. Clear its history
- // and start fresh.
- if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer down @ index "
- + upOrDownPointer + " id " + mPointerIds[i]);
- mHistoryDataStart[i] = 0;
- mHistoryDataEnd[i] = 0;
- System.arraycopy(rawData, ioff, mHistoryData, poff,
- MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(rawData, ioff, mAveragedData, ioff,
- MotionEvent.NUM_SAMPLE_DATA);
- continue;
- } else {
- // The pointer is going up. Just fall through to
- // recompute the last averaged point (and don't add
- // it as a new point to include in the average).
- if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer up @ index "
- + upOrDownPointer + " id " + mPointerIds[i]);
- }
- } else {
- int end = mHistoryDataEnd[i];
- int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- int oldX = mHistoryData[eoff + MotionEvent.SAMPLE_X];
- int oldY = mHistoryData[eoff + MotionEvent.SAMPLE_Y];
- int newX = rawData[ioff + MotionEvent.SAMPLE_X];
- int newY = rawData[ioff + MotionEvent.SAMPLE_Y];
- int dx = newX-oldX;
- int dy = newY-oldY;
- int delta = dx*dx + dy*dy;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Delta from last: " + delta);
- if (delta >= (75*75)) {
- // Magic number, if moving farther than this, turn
- // off filtering to avoid lag in response.
- mHistoryDataStart[i] = 0;
- mHistoryDataEnd[i] = 0;
- System.arraycopy(rawData, ioff, mHistoryData, poff,
- MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(rawData, ioff, mAveragedData, ioff,
- MotionEvent.NUM_SAMPLE_DATA);
- continue;
- } else {
- end++;
- if (end >= HISTORY_SIZE) {
- end -= HISTORY_SIZE;
- }
- mHistoryDataEnd[i] = end;
- int noff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- mHistoryData[noff + MotionEvent.SAMPLE_X] = newX;
- mHistoryData[noff + MotionEvent.SAMPLE_Y] = newY;
- mHistoryData[noff + MotionEvent.SAMPLE_PRESSURE]
- = rawData[ioff + MotionEvent.SAMPLE_PRESSURE];
- int start = mHistoryDataStart[i];
- if (end == start) {
- start++;
- if (start >= HISTORY_SIZE) {
- start -= HISTORY_SIZE;
- }
- mHistoryDataStart[i] = start;
- }
- }
- }
-
- // Now compute the average.
- int start = mHistoryDataStart[i];
- int end = mHistoryDataEnd[i];
- int x=0, y=0;
- int totalPressure = 0;
- while (start != end) {
- int soff = poff + (start*MotionEvent.NUM_SAMPLE_DATA);
- int pressure = mHistoryData[soff + MotionEvent.SAMPLE_PRESSURE];
- if (pressure <= 0) pressure = 1;
- x += mHistoryData[soff + MotionEvent.SAMPLE_X] * pressure;
- y += mHistoryData[soff + MotionEvent.SAMPLE_Y] * pressure;
- totalPressure += pressure;
- start++;
- if (start >= HISTORY_SIZE) start = 0;
- }
- int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- int pressure = mHistoryData[eoff + MotionEvent.SAMPLE_PRESSURE];
- if (pressure <= 0) pressure = 1;
- x += mHistoryData[eoff + MotionEvent.SAMPLE_X] * pressure;
- y += mHistoryData[eoff + MotionEvent.SAMPLE_Y] * pressure;
- totalPressure += pressure;
- x /= totalPressure;
- y /= totalPressure;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Averaging " + totalPressure
- + " weight: (" + x + "," + y + ")");
- mAveragedData[ioff + MotionEvent.SAMPLE_X] = x;
- mAveragedData[ioff + MotionEvent.SAMPLE_Y] = y;
- mAveragedData[ioff + MotionEvent.SAMPLE_PRESSURE] =
- rawData[ioff + MotionEvent.SAMPLE_PRESSURE];
- mAveragedData[ioff + MotionEvent.SAMPLE_SIZE] =
- rawData[ioff + MotionEvent.SAMPLE_SIZE];
- }
- return mAveragedData;
- }
-
- private boolean assignPointer(int nextIndex, boolean allowOverlap) {
- final int lastNumPointers = mLastNumPointers;
- final int[] next2Last = mNext2Last;
- final long[] next2LastDistance = mNext2LastDistance;
- final int[] last2Next = mLast2Next;
- final int[] lastData = mLastData;
- final int[] nextData = mNextData;
- final int id = nextIndex * MotionEvent.NUM_SAMPLE_DATA;
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "assignPointer: nextIndex="
- + nextIndex + " dataOff=" + id);
- final int x1 = nextData[id + MotionEvent.SAMPLE_X];
- final int y1 = nextData[id + MotionEvent.SAMPLE_Y];
-
- long bestDistance = -1;
- int bestIndex = -1;
- for (int j=0; j<lastNumPointers; j++) {
- // If we are not allowing multiple new points to be assigned
- // to the same old pointer, then skip this one if it is already
- // detected as a conflict (-2).
- if (!allowOverlap && last2Next[j] < -1) {
- continue;
- }
- final int jd = j * MotionEvent.NUM_SAMPLE_DATA;
- final int xd = lastData[jd + MotionEvent.SAMPLE_X] - x1;
- final int yd = lastData[jd + MotionEvent.SAMPLE_Y] - y1;
- final long distance = xd*(long)xd + yd*(long)yd;
- if (bestDistance == -1 || distance < bestDistance) {
- bestDistance = distance;
- bestIndex = j;
- }
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "New index " + nextIndex
- + " best old index=" + bestIndex + " (distance="
- + bestDistance + ")");
- next2Last[nextIndex] = bestIndex;
- next2LastDistance[nextIndex] = bestDistance;
-
- if (bestIndex < 0) {
- return true;
- }
-
- if (last2Next[bestIndex] == -1) {
- last2Next[bestIndex] = nextIndex;
- return false;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Old index " + bestIndex
- + " has multiple best new pointers!");
-
- last2Next[bestIndex] = -2;
- return true;
- }
-
- private int updatePointerIdentifiers() {
- final int[] lastData = mLastData;
- final int[] nextData = mNextData;
- final int nextNumPointers = mNextNumPointers;
- final int lastNumPointers = mLastNumPointers;
-
- if (nextNumPointers == 1 && lastNumPointers == 1) {
- System.arraycopy(nextData, 0, lastData, 0,
- MotionEvent.NUM_SAMPLE_DATA);
- return -1;
- }
-
- // Clear our old state.
- final int[] last2Next = mLast2Next;
- for (int i=0; i<lastNumPointers; i++) {
- last2Next[i] = -1;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Update pointers: lastNumPointers=" + lastNumPointers
- + " nextNumPointers=" + nextNumPointers);
-
- // Figure out the closes new points to the previous points.
- final int[] next2Last = mNext2Last;
- final long[] next2LastDistance = mNext2LastDistance;
- boolean conflicts = false;
- for (int i=0; i<nextNumPointers; i++) {
- conflicts |= assignPointer(i, true);
- }
-
- // Resolve ambiguities in pointer mappings, when two or more
- // new pointer locations find their best previous location is
- // the same.
- if (conflicts) {
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving conflicts");
-
- for (int i=0; i<lastNumPointers; i++) {
- if (last2Next[i] != -2) {
- continue;
- }
-
- // Note that this algorithm is far from perfect. Ideally
- // we should do something like the one described at
- // http://portal.acm.org/citation.cfm?id=997856
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Resolving last index #" + i);
-
- int numFound;
- do {
- numFound = 0;
- long worstDistance = 0;
- int worstJ = -1;
- for (int j=0; j<nextNumPointers; j++) {
- if (next2Last[j] != i) {
- continue;
- }
- numFound++;
- if (worstDistance < next2LastDistance[j]) {
- worstDistance = next2LastDistance[j];
- worstJ = j;
- }
- }
-
- if (worstJ >= 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Worst new pointer: " + worstJ
- + " (distance=" + worstDistance + ")");
- if (assignPointer(worstJ, false)) {
- // In this case there is no last pointer
- // remaining for this new one!
- next2Last[worstJ] = -1;
- }
- }
- } while (numFound > 2);
- }
- }
-
- int retIndex = -1;
-
- if (lastNumPointers < nextNumPointers) {
- // We have one or more new pointers that are down. Create a
- // new pointer identifier for one of them.
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Adding new pointer");
- int nextId = 0;
- int i=0;
- while (i < lastNumPointers) {
- if (mPointerIds[i] > nextId) {
- // Found a hole, insert the pointer here.
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Inserting new pointer at hole " + i);
- System.arraycopy(mPointerIds, i, mPointerIds,
- i+1, lastNumPointers-i);
- System.arraycopy(lastData, i*MotionEvent.NUM_SAMPLE_DATA,
- lastData, (i+1)*MotionEvent.NUM_SAMPLE_DATA,
- (lastNumPointers-i)*MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(next2Last, i, next2Last,
- i+1, lastNumPointers-i);
- break;
- }
- i++;
- nextId++;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "New pointer id " + nextId + " at index " + i);
-
- mLastNumPointers++;
- retIndex = i;
- mPointerIds[i] = nextId;
-
- // And assign this identifier to the first new pointer.
- for (int j=0; j<nextNumPointers; j++) {
- if (next2Last[j] < 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Assigning new id to new pointer index " + j);
- next2Last[j] = i;
- break;
- }
- }
- }
-
- // Propagate all of the current data into the appropriate
- // location in the old data to match the pointer ID that was
- // assigned to it.
- for (int i=0; i<nextNumPointers; i++) {
- int lastIndex = next2Last[i];
- if (lastIndex >= 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Copying next pointer index " + i
- + " to last index " + lastIndex);
- System.arraycopy(nextData, i*MotionEvent.NUM_SAMPLE_DATA,
- lastData, lastIndex*MotionEvent.NUM_SAMPLE_DATA,
- MotionEvent.NUM_SAMPLE_DATA);
- }
- }
-
- if (lastNumPointers > nextNumPointers) {
- // One or more pointers has gone up. Find the first one,
- // and adjust accordingly.
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Removing old pointer");
- for (int i=0; i<lastNumPointers; i++) {
- if (last2Next[i] == -1) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Removing old pointer at index " + i);
- retIndex = i;
- break;
- }
- }
- }
-
- return retIndex;
- }
-
- void removeOldPointer(int index) {
- final int lastNumPointers = mLastNumPointers;
- if (index >= 0 && index < lastNumPointers) {
- System.arraycopy(mPointerIds, index+1, mPointerIds,
- index, lastNumPointers-index-1);
- System.arraycopy(mLastData, (index+1)*MotionEvent.NUM_SAMPLE_DATA,
- mLastData, (index)*MotionEvent.NUM_SAMPLE_DATA,
- (lastNumPointers-index-1)*MotionEvent.NUM_SAMPLE_DATA);
- mLastNumPointers--;
- }
- }
-
- MotionEvent generateAbsMotion(InputDevice device, long curTime,
- long curTimeNano, Display display, int orientation,
- int metaState) {
-
- if (mSkipLastPointers) {
- mSkipLastPointers = false;
- mLastNumPointers = 0;
- }
-
- if (mNextNumPointers <= 0 && mLastNumPointers <= 0) {
- return null;
- }
-
- final int lastNumPointers = mLastNumPointers;
- final int nextNumPointers = mNextNumPointers;
- if (mNextNumPointers > MAX_POINTERS) {
- Slog.w("InputDevice", "Number of pointers " + mNextNumPointers
- + " exceeded maximum of " + MAX_POINTERS);
- mNextNumPointers = MAX_POINTERS;
- }
-
- int upOrDownPointer = updatePointerIdentifiers();
-
- final float[] reportData = mReportData;
- final int[] rawData;
- if (KeyInputQueue.BAD_TOUCH_HACK) {
- rawData = generateAveragedData(upOrDownPointer, lastNumPointers,
- nextNumPointers);
- } else {
- rawData = mLastData;
- }
-
- final int numPointers = mLastNumPointers;
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Processing "
- + numPointers + " pointers (going from " + lastNumPointers
- + " to " + nextNumPointers + ")");
-
- for (int i=0; i<numPointers; i++) {
- final int pos = i * MotionEvent.NUM_SAMPLE_DATA;
- reportData[pos + MotionEvent.SAMPLE_X] = rawData[pos + MotionEvent.SAMPLE_X];
- reportData[pos + MotionEvent.SAMPLE_Y] = rawData[pos + MotionEvent.SAMPLE_Y];
- reportData[pos + MotionEvent.SAMPLE_PRESSURE] = rawData[pos + MotionEvent.SAMPLE_PRESSURE];
- reportData[pos + MotionEvent.SAMPLE_SIZE] = rawData[pos + MotionEvent.SAMPLE_SIZE];
- }
-
- int action;
- int edgeFlags = 0;
- if (nextNumPointers != lastNumPointers) {
- if (nextNumPointers > lastNumPointers) {
- if (lastNumPointers == 0) {
- action = MotionEvent.ACTION_DOWN;
- mDownTime = curTime;
- } else {
- action = MotionEvent.ACTION_POINTER_DOWN
- | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- }
- } else {
- if (numPointers == 1) {
- action = MotionEvent.ACTION_UP;
- } else {
- action = MotionEvent.ACTION_POINTER_UP
- | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- }
- }
- currentMove = null;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
-
- final int dispW = display.getWidth()-1;
- final int dispH = display.getHeight()-1;
- int w = dispW;
- int h = dispH;
- if (orientation == Surface.ROTATION_90
- || orientation == Surface.ROTATION_270) {
- int tmp = w;
- w = h;
- h = tmp;
- }
-
- final AbsoluteInfo absX = device.absX;
- final AbsoluteInfo absY = device.absY;
- final AbsoluteInfo absPressure = device.absPressure;
- final AbsoluteInfo absSize = device.absSize;
- for (int i=0; i<numPointers; i++) {
- final int j = i * MotionEvent.NUM_SAMPLE_DATA;
-
- if (absX != null) {
- reportData[j + MotionEvent.SAMPLE_X] =
- ((reportData[j + MotionEvent.SAMPLE_X]-absX.minValue)
- / absX.range) * w;
- }
- if (absY != null) {
- reportData[j + MotionEvent.SAMPLE_Y] =
- ((reportData[j + MotionEvent.SAMPLE_Y]-absY.minValue)
- / absY.range) * h;
- }
- if (absPressure != null) {
- reportData[j + MotionEvent.SAMPLE_PRESSURE] =
- ((reportData[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue)
- / (float)absPressure.range);
- }
- if (absSize != null) {
- reportData[j + MotionEvent.SAMPLE_SIZE] =
- ((reportData[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue)
- / (float)absSize.range);
- }
-
- switch (orientation) {
- case Surface.ROTATION_90: {
- final float temp = reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_X] = reportData[j + MotionEvent.SAMPLE_Y];
- reportData[j + MotionEvent.SAMPLE_Y] = w-temp;
- break;
- }
- case Surface.ROTATION_180: {
- reportData[j + MotionEvent.SAMPLE_X] = w-reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_Y] = h-reportData[j + MotionEvent.SAMPLE_Y];
- break;
- }
- case Surface.ROTATION_270: {
- final float temp = reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_X] = h-reportData[j + MotionEvent.SAMPLE_Y];
- reportData[j + MotionEvent.SAMPLE_Y] = temp;
- break;
- }
- }
- }
-
- // We only consider the first pointer when computing the edge
- // flags, since they are global to the event.
- if (action == MotionEvent.ACTION_DOWN) {
- if (reportData[MotionEvent.SAMPLE_X] <= 0) {
- edgeFlags |= MotionEvent.EDGE_LEFT;
- } else if (reportData[MotionEvent.SAMPLE_X] >= dispW) {
- edgeFlags |= MotionEvent.EDGE_RIGHT;
- }
- if (reportData[MotionEvent.SAMPLE_Y] <= 0) {
- edgeFlags |= MotionEvent.EDGE_TOP;
- } else if (reportData[MotionEvent.SAMPLE_Y] >= dispH) {
- edgeFlags |= MotionEvent.EDGE_BOTTOM;
- }
- }
-
- if (currentMove != null) {
- if (false) Slog.i("InputDevice", "Adding batch x="
- + reportData[MotionEvent.SAMPLE_X]
- + " y=" + reportData[MotionEvent.SAMPLE_Y]
- + " to " + currentMove);
- currentMove.addBatch(curTime, reportData, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
- }
-
- MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
- curTimeNano, action, numPointers, mPointerIds, reportData,
- metaState, xPrecision, yPrecision, device.id, edgeFlags);
- if (action == MotionEvent.ACTION_MOVE) {
- currentMove = me;
- }
-
- if (nextNumPointers < lastNumPointers) {
- removeOldPointer(upOrDownPointer);
- }
-
- return me;
- }
-
- boolean hasMore() {
- return mLastNumPointers != mNextNumPointers;
- }
-
- void finish() {
- mNextNumPointers = mAddingPointerOffset = 0;
- mNextData[MotionEvent.SAMPLE_PRESSURE] = 0;
- }
-
- MotionEvent generateRelMotion(InputDevice device, long curTime,
- long curTimeNano, int orientation, int metaState) {
-
- final float[] scaled = mReportData;
-
- // For now we only support 1 pointer with relative motions.
- scaled[MotionEvent.SAMPLE_X] = mNextData[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_Y] = mNextData[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f;
- scaled[MotionEvent.SAMPLE_SIZE] = 0;
- int edgeFlags = 0;
-
- int action;
- if (mNextNumPointers != mLastNumPointers) {
- mNextData[MotionEvent.SAMPLE_X] =
- mNextData[MotionEvent.SAMPLE_Y] = 0;
- if (mNextNumPointers > 0 && mLastNumPointers == 0) {
- action = MotionEvent.ACTION_DOWN;
- mDownTime = curTime;
- } else if (mNextNumPointers == 0) {
- action = MotionEvent.ACTION_UP;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
- mLastNumPointers = mNextNumPointers;
- currentMove = null;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
-
- scaled[MotionEvent.SAMPLE_X] *= xMoveScale;
- scaled[MotionEvent.SAMPLE_Y] *= yMoveScale;
- switch (orientation) {
- case Surface.ROTATION_90: {
- final float temp = scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_X] = scaled[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_Y] = -temp;
- break;
- }
- case Surface.ROTATION_180: {
- scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_Y] = -scaled[MotionEvent.SAMPLE_Y];
- break;
- }
- case Surface.ROTATION_270: {
- final float temp = scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_Y] = temp;
- break;
- }
- }
-
- if (currentMove != null) {
- if (false) Slog.i("InputDevice", "Adding batch x="
- + scaled[MotionEvent.SAMPLE_X]
- + " y=" + scaled[MotionEvent.SAMPLE_Y]
- + " to " + currentMove);
- currentMove.addBatch(curTime, scaled, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
- }
-
- MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
- curTimeNano, action, 1, mPointerIds, scaled, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
- if (action == MotionEvent.ACTION_MOVE) {
- currentMove = me;
- }
- return me;
- }
- }
-
- static class AbsoluteInfo {
- int minValue;
- int maxValue;
- int range;
- int flat;
- int fuzz;
-
- final void dump(PrintWriter pw) {
- pw.print("minValue="); pw.print(minValue);
- pw.print(" maxValue="); pw.print(maxValue);
- pw.print(" range="); pw.print(range);
- pw.print(" flat="); pw.print(flat);
- pw.print(" fuzz="); pw.print(fuzz);
- }
- };
-
- InputDevice(int _id, int _classes, String _name,
- AbsoluteInfo _absX, AbsoluteInfo _absY,
- AbsoluteInfo _absPressure, AbsoluteInfo _absSize) {
- id = _id;
- classes = _classes;
- name = _name;
- absX = _absX;
- absY = _absY;
- absPressure = _absPressure;
- absSize = _absSize;
- }
-};
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
new file mode 100644
index 0000000..df41264
--- /dev/null
+++ b/services/java/com/android/server/InputManager.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2010 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 com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.util.Xml;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.Surface;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Properties;
+
+/*
+ * Wraps the C++ InputManager and provides its callbacks.
+ */
+public class InputManager {
+ static final String TAG = "InputManager";
+
+ private static final boolean DEBUG = false;
+
+ private final Callbacks mCallbacks;
+ private final Context mContext;
+ private final WindowManagerService mWindowManagerService;
+
+ private static native void nativeInit(Callbacks callbacks);
+ private static native void nativeStart();
+ private static native void nativeSetDisplaySize(int displayId, int width, int height);
+ private static native void nativeSetDisplayOrientation(int displayId, int rotation);
+
+ private static native int nativeGetScanCodeState(int deviceId, int sourceMask,
+ int scanCode);
+ private static native int nativeGetKeyCodeState(int deviceId, int sourceMask,
+ int keyCode);
+ private static native int nativeGetSwitchState(int deviceId, int sourceMask,
+ int sw);
+ private static native boolean nativeHasKeys(int deviceId, int sourceMask,
+ int[] keyCodes, boolean[] keyExists);
+ private static native void nativeRegisterInputChannel(InputChannel inputChannel,
+ boolean monitor);
+ private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+ private static native int nativeInjectInputEvent(InputEvent event,
+ int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
+ private static native void nativeSetInputWindows(InputWindow[] windows);
+ private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
+ private static native void nativeSetFocusedApplication(InputApplication application);
+ private static native InputDevice nativeGetInputDevice(int deviceId);
+ private static native void nativeGetInputConfiguration(Configuration configuration);
+ private static native int[] nativeGetInputDeviceIds();
+ private static native String nativeDump();
+
+ // Input event injection constants defined in InputDispatcher.h.
+ static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
+ static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
+ static final int INPUT_EVENT_INJECTION_FAILED = 2;
+ static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
+
+ // Input event injection synchronization modes defined in InputDispatcher.h
+ static final int INPUT_EVENT_INJECTION_SYNC_NONE = 0;
+ static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1;
+ static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH = 2;
+
+ // Key states (may be returned by queries about the current state of a
+ // particular key code, scan code or switch).
+
+ /** The key state is unknown or the requested key itself is not supported. */
+ public static final int KEY_STATE_UNKNOWN = -1;
+
+ /** The key is up. /*/
+ public static final int KEY_STATE_UP = 0;
+
+ /** The key is down. */
+ public static final int KEY_STATE_DOWN = 1;
+
+ /** The key is down but is a virtual key press that is being emulated by the system. */
+ public static final int KEY_STATE_VIRTUAL = 2;
+
+ public InputManager(Context context, WindowManagerService windowManagerService) {
+ this.mContext = context;
+ this.mWindowManagerService = windowManagerService;
+
+ this.mCallbacks = new Callbacks();
+
+ init();
+ }
+
+ private void init() {
+ Slog.i(TAG, "Initializing input manager");
+ nativeInit(mCallbacks);
+ }
+
+ public void start() {
+ Slog.i(TAG, "Starting input manager");
+ nativeStart();
+ }
+
+ public void setDisplaySize(int displayId, int width, int height) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Invalid display id or dimensions.");
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height);
+ }
+ nativeSetDisplaySize(displayId, width, height);
+ }
+
+ public void setDisplayOrientation(int displayId, int rotation) {
+ if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
+ throw new IllegalArgumentException("Invalid rotation.");
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Setting display #" + displayId + " orientation to " + rotation);
+ }
+ nativeSetDisplayOrientation(displayId, rotation);
+ }
+
+ public void getInputConfiguration(Configuration config) {
+ if (config == null) {
+ throw new IllegalArgumentException("config must not be null.");
+ }
+
+ nativeGetInputConfiguration(config);
+ }
+
+ /**
+ * Gets the current state of a key or button by key code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param keyCode The key code to check.
+ * @return The key state.
+ */
+ public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) {
+ return nativeGetKeyCodeState(deviceId, sourceMask, keyCode);
+ }
+
+ /**
+ * Gets the current state of a key or button by scan code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param scanCode The scan code to check.
+ * @return The key state.
+ */
+ public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
+ return nativeGetScanCodeState(deviceId, sourceMask, scanCode);
+ }
+
+ /**
+ * Gets the current state of a switch by switch code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param switchCode The switch code to check.
+ * @return The switch state.
+ */
+ public int getSwitchState(int deviceId, int sourceMask, int switchCode) {
+ return nativeGetSwitchState(deviceId, sourceMask, switchCode);
+ }
+
+ /**
+ * Determines whether the specified key codes are supported by a particular device.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param keyCodes The array of key codes to check.
+ * @param keyExists An array at least as large as keyCodes whose entries will be set
+ * to true or false based on the presence or absence of support for the corresponding
+ * key codes.
+ * @return True if the lookup was successful, false otherwise.
+ */
+ public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
+ if (keyCodes == null) {
+ throw new IllegalArgumentException("keyCodes must not be null.");
+ }
+ if (keyExists == null || keyExists.length < keyCodes.length) {
+ throw new IllegalArgumentException("keyExists must not be null and must be at "
+ + "least as large as keyCodes.");
+ }
+
+ return nativeHasKeys(deviceId, sourceMask, keyCodes, keyExists);
+ }
+
+ /**
+ * Creates an input channel that will receive all input from the input dispatcher.
+ * @param inputChannelName The input channel name.
+ * @return The input channel.
+ */
+ public InputChannel monitorInput(String inputChannelName) {
+ if (inputChannelName == null) {
+ throw new IllegalArgumentException("inputChannelName must not be null.");
+ }
+
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
+ nativeRegisterInputChannel(inputChannels[0], true);
+ inputChannels[0].dispose(); // don't need to retain the Java object reference
+ return inputChannels[1];
+ }
+
+ /**
+ * Registers an input channel so that it can be used as an input event target.
+ * @param inputChannel The input channel to register.
+ */
+ public void registerInputChannel(InputChannel inputChannel) {
+ if (inputChannel == null) {
+ throw new IllegalArgumentException("inputChannel must not be null.");
+ }
+
+ nativeRegisterInputChannel(inputChannel, false);
+ }
+
+ /**
+ * Unregisters an input channel.
+ * @param inputChannel The input channel to unregister.
+ */
+ public void unregisterInputChannel(InputChannel inputChannel) {
+ if (inputChannel == null) {
+ throw new IllegalArgumentException("inputChannel must not be null.");
+ }
+
+ nativeUnregisterInputChannel(inputChannel);
+ }
+
+ /**
+ * Injects an input event into the event system on behalf of an application.
+ * The synchronization mode determines whether the method blocks while waiting for
+ * input injection to proceed.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_NONE} never blocks. Injection is asynchronous and
+ * is assumed always to be successful.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT} waits for previous events to be
+ * dispatched so that the input dispatcher can determine whether input event injection will
+ * be permitted based on the current input focus. Does not wait for the input event to
+ * finish processing.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH} waits for the input event to
+ * be completely processed.
+ *
+ * @param event The event to inject.
+ * @param injectorPid The pid of the injecting application.
+ * @param injectorUid The uid of the injecting application.
+ * @param syncMode The synchronization mode.
+ * @param timeoutMillis The injection timeout in milliseconds.
+ * @return One of the INPUT_EVENT_INJECTION_XXX constants.
+ */
+ public int injectInputEvent(InputEvent event, int injectorPid, int injectorUid,
+ int syncMode, int timeoutMillis) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+ if (injectorPid < 0 || injectorUid < 0) {
+ throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
+ }
+ if (timeoutMillis <= 0) {
+ throw new IllegalArgumentException("timeoutMillis must be positive");
+ }
+
+ return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
+ }
+
+ /**
+ * Gets information about the input device with the specified id.
+ * @param id The device id.
+ * @return The input device or null if not found.
+ */
+ public InputDevice getInputDevice(int deviceId) {
+ return nativeGetInputDevice(deviceId);
+ }
+
+ /**
+ * Gets the ids of all input devices in the system.
+ * @return The input device ids.
+ */
+ public int[] getInputDeviceIds() {
+ return nativeGetInputDeviceIds();
+ }
+
+ public void setInputWindows(InputWindow[] windows) {
+ nativeSetInputWindows(windows);
+ }
+
+ public void setFocusedApplication(InputApplication application) {
+ nativeSetFocusedApplication(application);
+ }
+
+ public void setInputDispatchMode(boolean enabled, boolean frozen) {
+ nativeSetInputDispatchMode(enabled, frozen);
+ }
+
+ public void dump(PrintWriter pw) {
+ String dumpStr = nativeDump();
+ if (dumpStr != null) {
+ pw.println(dumpStr);
+ }
+ }
+
+ private static final class VirtualKeyDefinition {
+ public int scanCode;
+
+ // configured position data, specified in display coords
+ public int centerX;
+ public int centerY;
+ public int width;
+ public int height;
+ }
+
+ private static final class InputDeviceCalibration {
+ public String[] keys;
+ public String[] values;
+ }
+
+ /*
+ * Callbacks from native.
+ */
+ private class Callbacks {
+ static final String TAG = "InputManager-Callbacks";
+
+ private static final boolean DEBUG_VIRTUAL_KEYS = false;
+ private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+ private static final String CALIBRATION_DIR_PATH = "usr/idc/";
+
+ @SuppressWarnings("unused")
+ public void notifyConfigurationChanged(long whenNanos) {
+ mWindowManagerService.sendNewConfiguration();
+ }
+
+ @SuppressWarnings("unused")
+ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+ mWindowManagerService.mInputMonitor.notifyLidSwitchChanged(whenNanos, lidOpen);
+ }
+
+ @SuppressWarnings("unused")
+ public void notifyInputChannelBroken(InputChannel inputChannel) {
+ mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
+ }
+
+ @SuppressWarnings("unused")
+ public long notifyANR(Object token, InputChannel inputChannel) {
+ return mWindowManagerService.mInputMonitor.notifyANR(token, inputChannel);
+ }
+
+ @SuppressWarnings("unused")
+ public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
+ int policyFlags, boolean isScreenOn) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
+ whenNanos, keyCode, down, policyFlags, isScreenOn);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean interceptKeyBeforeDispatching(InputChannel focus, int action,
+ int flags, int keyCode, int metaState, int repeatCount, int policyFlags) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
+ action, flags, keyCode, metaState, repeatCount, policyFlags);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
+ return mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @SuppressWarnings("unused")
+ public boolean filterTouchEvents() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_filterTouchEvents);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean filterJumpyTouchEvents() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_filterJumpyTouchEvents);
+ }
+
+ @SuppressWarnings("unused")
+ public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
+ ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
+
+ try {
+ FileInputStream fis = new FileInputStream(
+ "/sys/board_properties/virtualkeys." + deviceName);
+ InputStreamReader isr = new InputStreamReader(fis);
+ BufferedReader br = new BufferedReader(isr, 2048);
+ String str = br.readLine();
+ if (str != null) {
+ String[] it = str.split(":");
+ if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
+ final int N = it.length-6;
+ for (int i=0; i<=N; i+=6) {
+ if (!"0x01".equals(it[i])) {
+ Slog.w(TAG, "Unknown virtual key type at elem #"
+ + i + ": " + it[i] + " for device " + deviceName);
+ continue;
+ }
+ try {
+ VirtualKeyDefinition key = new VirtualKeyDefinition();
+ key.scanCode = Integer.parseInt(it[i+1]);
+ key.centerX = Integer.parseInt(it[i+2]);
+ key.centerY = Integer.parseInt(it[i+3]);
+ key.width = Integer.parseInt(it[i+4]);
+ key.height = Integer.parseInt(it[i+5]);
+ if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
+ + key.scanCode + ": center=" + key.centerX + ","
+ + key.centerY + " size=" + key.width + "x"
+ + key.height);
+ keys.add(key);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Bad number in virtual key definition at region "
+ + i + " in: " + str + " for device " + deviceName, e);
+ }
+ }
+ }
+ br.close();
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "No virtual keys found for device " + deviceName + ".");
+ } catch (IOException e) {
+ Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e);
+ }
+
+ return keys.toArray(new VirtualKeyDefinition[keys.size()]);
+ }
+
+ @SuppressWarnings("unused")
+ public InputDeviceCalibration getInputDeviceCalibration(String deviceName) {
+ // Calibration is specified as a sequence of colon-delimited key value pairs.
+ Properties properties = new Properties();
+ File calibrationFile = new File(Environment.getRootDirectory(),
+ CALIBRATION_DIR_PATH + deviceName + ".idc");
+ if (calibrationFile.exists()) {
+ try {
+ properties.load(new FileInputStream(calibrationFile));
+ } catch (IOException ex) {
+ Slog.w(TAG, "Error reading input device calibration properties for device "
+ + deviceName + " from " + calibrationFile + ".", ex);
+ }
+ } else {
+ Slog.i(TAG, "No input device calibration properties found for device "
+ + deviceName + ".");
+ return null;
+ }
+
+ InputDeviceCalibration calibration = new InputDeviceCalibration();
+ calibration.keys = properties.keySet().toArray(new String[properties.size()]);
+ calibration.values = properties.values().toArray(new String[properties.size()]);
+ return calibration;
+ }
+
+ @SuppressWarnings("unused")
+ public String[] getExcludedDeviceNames() {
+ ArrayList<String> names = new ArrayList<String>();
+
+ // Read partner-provided list of excluded input devices
+ XmlPullParser parser = null;
+ // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
+ File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
+ FileReader confreader = null;
+ try {
+ confreader = new FileReader(confFile);
+ parser = Xml.newPullParser();
+ parser.setInput(confreader);
+ XmlUtils.beginDocument(parser, "devices");
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (!"device".equals(parser.getName())) {
+ break;
+ }
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ names.add(name);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // It's ok if the file does not exist.
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
+ } finally {
+ try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+ }
+
+ return names.toArray(new String[names.size()]);
+ }
+
+ @SuppressWarnings("unused")
+ public int getMaxEventsPerSecond() {
+ int result = 0;
+ try {
+ result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec"));
+ } catch (NumberFormatException e) {
+ }
+ if (result < 1) {
+ result = 60;
+ }
+ return result;
+ }
+ }
+}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 5a995ae..ecad3cc 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -26,8 +26,7 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
-import com.android.server.status.IconData;
-import com.android.server.status.StatusBarService;
+import com.android.server.StatusBarManagerService;
import org.xmlpull.v1.XmlPullParserException;
@@ -113,9 +112,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final Context mContext;
final Handler mHandler;
final SettingsObserver mSettingsObserver;
- final StatusBarService mStatusBar;
- final IBinder mInputMethodIcon;
- final IconData mInputMethodData;
+ final StatusBarManagerService mStatusBar;
final IWindowManager mIWindowManager;
final HandlerCaller mCaller;
@@ -450,7 +447,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- public InputMethodManagerService(Context context, StatusBarService statusBar) {
+ public InputMethodManagerService(Context context, StatusBarManagerService statusBar) {
mContext = context;
mHandler = new Handler(this);
mIWindowManager = IWindowManager.Stub.asInterface(
@@ -470,9 +467,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
mStatusBar = statusBar;
- mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0);
- mInputMethodIcon = statusBar.addIcon(mInputMethodData, null);
- statusBar.setIconVisibility(mInputMethodIcon, false);
+ statusBar.setIconVisibility("ime", false);
buildInputMethodListLocked(mMethodList, mMethodMap);
@@ -480,8 +475,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mContext.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS);
Slog.i(TAG, "Enabled input methods: " + enabledStr);
- if (enabledStr == null) {
- Slog.i(TAG, "Enabled input methods has not been set, enabling all");
+ final String defaultIme = Settings.Secure.getString(mContext
+ .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ if (enabledStr == null || TextUtils.isEmpty(defaultIme)) {
+ Slog.i(TAG, "Enabled input methods or default IME has not been set, enabling all");
InputMethodInfo defIm = null;
StringBuilder sb = new StringBuilder(256);
final int N = mMethodList.size();
@@ -915,7 +912,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mEnabledSession = null;
mCurMethod = null;
}
- mStatusBar.setIconVisibility(mInputMethodIcon, false);
+ mStatusBar.setIconVisibility("ime", false);
}
public void onServiceDisconnected(ComponentName name) {
@@ -939,23 +936,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
public void updateStatusIcon(IBinder token, String packageName, int iconId) {
+ int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring setInputMethod of token: " + token);
+ Slog.w(TAG, "Ignoring setInputMethod of uid " + uid + " token: " + token);
return;
}
synchronized (mMethodMap) {
if (iconId == 0) {
if (DEBUG) Slog.d(TAG, "hide the small icon for the input method");
- mStatusBar.setIconVisibility(mInputMethodIcon, false);
+ mStatusBar.setIconVisibility("ime", false);
} else if (packageName != null) {
if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
- mInputMethodData.iconId = iconId;
- mInputMethodData.iconPackage = packageName;
- mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null);
- mStatusBar.setIconVisibility(mInputMethodIcon, true);
+ mStatusBar.setIcon("ime", packageName, iconId, 0);
+ mStatusBar.setIconVisibility("ime", true);
}
}
} finally {
@@ -988,7 +984,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
void setInputMethodLocked(String id) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
- throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
+ throw new IllegalArgumentException("Unknown id: " + id);
}
if (id.equals(mCurMethodId)) {
@@ -1015,6 +1011,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public boolean showSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
+ int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
synchronized (mMethodMap) {
@@ -1025,7 +1022,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// focus in the window manager, to allow this call to
// be made before input is started in it.
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
- Slog.w(TAG, "Ignoring showSoftInput of: " + client);
+ Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
return false;
}
} catch (RemoteException e) {
@@ -1079,6 +1076,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public boolean hideSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
+ int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
synchronized (mMethodMap) {
@@ -1089,7 +1087,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// focus in the window manager, to allow this call to
// be made before input is started in it.
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
- Slog.w(TAG, "Ignoring hideSoftInput of: " + client);
+ if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid "
+ + uid + ": " + client);
return false;
}
} catch (RemoteException e) {
@@ -1224,7 +1223,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
- Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client);
+ Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of uid "
+ + Binder.getCallingUid() + ": " + client);
}
mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
@@ -1242,7 +1242,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
} else if (mCurToken != token) {
- Slog.w(TAG, "Ignoring setInputMethod of token: " + token);
+ Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
+ + " token: " + token);
return;
}
@@ -1258,7 +1259,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public void hideMySoftInput(IBinder token, int flags) {
synchronized (mMethodMap) {
if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring hideInputMethod of token: " + token);
+ if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid "
+ + Binder.getCallingUid() + " token: " + token);
return;
}
long ident = Binder.clearCallingIdentity();
@@ -1273,7 +1275,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public void showMySoftInput(IBinder token, int flags) {
synchronized (mMethodMap) {
if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring hideInputMethod of token: " + token);
+ Slog.w(TAG, "Ignoring showMySoftInput of uid "
+ + Binder.getCallingUid() + " token: " + token);
return;
}
long ident = Binder.clearCallingIdentity();
@@ -1486,7 +1489,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
String defaultIme = Settings.Secure.getString(mContext
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
- if (!map.containsKey(defaultIme)) {
+ if (!TextUtils.isEmpty(defaultIme) && !map.containsKey(defaultIme)) {
if (chooseNewDefaultIMELocked()) {
updateFromSettingsLocked();
}
@@ -1738,8 +1741,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
p.println(" sessionRequested=" + ci.sessionRequested);
p.println(" curSession=" + ci.curSession);
}
- p.println(" mInputMethodIcon=" + mInputMethodIcon);
- p.println(" mInputMethodData=" + mInputMethodData);
p.println(" mCurMethodId=" + mCurMethodId);
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
@@ -1758,24 +1759,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
p.println(" mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn);
}
+ p.println(" ");
if (client != null) {
- p.println(" ");
pw.flush();
try {
client.client.asBinder().dump(fd, args);
} catch (RemoteException e) {
p.println("Input method client dead: " + e);
}
+ } else {
+ p.println("No input method client.");
}
+ p.println(" ");
if (method != null) {
- p.println(" ");
pw.flush();
try {
method.asBinder().dump(fd, args);
} catch (RemoteException e) {
p.println("Input method service dead: " + e);
}
+ } else {
+ p.println("No input method service.");
}
}
}
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
new file mode 100644
index 0000000..befc770
--- /dev/null
+++ b/services/java/com/android/server/InputWindow.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 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.view.InputChannel;
+
+/**
+ * Describes input-related window properties for use by the input dispatcher.
+ *
+ * @hide
+ */
+public final class InputWindow {
+ // The input channel associated with the window.
+ public InputChannel inputChannel;
+
+ // The window name.
+ public String name;
+
+ // Window layout params attributes. (WindowManager.LayoutParams)
+ public int layoutParamsFlags;
+ public int layoutParamsType;
+
+ // Dispatching timeout.
+ public long dispatchingTimeoutNanos;
+
+ // Window frame area.
+ public int frameLeft;
+ public int frameTop;
+ public int frameRight;
+ public int frameBottom;
+
+ // Window visible frame area.
+ public int visibleFrameLeft;
+ public int visibleFrameTop;
+ public int visibleFrameRight;
+ public int visibleFrameBottom;
+
+ // Window touchable area.
+ public int touchableAreaLeft;
+ public int touchableAreaTop;
+ public int touchableAreaRight;
+ public int touchableAreaBottom;
+
+ // Window is visible.
+ public boolean visible;
+
+ // Window can receive keys.
+ public boolean canReceiveKeys;
+
+ // Window has focus.
+ public boolean hasFocus;
+
+ // Window has wallpaper. (window is the current wallpaper target)
+ public boolean hasWallpaper;
+
+ // Input event dispatching is paused.
+ public boolean paused;
+
+ // Window layer.
+ public int layer;
+
+ // Id of process and user that owns the window.
+ public int ownerPid;
+ public int ownerUid;
+
+ public void recycle() {
+ inputChannel = null;
+ }
+}
diff --git a/services/java/com/android/server/InputWindowList.java b/services/java/com/android/server/InputWindowList.java
new file mode 100644
index 0000000..1cbb2cc
--- /dev/null
+++ b/services/java/com/android/server/InputWindowList.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 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;
+
+
+/**
+ * A specialized list of window information objects backed by an array.
+ *
+ * This class is part of an InputManager optimization to avoid allocating objects and arrays
+ * unnecessarily. Internally, it keeps an array full of demand-allocated objects that it
+ * recycles each time the list is cleared. The used portion of the array is padded with a null.
+ *
+ * The contents of the list are intended to be Z-ordered from top to bottom.
+ *
+ * @hide
+ */
+public final class InputWindowList {
+ private InputWindow[] mArray;
+ private int mCount;
+
+ /**
+ * Creates an empty list.
+ */
+ public InputWindowList() {
+ mArray = new InputWindow[8];
+ }
+
+ /**
+ * Clears the list.
+ */
+ public void clear() {
+ if (mCount == 0) {
+ return;
+ }
+
+ int count = mCount;
+ mCount = 0;
+ mArray[count] = mArray[0];
+ while (count > 0) {
+ count -= 1;
+ mArray[count].recycle();
+ }
+ mArray[0] = null;
+ }
+
+ /**
+ * Adds an uninitialized input window object to the list and returns it.
+ */
+ public InputWindow add() {
+ if (mCount + 1 == mArray.length) {
+ InputWindow[] oldArray = mArray;
+ mArray = new InputWindow[oldArray.length * 2];
+ System.arraycopy(oldArray, 0, mArray, 0, mCount);
+ }
+
+ // Grab object from tail (after used section) if available.
+ InputWindow item = mArray[mCount + 1];
+ if (item == null) {
+ item = new InputWindow();
+ }
+
+ mArray[mCount] = item;
+ mCount += 1;
+ mArray[mCount] = null;
+ return item;
+ }
+
+ /**
+ * Gets the input window objects as a null-terminated array.
+ * @return The input window array.
+ */
+ public InputWindow[] toNullTerminatedArray() {
+ return mArray;
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java
index 2eaa58c..85eca60 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/Installer.java
@@ -166,11 +166,17 @@ class Installer {
}
}
- public int install(String name, int uid, int gid) {
+ public int install(String name, boolean useEncryptedFilesystem, int uid, int gid) {
StringBuilder builder = new StringBuilder("install");
builder.append(' ');
builder.append(name);
builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
+ builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(gid);
@@ -203,33 +209,57 @@ class Installer {
return execute(builder.toString());
}
- public int remove(String name) {
+ public int remove(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("remove");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int rename(String oldname, String newname) {
+ public int rename(String oldname, String newname, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rename");
builder.append(' ');
builder.append(oldname);
builder.append(' ');
builder.append(newname);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int deleteCacheFiles(String name) {
+ public int deleteCacheFiles(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rmcache");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int clearUserData(String name) {
+ public int clearUserData(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rmuserdata");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
@@ -263,7 +293,7 @@ class Installer {
}
public int getSizeInfo(String pkgName, String apkPath,
- String fwdLockApkPath, PackageStats pStats) {
+ String fwdLockApkPath, PackageStats pStats, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
@@ -271,6 +301,13 @@ class Installer {
builder.append(apkPath);
builder.append(' ');
builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
+
String s = transaction(builder.toString());
String res[] = s.split(" ");
@@ -290,4 +327,33 @@ class Installer {
public int moveFiles() {
return execute("movefiles");
}
+
+ public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath) {
+ if (dataPath == null) {
+ Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
+ return -1;
+ } else if (nativeLibPath == null) {
+ Slog.e(TAG, "unlinkNativeLibraryDirectory nativeLibPath is null");
+ return -1;
+ }
+
+ StringBuilder builder = new StringBuilder("linklib ");
+ builder.append(dataPath);
+ builder.append(' ');
+ builder.append(nativeLibPath);
+
+ return execute(builder.toString());
+ }
+
+ public int unlinkNativeLibraryDirectory(String dataPath) {
+ if (dataPath == null) {
+ Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
+ return -1;
+ }
+
+ StringBuilder builder = new StringBuilder("unlinklib ");
+ builder.append(dataPath);
+
+ return execute(builder.toString());
+ }
}
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 8ab65e9..e47de13 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.Set;
import android.util.Log;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.LogPrinter;
import android.util.Printer;
@@ -92,10 +93,12 @@ public class IntentResolver<F extends IntentFilter, R extends Object> {
}
boolean dumpMap(PrintWriter out, String titlePrefix, String title,
- String prefix, Map<String, ArrayList<F>> map, String packageName) {
+ String prefix, Map<String, ArrayList<F>> map, String packageName,
+ boolean printFilter) {
String eprefix = prefix + " ";
String fprefix = prefix + " ";
boolean printedSomething = false;
+ Printer printer = null;
for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
ArrayList<F> a = e.getValue();
final int N = a.size();
@@ -115,37 +118,44 @@ public class IntentResolver<F extends IntentFilter, R extends Object> {
}
printedSomething = true;
dumpFilter(out, fprefix, filter);
+ if (printFilter) {
+ if (printer == null) {
+ printer = new PrintWriterPrinter(out);
+ }
+ filter.dump(printer, fprefix + " ");
+ }
}
}
return printedSomething;
}
- public boolean dump(PrintWriter out, String title, String prefix, String packageName) {
+ public boolean dump(PrintWriter out, String title, String prefix, String packageName,
+ boolean printFilter) {
String innerPrefix = prefix + " ";
String sepPrefix = "\n" + prefix;
String curPrefix = title + "\n" + prefix;
if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
- mTypeToFilter, packageName)) {
+ mTypeToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
- mBaseTypeToFilter, packageName)) {
+ mBaseTypeToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
- mWildTypeToFilter, packageName)) {
+ mWildTypeToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
- mSchemeToFilter, packageName)) {
+ mSchemeToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
- mActionToFilter, packageName)) {
+ mActionToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
- mTypedActionToFilter, packageName)) {
+ mTypedActionToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
return curPrefix == sepPrefix;
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
deleted file mode 100644
index 6d42141..0000000
--- a/services/java/com/android/server/KeyInputQueue.java
+++ /dev/null
@@ -1,1386 +0,0 @@
-/*
- * Copyright (C) 2007 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.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Environment;
-import android.os.LatencyTimer;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.Xml;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.RawInputEvent;
-import android.view.Surface;
-import android.view.WindowManagerPolicy;
-
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-public abstract class KeyInputQueue {
- static final String TAG = "KeyInputQueue";
-
- static final boolean DEBUG = false;
- static final boolean DEBUG_VIRTUAL_KEYS = false;
- static final boolean DEBUG_POINTERS = false;
-
- /**
- * Turn on some hacks we have to improve the touch interaction with a
- * certain device whose screen currently is not all that good.
- */
- static boolean BAD_TOUCH_HACK = false;
-
- /**
- * Turn on some hacks to improve touch interaction with another device
- * where touch coordinate data can get corrupted.
- */
- static boolean JUMPY_TOUCH_HACK = false;
-
- private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
-
- final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
- final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
- final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
- final HapticFeedbackCallback mHapticFeedbackCallback;
-
- int mGlobalMetaState = 0;
- boolean mHaveGlobalMetaState = false;
-
- final QueuedEvent mFirst;
- final QueuedEvent mLast;
- QueuedEvent mCache;
- int mCacheCount;
-
- Display mDisplay = null;
- int mDisplayWidth;
- int mDisplayHeight;
-
- int mOrientation = Surface.ROTATION_0;
- int[] mKeyRotationMap = null;
-
- VirtualKey mPressedVirtualKey = null;
-
- PowerManager.WakeLock mWakeLock;
-
- static final int[] KEY_90_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
- };
-
- static final int[] KEY_180_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
- };
-
- static final int[] KEY_270_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
- };
-
- public static final int FILTER_REMOVE = 0;
- public static final int FILTER_KEEP = 1;
- public static final int FILTER_ABORT = -1;
-
- private static final boolean MEASURE_LATENCY = false;
- private LatencyTimer lt;
-
- public interface FilterCallback {
- int filterEvent(QueuedEvent ev);
- }
-
- public interface HapticFeedbackCallback {
- void virtualKeyFeedback(KeyEvent event);
- }
-
- static class QueuedEvent {
- InputDevice inputDevice;
- long whenNano;
- int flags; // From the raw event
- int classType; // One of the class constants in InputEvent
- Object event;
- boolean inQueue;
-
- void copyFrom(QueuedEvent that) {
- this.inputDevice = that.inputDevice;
- this.whenNano = that.whenNano;
- this.flags = that.flags;
- this.classType = that.classType;
- this.event = that.event;
- }
-
- @Override
- public String toString() {
- return "QueuedEvent{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + event + "}";
- }
-
- // not copied
- QueuedEvent prev;
- QueuedEvent next;
- }
-
- /**
- * A key that exists as a part of the touch-screen, outside of the normal
- * display area of the screen.
- */
- static class VirtualKey {
- int scancode;
- int centerx;
- int centery;
- int width;
- int height;
-
- int hitLeft;
- int hitTop;
- int hitRight;
- int hitBottom;
-
- InputDevice lastDevice;
- int lastKeycode;
-
- boolean checkHit(int x, int y) {
- return (x >= hitLeft && x <= hitRight
- && y >= hitTop && y <= hitBottom);
- }
-
- void computeHitRect(InputDevice dev, int dw, int dh) {
- if (dev == lastDevice) {
- return;
- }
-
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode
- + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
-
- lastDevice = dev;
-
- int minx = dev.absX.minValue;
- int maxx = dev.absX.maxValue;
-
- int halfw = width/2;
- int left = centerx - halfw;
- int right = centerx + halfw;
- hitLeft = minx + ((left*maxx-minx)/dw);
- hitRight = minx + ((right*maxx-minx)/dw);
-
- int miny = dev.absY.minValue;
- int maxy = dev.absY.maxValue;
-
- int halfh = height/2;
- int top = centery - halfh;
- int bottom = centery + halfh;
- hitTop = miny + ((top*maxy-miny)/dh);
- hitBottom = miny + ((bottom*maxy-miny)/dh);
- }
- }
-
- private void readVirtualKeys(String deviceName) {
- try {
- FileInputStream fis = new FileInputStream(
- "/sys/board_properties/virtualkeys." + deviceName);
- InputStreamReader isr = new InputStreamReader(fis);
- BufferedReader br = new BufferedReader(isr, 2048);
- String str = br.readLine();
- if (str != null) {
- String[] it = str.split(":");
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
- final int N = it.length-6;
- for (int i=0; i<=N; i+=6) {
- if (!"0x01".equals(it[i])) {
- Slog.w(TAG, "Unknown virtual key type at elem #" + i
- + ": " + it[i]);
- continue;
- }
- try {
- VirtualKey sb = new VirtualKey();
- sb.scancode = Integer.parseInt(it[i+1]);
- sb.centerx = Integer.parseInt(it[i+2]);
- sb.centery = Integer.parseInt(it[i+3]);
- sb.width = Integer.parseInt(it[i+4]);
- sb.height = Integer.parseInt(it[i+5]);
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
- + sb.scancode + ": center=" + sb.centerx + ","
- + sb.centery + " size=" + sb.width + "x"
- + sb.height);
- mVirtualKeys.add(sb);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Bad number at region " + i + " in: "
- + str, e);
- }
- }
- }
- br.close();
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "No virtual keys found");
- } catch (IOException e) {
- Slog.w(TAG, "Error reading virtual keys", e);
- }
- }
-
- private void readExcludedDevices() {
- // Read partner-provided list of excluded input devices
- XmlPullParser parser = null;
- // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
- File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
- FileReader confreader = null;
- try {
- confreader = new FileReader(confFile);
- parser = Xml.newPullParser();
- parser.setInput(confreader);
- XmlUtils.beginDocument(parser, "devices");
-
- while (true) {
- XmlUtils.nextElement(parser);
- if (!"device".equals(parser.getName())) {
- break;
- }
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name);
- addExcludedDevice(name);
- }
- }
- } catch (FileNotFoundException e) {
- // It's ok if the file does not exist.
- } catch (Exception e) {
- Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
- } finally {
- try { if (confreader != null) confreader.close(); } catch (IOException e) { }
- }
- }
-
- KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
- if (MEASURE_LATENCY) {
- lt = new LatencyTimer(100, 1000);
- }
-
- Resources r = context.getResources();
- BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);
-
- JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);
-
- mHapticFeedbackCallback = hapticFeedbackCallback;
-
- readExcludedDevices();
-
- PowerManager pm = (PowerManager)context.getSystemService(
- Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "KeyInputQueue");
- mWakeLock.setReferenceCounted(false);
-
- mFirst = new QueuedEvent();
- mLast = new QueuedEvent();
- mFirst.next = mLast;
- mLast.prev = mFirst;
- }
-
- void start() {
- mThread.start();
- }
-
- public void setDisplay(Display display) {
- mDisplay = display;
-
- // We assume at this point that the display dimensions reflect the
- // natural, unrotated display. We will perform hit tests for soft
- // buttons based on that display.
- mDisplayWidth = display.getWidth();
- mDisplayHeight = display.getHeight();
- }
-
- public void getInputConfiguration(Configuration config) {
- synchronized (mFirst) {
- config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
- config.keyboard = Configuration.KEYBOARD_NOKEYS;
- config.navigation = Configuration.NAVIGATION_NONAV;
-
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice d = mDevices.valueAt(i);
- if (d != null) {
- if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- config.touchscreen
- = Configuration.TOUCHSCREEN_FINGER;
- //Slog.i("foo", "***** HAVE TOUCHSCREEN!");
- }
- if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
- config.keyboard
- = Configuration.KEYBOARD_QWERTY;
- //Slog.i("foo", "***** HAVE QWERTY!");
- }
- if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- config.navigation
- = Configuration.NAVIGATION_TRACKBALL;
- //Slog.i("foo", "***** HAVE TRACKBALL!");
- } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
- config.navigation
- = Configuration.NAVIGATION_DPAD;
- //Slog.i("foo", "***** HAVE DPAD!");
- }
- }
- }
- }
- }
-
- public int getScancodeState(int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.scancode == code) {
- return 2;
- }
- }
- return nativeGetScancodeState(code);
- }
- }
-
- public int getScancodeState(int deviceId, int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.scancode == code) {
- return 2;
- }
- }
- return nativeGetScancodeState(deviceId, code);
- }
- }
-
- public int getTrackballScancodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- int res = nativeGetScancodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getDPadScancodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
- int res = nativeGetScancodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getKeycodeState(int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.lastKeycode == code) {
- return 2;
- }
- }
- return nativeGetKeycodeState(code);
- }
- }
-
- public int getKeycodeState(int deviceId, int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.lastKeycode == code) {
- return 2;
- }
- }
- return nativeGetKeycodeState(deviceId, code);
- }
- }
-
- public int getTrackballKeycodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- int res = nativeGetKeycodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getDPadKeycodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
- int res = nativeGetKeycodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public static native String getDeviceName(int deviceId);
- public static native int getDeviceClasses(int deviceId);
- public static native void addExcludedDevice(String deviceName);
- public static native boolean getAbsoluteInfo(int deviceId, int axis,
- InputDevice.AbsoluteInfo outInfo);
- public static native int getSwitchState(int sw);
- public static native int getSwitchState(int deviceId, int sw);
- public static native int nativeGetScancodeState(int code);
- public static native int nativeGetScancodeState(int deviceId, int code);
- public static native int nativeGetKeycodeState(int code);
- public static native int nativeGetKeycodeState(int deviceId, int code);
- public static native int scancodeToKeycode(int deviceId, int scancode);
- public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
-
- public static KeyEvent newKeyEvent(InputDevice device, long downTime,
- long eventTime, boolean down, int keycode, int repeatCount,
- int scancode, int flags) {
- return new KeyEvent(
- downTime, eventTime,
- down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
- keycode, repeatCount,
- device != null ? device.mMetaKeysState : 0,
- device != null ? device.id : -1, scancode,
- flags | KeyEvent.FLAG_FROM_SYSTEM);
- }
-
- Thread mThread = new Thread("InputDeviceReader") {
- public void run() {
- if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()");
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
- RawInputEvent ev = new RawInputEvent();
- while (true) {
- try {
- InputDevice di;
-
- // block, doesn't release the monitor
- readEvent(ev);
-
- boolean send = false;
- boolean configChanged = false;
-
- if (false) {
- Slog.i(TAG, "Input event: dev=0x"
- + Integer.toHexString(ev.deviceId)
- + " type=0x" + Integer.toHexString(ev.type)
- + " scancode=" + ev.scancode
- + " keycode=" + ev.keycode
- + " value=" + ev.value);
- }
-
- if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
- synchronized (mFirst) {
- di = newInputDevice(ev.deviceId);
- if (di.classes != 0) {
- // If this device is some kind of input class,
- // we care about it.
- mDevices.put(ev.deviceId, di);
- if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- readVirtualKeys(di.name);
- }
- // The configuration may have changed because
- // of this device.
- configChanged = true;
- } else {
- // We won't do anything with this device.
- mIgnoredDevices.put(ev.deviceId, di);
- Slog.i(TAG, "Ignoring non-input device: id=0x"
- + Integer.toHexString(di.id)
- + ", name=" + di.name);
- }
- }
- } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
- synchronized (mFirst) {
- if (false) {
- Slog.i(TAG, "Device removed: id=0x"
- + Integer.toHexString(ev.deviceId));
- }
- di = mDevices.get(ev.deviceId);
- if (di != null) {
- mDevices.delete(ev.deviceId);
- // The configuration may have changed because
- // of this device.
- configChanged = true;
- } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
- mIgnoredDevices.remove(ev.deviceId);
- } else {
- Slog.w(TAG, "Removing bad device id: "
- + Integer.toHexString(ev.deviceId));
- continue;
- }
- }
- } else {
- di = getInputDevice(ev.deviceId);
- if (di == null) {
- // This may be some junk from an ignored device.
- continue;
- }
-
- // first crack at it
- send = preprocessEvent(di, ev);
-
- if (ev.type == RawInputEvent.EV_KEY) {
- di.mMetaKeysState = makeMetaState(ev.keycode,
- ev.value != 0, di.mMetaKeysState);
- mHaveGlobalMetaState = false;
- }
- }
-
- if (configChanged) {
- synchronized (mFirst) {
- addLocked(di, System.nanoTime(), 0,
- RawInputEvent.CLASS_CONFIGURATION_CHANGED,
- null);
- }
- }
-
- if (!send) {
- continue;
- }
-
- synchronized (mFirst) {
- // NOTE: The event timebase absolutely must be the same
- // timebase as SystemClock.uptimeMillis().
- //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
- final long curTime = SystemClock.uptimeMillis();
- final long curTimeNano = System.nanoTime();
- //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
-
- final int classes = di.classes;
- final int type = ev.type;
- final int scancode = ev.scancode;
- send = false;
-
- // Is it a key event?
- if (type == RawInputEvent.EV_KEY &&
- (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
- (scancode < RawInputEvent.BTN_FIRST ||
- scancode > RawInputEvent.BTN_LAST)) {
- boolean down;
- if (ev.value != 0) {
- down = true;
- di.mKeyDownTime = curTime;
- } else {
- down = false;
- }
- int keycode = rotateKeyCodeLocked(ev.keycode);
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_KEYBOARD,
- newKeyEvent(di, di.mKeyDownTime, curTime, down,
- keycode, 0, scancode,
- ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
- ? KeyEvent.FLAG_WOKE_HERE : 0));
-
- } else if (ev.type == RawInputEvent.EV_KEY) {
- // Single touch protocol: touch going down or up.
- if (ev.scancode == RawInputEvent.BTN_TOUCH &&
- (classes&(RawInputEvent.CLASS_TOUCHSCREEN
- |RawInputEvent.CLASS_TOUCHSCREEN_MT))
- == RawInputEvent.CLASS_TOUCHSCREEN) {
- di.mAbs.changed = true;
- di.mAbs.mDown[0] = ev.value != 0;
-
- // Trackball (mouse) protocol: press down or up.
- } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
- (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- di.mRel.changed = true;
- di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
- send = true;
- }
-
- // Process position events from multitouch protocol.
- } else if (ev.type == RawInputEvent.EV_ABS &&
- (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
- if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_PRESSURE] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_X] = ev.value;
- if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
- + di.mAbs.mAddingPointerOffset
- + " X:" + ev.value);
- } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_Y] = ev.value;
- if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
- + di.mAbs.mAddingPointerOffset
- + " Y:" + ev.value);
- } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_SIZE] = ev.value;
- }
-
- // Process position events from single touch protocol.
- } else if (ev.type == RawInputEvent.EV_ABS &&
- (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- if (ev.scancode == RawInputEvent.ABS_X) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_Y) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
- di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
- + MotionEvent.SAMPLE_PRESSURE] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
- di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
- + MotionEvent.SAMPLE_SIZE] = ev.value;
- }
-
- // Process movement events from trackball (mouse) protocol.
- } else if (ev.type == RawInputEvent.EV_REL &&
- (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- // Add this relative movement into our totals.
- if (ev.scancode == RawInputEvent.REL_X) {
- di.mRel.changed = true;
- di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
- } else if (ev.scancode == RawInputEvent.REL_Y) {
- di.mRel.changed = true;
- di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
- }
- }
-
- // Handle multitouch protocol sync: tells us that the
- // driver has returned all data for -one- of the pointers
- // that is currently down.
- if (ev.type == RawInputEvent.EV_SYN
- && ev.scancode == RawInputEvent.SYN_MT_REPORT
- && di.mAbs != null) {
- di.mAbs.changed = true;
- if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
- // If the value is <= 0, the pointer is not
- // down, so keep it in the count.
-
- if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_PRESSURE] != 0) {
- final int num = di.mAbs.mNextNumPointers+1;
- di.mAbs.mNextNumPointers = num;
- if (DEBUG_POINTERS) Slog.v(TAG,
- "MT_REPORT: now have " + num + " pointers");
- final int newOffset = (num <= InputDevice.MAX_POINTERS)
- ? (num * MotionEvent.NUM_SAMPLE_DATA)
- : (InputDevice.MAX_POINTERS *
- MotionEvent.NUM_SAMPLE_DATA);
- di.mAbs.mAddingPointerOffset = newOffset;
- di.mAbs.mNextData[newOffset
- + MotionEvent.SAMPLE_PRESSURE] = 0;
- } else {
- if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer");
- }
- }
-
- // Handle general event sync: all data for the current
- // event update has been delivered.
- } else if (send || (ev.type == RawInputEvent.EV_SYN
- && ev.scancode == RawInputEvent.SYN_REPORT)) {
- if (mDisplay != null) {
- if (!mHaveGlobalMetaState) {
- computeGlobalMetaStateLocked();
- }
-
- MotionEvent me;
-
- InputDevice.MotionState ms = di.mAbs;
- if (ms.changed) {
- ms.everChanged = true;
- ms.changed = false;
-
- if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
- |RawInputEvent.CLASS_TOUCHSCREEN_MT))
- == RawInputEvent.CLASS_TOUCHSCREEN) {
- ms.mNextNumPointers = 0;
- if (ms.mDown[0]) {
- System.arraycopy(di.curTouchVals, 0,
- ms.mNextData, 0,
- MotionEvent.NUM_SAMPLE_DATA);
- ms.mNextNumPointers++;
- }
- }
-
- if (BAD_TOUCH_HACK) {
- ms.dropBadPoint(di);
- }
- if (JUMPY_TOUCH_HACK) {
- ms.dropJumpyPoint(di);
- }
-
- boolean doMotion = !monitorVirtualKey(di,
- ev, curTime, curTimeNano);
-
- if (doMotion && ms.mNextNumPointers > 0
- && (ms.mLastNumPointers == 0
- || ms.mSkipLastPointers)) {
- doMotion = !generateVirtualKeyDown(di,
- ev, curTime, curTimeNano);
- }
-
- if (doMotion) {
- // XXX Need to be able to generate
- // multiple events here, for example
- // if two fingers change up/down state
- // at the same time.
- do {
- me = ms.generateAbsMotion(di, curTime,
- curTimeNano, mDisplay,
- mOrientation, mGlobalMetaState);
- if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x="
- + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
- + " y="
- + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
- + " ev=" + me);
- if (me != null) {
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i(TAG, "Enqueueing: " + me);
- }
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_TOUCHSCREEN, me);
- }
- } while (ms.hasMore());
- } else {
- // We are consuming movement in the
- // virtual key area... but still
- // propagate this to the previous
- // data for comparisons.
- int num = ms.mNextNumPointers;
- if (num > InputDevice.MAX_POINTERS) {
- num = InputDevice.MAX_POINTERS;
- }
- System.arraycopy(ms.mNextData, 0,
- ms.mLastData, 0,
- num * MotionEvent.NUM_SAMPLE_DATA);
- ms.mLastNumPointers = num;
- ms.mSkipLastPointers = true;
- }
-
- ms.finish();
- }
-
- ms = di.mRel;
- if (ms.changed) {
- ms.everChanged = true;
- ms.changed = false;
-
- me = ms.generateRelMotion(di, curTime,
- curTimeNano,
- mOrientation, mGlobalMetaState);
- if (false) Slog.v(TAG, "Relative: x="
- + di.mRel.mNextData[MotionEvent.SAMPLE_X]
- + " y="
- + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
- + " ev=" + me);
- if (me != null) {
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_TRACKBALL, me);
- }
- }
- }
- }
- }
-
- } catch (RuntimeException exc) {
- Slog.e(TAG, "InputReaderThread uncaught exception", exc);
- }
- }
- }
- };
-
- private boolean isInsideDisplay(InputDevice dev) {
- final InputDevice.AbsoluteInfo absx = dev.absX;
- final InputDevice.AbsoluteInfo absy = dev.absY;
- final InputDevice.MotionState absm = dev.mAbs;
- if (absx == null || absy == null || absm == null) {
- return true;
- }
-
- if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
- && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
- && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
- && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input ("
- + absm.mNextData[MotionEvent.SAMPLE_X]
- + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
- + ") inside of display");
- return true;
- }
-
- return false;
- }
-
- private VirtualKey findVirtualKey(InputDevice dev) {
- final int N = mVirtualKeys.size();
- if (N <= 0) {
- return null;
- }
-
- final InputDevice.MotionState absm = dev.mAbs;
- for (int i=0; i<N; i++) {
- VirtualKey sb = mVirtualKeys.get(i);
- sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test ("
- + absm.mNextData[MotionEvent.SAMPLE_X] + ","
- + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
- + sb.scancode + " - (" + sb.hitLeft
- + "," + sb.hitTop + ")-(" + sb.hitRight + ","
- + sb.hitBottom + ")");
- if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
- absm.mNextData[MotionEvent.SAMPLE_Y])) {
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!");
- return sb;
- }
- }
-
- return null;
- }
-
- private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
- long curTime, long curTimeNano) {
- if (isInsideDisplay(di)) {
- // Didn't consume event.
- return false;
- }
-
-
- VirtualKey vk = findVirtualKey(di);
- if (vk != null) {
- final InputDevice.MotionState ms = di.mAbs;
- mPressedVirtualKey = vk;
- vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
- ms.mLastNumPointers = ms.mNextNumPointers;
- di.mKeyDownTime = curTime;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG,
- "Generate key down for: " + vk.scancode
- + " (keycode=" + vk.lastKeycode + ")");
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- }
-
- // We always consume the event, even if we didn't
- // generate a key event. There are two reasons for
- // this: to avoid spurious touches when holding
- // the edges of the device near the touchscreen,
- // and to avoid reporting events if there are virtual
- // keys on the touchscreen outside of the display
- // area.
- // Note that for all of this we are only looking at the
- // first pointer, since what we are handling here is the
- // first pointer going down, and this is the coordinate
- // that will be used to dispatch the event.
- if (false) {
- final InputDevice.AbsoluteInfo absx = di.absX;
- final InputDevice.AbsoluteInfo absy = di.absY;
- final InputDevice.MotionState absm = di.mAbs;
- Slog.v(TAG, "Rejecting ("
- + absm.mNextData[MotionEvent.SAMPLE_X] + ","
- + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
- + absx.minValue + "," + absy.minValue
- + ")-(" + absx.maxValue + ","
- + absx.maxValue + ")");
- }
- return true;
- }
-
- private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
- long curTime, long curTimeNano) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk == null) {
- return false;
- }
-
- final InputDevice.MotionState ms = di.mAbs;
- if (ms.mNextNumPointers <= 0) {
- mPressedVirtualKey = null;
- ms.mLastNumPointers = 0;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode);
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- return true;
-
- } else if (isInsideDisplay(di)) {
- // Whoops the pointer has moved into
- // the display area! Cancel the
- // virtual key and start a pointer
- // motion.
- mPressedVirtualKey = null;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode);
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- ms.mLastNumPointers = 0;
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns a new meta state for the given keys and old state.
- */
- private static final int makeMetaState(int keycode, boolean down, int old) {
- int mask;
- switch (keycode) {
- case KeyEvent.KEYCODE_ALT_LEFT:
- mask = KeyEvent.META_ALT_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_ALT_RIGHT:
- mask = KeyEvent.META_ALT_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- mask = KeyEvent.META_SHIFT_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- mask = KeyEvent.META_SHIFT_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_SYM:
- mask = KeyEvent.META_SYM_ON;
- break;
- default:
- return old;
- }
- int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
- & (down ? (old | mask) : (old & ~mask));
- if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
- result |= KeyEvent.META_ALT_ON;
- }
- if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
- result |= KeyEvent.META_SHIFT_ON;
- }
- return result;
- }
-
- private void computeGlobalMetaStateLocked() {
- int i = mDevices.size();
- mGlobalMetaState = 0;
- while ((--i) >= 0) {
- mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
- }
- mHaveGlobalMetaState = true;
- }
-
- /*
- * Return true if you want the event to get passed on to the
- * rest of the system, and false if you've handled it and want
- * it dropped.
- */
- abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
-
- InputDevice getInputDevice(int deviceId) {
- synchronized (mFirst) {
- return getInputDeviceLocked(deviceId);
- }
- }
-
- private InputDevice getInputDeviceLocked(int deviceId) {
- return mDevices.get(deviceId);
- }
-
- public void setOrientation(int orientation) {
- synchronized(mFirst) {
- mOrientation = orientation;
- switch (orientation) {
- case Surface.ROTATION_90:
- mKeyRotationMap = KEY_90_MAP;
- break;
- case Surface.ROTATION_180:
- mKeyRotationMap = KEY_180_MAP;
- break;
- case Surface.ROTATION_270:
- mKeyRotationMap = KEY_270_MAP;
- break;
- default:
- mKeyRotationMap = null;
- break;
- }
- }
- }
-
- public int rotateKeyCode(int keyCode) {
- synchronized(mFirst) {
- return rotateKeyCodeLocked(keyCode);
- }
- }
-
- private int rotateKeyCodeLocked(int keyCode) {
- int[] map = mKeyRotationMap;
- if (map != null) {
- final int N = map.length;
- for (int i=0; i<N; i+=2) {
- if (map[i] == keyCode) {
- return map[i+1];
- }
- }
- }
- return keyCode;
- }
-
- boolean hasEvents() {
- synchronized (mFirst) {
- return mFirst.next != mLast;
- }
- }
-
- /*
- * returns true if we returned an event, and false if we timed out
- */
- QueuedEvent getEvent(long timeoutMS) {
- long begin = SystemClock.uptimeMillis();
- final long end = begin+timeoutMS;
- long now = begin;
- synchronized (mFirst) {
- while (mFirst.next == mLast && end > now) {
- try {
- mWakeLock.release();
- mFirst.wait(end-now);
- }
- catch (InterruptedException e) {
- }
- now = SystemClock.uptimeMillis();
- if (begin > now) {
- begin = now;
- }
- }
- if (mFirst.next == mLast) {
- return null;
- }
- QueuedEvent p = mFirst.next;
- mFirst.next = p.next;
- mFirst.next.prev = mFirst;
- p.inQueue = false;
- return p;
- }
- }
-
- /**
- * Return true if the queue has an up event pending that corresponds
- * to the same key as the given key event.
- */
- boolean hasKeyUpEvent(KeyEvent origEvent) {
- synchronized (mFirst) {
- final int keyCode = origEvent.getKeyCode();
- QueuedEvent cur = mLast.prev;
- while (cur.prev != null) {
- if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
- KeyEvent ke = (KeyEvent)cur.event;
- if (ke.getAction() == KeyEvent.ACTION_UP
- && ke.getKeyCode() == keyCode) {
- return true;
- }
- }
- cur = cur.prev;
- }
- }
-
- return false;
- }
-
- void recycleEvent(QueuedEvent ev) {
- synchronized (mFirst) {
- //Slog.i(TAG, "Recycle event: " + ev);
- if (ev.event == ev.inputDevice.mAbs.currentMove) {
- ev.inputDevice.mAbs.currentMove = null;
- }
- if (ev.event == ev.inputDevice.mRel.currentMove) {
- if (false) Slog.i(TAG, "Detach rel " + ev.event);
- ev.inputDevice.mRel.currentMove = null;
- ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
- ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
- }
- recycleLocked(ev);
- }
- }
-
- void filterQueue(FilterCallback cb) {
- synchronized (mFirst) {
- QueuedEvent cur = mLast.prev;
- while (cur.prev != null) {
- switch (cb.filterEvent(cur)) {
- case FILTER_REMOVE:
- cur.prev.next = cur.next;
- cur.next.prev = cur.prev;
- break;
- case FILTER_ABORT:
- return;
- }
- cur = cur.prev;
- }
- }
- }
-
- private QueuedEvent obtainLocked(InputDevice device, long whenNano,
- int flags, int classType, Object event) {
- QueuedEvent ev;
- if (mCacheCount == 0) {
- ev = new QueuedEvent();
- } else {
- ev = mCache;
- ev.inQueue = false;
- mCache = ev.next;
- mCacheCount--;
- }
- ev.inputDevice = device;
- ev.whenNano = whenNano;
- ev.flags = flags;
- ev.classType = classType;
- ev.event = event;
- return ev;
- }
-
- private void recycleLocked(QueuedEvent ev) {
- if (ev.inQueue) {
- throw new RuntimeException("Event already in queue!");
- }
- if (mCacheCount < 10) {
- mCacheCount++;
- ev.next = mCache;
- mCache = ev;
- ev.inQueue = true;
- }
- }
-
- private void addLocked(InputDevice device, long whenNano, int flags,
- int classType, Object event) {
- boolean poke = mFirst.next == mLast;
-
- QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
- QueuedEvent p = mLast.prev;
- while (p != mFirst && ev.whenNano < p.whenNano) {
- p = p.prev;
- }
-
- ev.next = p.next;
- ev.prev = p;
- p.next = ev;
- ev.next.prev = ev;
- ev.inQueue = true;
-
- if (poke) {
- long time;
- if (MEASURE_LATENCY) {
- time = System.nanoTime();
- }
- mFirst.notify();
- mWakeLock.acquire();
- if (MEASURE_LATENCY) {
- lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
- }
- }
- }
-
- private InputDevice newInputDevice(int deviceId) {
- int classes = getDeviceClasses(deviceId);
- String name = getDeviceName(deviceId);
- InputDevice.AbsoluteInfo absX = null;
- InputDevice.AbsoluteInfo absY = null;
- InputDevice.AbsoluteInfo absPressure = null;
- InputDevice.AbsoluteInfo absSize = null;
- if (classes != 0) {
- Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
- + ", name=" + name
- + ", classes=" + Integer.toHexString(classes));
- if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
- absX = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_POSITION_X, "X");
- absY = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_POSITION_Y, "Y");
- absPressure = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
- absSize = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
- } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- absX = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_X, "X");
- absY = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_Y, "Y");
- absPressure = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_PRESSURE, "Pressure");
- absSize = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_TOOL_WIDTH, "Size");
- }
- }
-
- return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
- }
-
- private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
- String name) {
- InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
- if (getAbsoluteInfo(id, channel, info)
- && info.minValue != info.maxValue) {
- Slog.i(TAG, " " + name + ": min=" + info.minValue
- + " max=" + info.maxValue
- + " flat=" + info.flat
- + " fuzz=" + info.fuzz);
- info.range = info.maxValue-info.minValue;
- return info;
- }
- Slog.i(TAG, " " + name + ": unknown values");
- return null;
- }
- private static native boolean readEvent(RawInputEvent outEvent);
-
- void dump(PrintWriter pw, String prefix) {
- synchronized (mFirst) {
- for (int i=0; i<mDevices.size(); i++) {
- InputDevice dev = mDevices.valueAt(i);
- pw.print(prefix); pw.print("Device #");
- pw.print(mDevices.keyAt(i)); pw.print(" ");
- pw.print(dev.name); pw.print(" (classes=0x");
- pw.print(Integer.toHexString(dev.classes));
- pw.println("):");
- pw.print(prefix); pw.print(" mKeyDownTime=");
- pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState=");
- pw.println(dev.mMetaKeysState);
- if (dev.absX != null) {
- pw.print(prefix); pw.print(" absX: "); dev.absX.dump(pw);
- pw.println("");
- }
- if (dev.absY != null) {
- pw.print(prefix); pw.print(" absY: "); dev.absY.dump(pw);
- pw.println("");
- }
- if (dev.absPressure != null) {
- pw.print(prefix); pw.print(" absPressure: ");
- dev.absPressure.dump(pw); pw.println("");
- }
- if (dev.absSize != null) {
- pw.print(prefix); pw.print(" absSize: ");
- dev.absSize.dump(pw); pw.println("");
- }
- if (dev.mAbs.everChanged) {
- pw.print(prefix); pw.println(" mAbs:");
- dev.mAbs.dump(pw, prefix + " ");
- }
- if (dev.mRel.everChanged) {
- pw.print(prefix); pw.println(" mRel:");
- dev.mRel.dump(pw, prefix + " ");
- }
- }
- pw.println(" ");
- for (int i=0; i<mIgnoredDevices.size(); i++) {
- InputDevice dev = mIgnoredDevices.valueAt(i);
- pw.print(prefix); pw.print("Ignored Device #");
- pw.print(mIgnoredDevices.keyAt(i)); pw.print(" ");
- pw.print(dev.name); pw.print(" (classes=0x");
- pw.print(Integer.toHexString(dev.classes));
- pw.println(")");
- }
- pw.println(" ");
- for (int i=0; i<mVirtualKeys.size(); i++) {
- VirtualKey vk = mVirtualKeys.get(i);
- pw.print(prefix); pw.print("Virtual Key #");
- pw.print(i); pw.println(":");
- pw.print(prefix); pw.print(" scancode="); pw.println(vk.scancode);
- pw.print(prefix); pw.print(" centerx="); pw.print(vk.centerx);
- pw.print(" centery="); pw.print(vk.centery);
- pw.print(" width="); pw.print(vk.width);
- pw.print(" height="); pw.println(vk.height);
- pw.print(prefix); pw.print(" hitLeft="); pw.print(vk.hitLeft);
- pw.print(" hitTop="); pw.print(vk.hitTop);
- pw.print(" hitRight="); pw.print(vk.hitRight);
- pw.print(" hitBottom="); pw.println(vk.hitBottom);
- if (vk.lastDevice != null) {
- pw.print(prefix); pw.print(" lastDevice=#");
- pw.println(vk.lastDevice.id);
- }
- if (vk.lastKeycode != 0) {
- pw.print(prefix); pw.print(" lastKeycode=");
- pw.println(vk.lastKeycode);
- }
- }
- pw.println(" ");
- pw.print(prefix); pw.print(" Default keyboard: ");
- pw.println(SystemProperties.get("hw.keyboards.0.devname"));
- pw.print(prefix); pw.print(" mGlobalMetaState=");
- pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState=");
- pw.println(mHaveGlobalMetaState);
- pw.print(prefix); pw.print(" mDisplayWidth=");
- pw.print(mDisplayWidth); pw.print(" mDisplayHeight=");
- pw.println(mDisplayHeight);
- pw.print(prefix); pw.print(" mOrientation=");
- pw.println(mOrientation);
- if (mPressedVirtualKey != null) {
- pw.print(prefix); pw.print(" mPressedVirtualKey.scancode=");
- pw.println(mPressedVirtualKey.scancode);
- }
- }
- }
-}
diff --git a/services/java/com/android/server/LoadAverageService.java b/services/java/com/android/server/LoadAverageService.java
index 0d86429..b6baadb 100644
--- a/services/java/com/android/server/LoadAverageService.java
+++ b/services/java/com/android/server/LoadAverageService.java
@@ -272,7 +272,7 @@ public class LoadAverageService extends Service {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 65f4194..8644ceb 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,17 +16,6 @@
package com.android.server;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Observable;
-import java.util.Observer;
-import java.util.Set;
-
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -41,6 +30,7 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.location.Address;
+import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
@@ -50,7 +40,6 @@ import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -63,17 +52,34 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import android.util.PrintWriterPrinter;
-import com.android.internal.location.GeocoderProxy;
-import com.android.internal.location.GpsLocationProvider;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.location.GpsNetInitiatedHandler;
-import com.android.internal.location.LocationProviderProxy;
-import com.android.internal.location.MockProvider;
-import com.android.internal.location.PassiveProvider;
+
+import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationProviderInterface;
+import com.android.server.location.LocationProviderProxy;
+import com.android.server.location.MockProvider;
+import com.android.server.location.PassiveProvider;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Set;
/**
* The service class that manages LocationProviders and issues location
@@ -111,17 +117,20 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private static boolean sProvidersLoaded = false;
private final Context mContext;
+ private final String mNetworkLocationProviderPackageName;
+ private final String mGeocodeProviderPackageName;
private GeocoderProxy mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
// Cache the real providers for use in addTestProvider() and removeTestProvider()
- LocationProviderInterface mNetworkLocationProvider;
+ LocationProviderProxy mNetworkLocationProvider;
LocationProviderInterface mGpsLocationProvider;
// Handler messages
private static final int MESSAGE_LOCATION_CHANGED = 1;
+ private static final int MESSAGE_PACKAGE_UPDATED = 2;
// wakelock variables
private final static String WAKELOCK_KEY = "LocationManagerService";
@@ -153,6 +162,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
new HashMap<String,ArrayList<UpdateRecord>>();
+ /**
+ * Temporary filled in when computing min time for a provider. Access is
+ * protected by global lock mLock.
+ */
+ private final WorkSource mTmpWorkSource = new WorkSource();
+
// Proximity listeners
private Receiver mProximityReceiver = null;
private ILocationListener mProximityListener = null;
@@ -209,15 +224,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public String toString() {
+ String result;
if (mListener != null) {
- return "Receiver{"
+ result = "Receiver{"
+ Integer.toHexString(System.identityHashCode(this))
+ " Listener " + mKey + "}";
} else {
- return "Receiver{"
+ result = "Receiver{"
+ Integer.toHexString(System.identityHashCode(this))
+ " Intent " + mKey + "}";
}
+ result += "mUpdateRecords: " + mUpdateRecords;
+ return result;
}
public boolean isListener() {
@@ -461,19 +479,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mEnabledProviders.add(passiveProvider.getName());
// initialize external network location and geocoder services
- Resources resources = mContext.getResources();
- String serviceName = resources.getString(
- com.android.internal.R.string.config_networkLocationProvider);
- if (serviceName != null) {
+ if (mNetworkLocationProviderPackageName != null) {
mNetworkLocationProvider =
new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
- serviceName, mLocationHandler);
+ mNetworkLocationProviderPackageName, mLocationHandler);
addProvider(mNetworkLocationProvider);
}
- serviceName = resources.getString(com.android.internal.R.string.config_geocodeProvider);
- if (serviceName != null) {
- mGeocodeProvider = new GeocoderProxy(mContext, serviceName);
+ if (mGeocodeProviderPackageName != null) {
+ mGeocodeProvider = new GeocoderProxy(mContext, mGeocodeProviderPackageName);
}
updateProvidersLocked();
@@ -485,6 +499,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
public LocationManagerService(Context context) {
super();
mContext = context;
+ Resources resources = context.getResources();
+ mNetworkLocationProviderPackageName = resources.getString(
+ com.android.internal.R.string.config_networkLocationProvider);
+ mGeocodeProviderPackageName = resources.getString(
+ com.android.internal.R.string.config_geocodeProvider);
+ mPackageMonitor.register(context, true);
if (LOCAL_LOGV) {
Slog.v(TAG, "Constructed LocationManager Service");
@@ -609,10 +629,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return out;
}
- public List<String> getProviders(boolean enabledOnly) {
+ public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
try {
synchronized (mLock) {
- return _getProvidersLocked(enabledOnly);
+ return _getProvidersLocked(criteria, enabledOnly);
}
} catch (SecurityException se) {
throw se;
@@ -622,7 +642,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private List<String> _getProvidersLocked(boolean enabledOnly) {
+ private List<String> _getProvidersLocked(Criteria criteria, boolean enabledOnly) {
if (LOCAL_LOGV) {
Slog.v(TAG, "getProviders");
}
@@ -634,25 +654,242 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (enabledOnly && !isAllowedBySettingsLocked(name)) {
continue;
}
+ if (criteria != null && !p.meetsCriteria(criteria)) {
+ continue;
+ }
out.add(name);
}
}
return out;
}
+ /**
+ * Returns the next looser power requirement, in the sequence:
+ *
+ * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
+ */
+ private int nextPower(int power) {
+ switch (power) {
+ case Criteria.POWER_LOW:
+ return Criteria.POWER_MEDIUM;
+ case Criteria.POWER_MEDIUM:
+ return Criteria.POWER_HIGH;
+ case Criteria.POWER_HIGH:
+ return Criteria.NO_REQUIREMENT;
+ case Criteria.NO_REQUIREMENT:
+ default:
+ return Criteria.NO_REQUIREMENT;
+ }
+ }
+
+ /**
+ * Returns the next looser accuracy requirement, in the sequence:
+ *
+ * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
+ */
+ private int nextAccuracy(int accuracy) {
+ if (accuracy == Criteria.ACCURACY_FINE) {
+ return Criteria.ACCURACY_COARSE;
+ } else {
+ return Criteria.NO_REQUIREMENT;
+ }
+ }
+
+ private class LpPowerComparator implements Comparator<LocationProviderInterface> {
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ // Smaller is better
+ return (l1.getPowerRequirement() - l2.getPowerRequirement());
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (l1.getPowerRequirement() == l2.getPowerRequirement());
+ }
+ }
+
+ private class LpAccuracyComparator implements Comparator<LocationProviderInterface> {
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ // Smaller is better
+ return (l1.getAccuracy() - l2.getAccuracy());
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (l1.getAccuracy() == l2.getAccuracy());
+ }
+ }
+
+ private class LpCapabilityComparator implements Comparator<LocationProviderInterface> {
+
+ private static final int ALTITUDE_SCORE = 4;
+ private static final int BEARING_SCORE = 4;
+ private static final int SPEED_SCORE = 4;
+
+ private int score(LocationProviderInterface p) {
+ return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
+ (p.supportsBearing() ? BEARING_SCORE : 0) +
+ (p.supportsSpeed() ? SPEED_SCORE : 0);
+ }
+
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (score(l2) - score(l1)); // Bigger is better
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (score(l1) == score(l2));
+ }
+ }
+
+ private LocationProviderInterface best(List<String> providerNames) {
+ ArrayList<LocationProviderInterface> providers;
+ synchronized (mLock) {
+ providers = new ArrayList<LocationProviderInterface>(mProviders.size());
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ providers.add(mProviders.get(i));
+ }
+ }
+
+ if (providers.size() < 2) {
+ return providers.get(0);
+ }
+
+ // First, sort by power requirement
+ Collections.sort(providers, new LpPowerComparator());
+ int power = providers.get(0).getPowerRequirement();
+ if (power < providers.get(1).getPowerRequirement()) {
+ return providers.get(0);
+ }
+
+ int idx, size;
+
+ ArrayList<LocationProviderInterface> tmp = new ArrayList<LocationProviderInterface>();
+ idx = 0;
+ size = providers.size();
+ while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
+ tmp.add(providers.get(idx));
+ idx++;
+ }
+
+ // Next, sort by accuracy
+ Collections.sort(tmp, new LpAccuracyComparator());
+ int acc = tmp.get(0).getAccuracy();
+ if (acc < tmp.get(1).getAccuracy()) {
+ return tmp.get(0);
+ }
+
+ ArrayList<LocationProviderInterface> tmp2 = new ArrayList<LocationProviderInterface>();
+ idx = 0;
+ size = tmp.size();
+ while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
+ tmp2.add(tmp.get(idx));
+ idx++;
+ }
+
+ // Finally, sort by capability "score"
+ Collections.sort(tmp2, new LpCapabilityComparator());
+ return tmp2.get(0);
+ }
+
+ /**
+ * Returns the name of the provider that best meets the given criteria. Only providers
+ * that are permitted to be accessed by the calling activity will be
+ * returned. If several providers meet the criteria, the one with the best
+ * accuracy is returned. If no provider meets the criteria,
+ * the criteria are loosened in the following sequence:
+ *
+ * <ul>
+ * <li> power requirement
+ * <li> accuracy
+ * <li> bearing
+ * <li> speed
+ * <li> altitude
+ * </ul>
+ *
+ * <p> Note that the requirement on monetary cost is not removed
+ * in this process.
+ *
+ * @param criteria the criteria that need to be matched
+ * @param enabledOnly if true then only a provider that is currently enabled is returned
+ * @return name of the provider that best matches the requirements
+ */
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+ List<String> goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Make a copy of the criteria that we can modify
+ criteria = new Criteria(criteria);
+
+ // Loosen power requirement
+ int power = criteria.getPowerRequirement();
+ while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
+ power = nextPower(power);
+ criteria.setPowerRequirement(power);
+ goodProviders = getProviders(criteria, enabledOnly);
+ }
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Loosen accuracy requirement
+ int accuracy = criteria.getAccuracy();
+ while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
+ accuracy = nextAccuracy(accuracy);
+ criteria.setAccuracy(accuracy);
+ goodProviders = getProviders(criteria, enabledOnly);
+ }
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove bearing requirement
+ criteria.setBearingRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove speed requirement
+ criteria.setSpeedRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove altitude requirement
+ criteria.setAltitudeRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ return null;
+ }
+
+ public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) {
+ throw new IllegalArgumentException("provider=" + provider);
+ }
+ return p.meetsCriteria(criteria);
+ }
+
private void updateProvidersLocked() {
+ boolean changesMade = false;
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface p = mProviders.get(i);
boolean isEnabled = p.isEnabled();
String name = p.getName();
boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
-
if (isEnabled && !shouldBeEnabled) {
updateProviderListenersLocked(name, false);
+ changesMade = true;
} else if (!isEnabled && shouldBeEnabled) {
updateProviderListenersLocked(name, true);
+ changesMade = true;
}
-
+ }
+ if (changesMade) {
+ mContext.sendBroadcast(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
}
}
@@ -691,7 +928,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (enabled) {
p.enable();
if (listeners > 0) {
- p.setMinTime(getMinTimeLocked(provider));
+ p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
p.enableLocationTracking(true);
}
} else {
@@ -703,9 +940,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private long getMinTimeLocked(String provider) {
long minTime = Long.MAX_VALUE;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+ mTmpWorkSource.clear();
if (records != null) {
for (int i=records.size()-1; i>=0; i--) {
- minTime = Math.min(minTime, records.get(i).mMinTime);
+ UpdateRecord ur = records.get(i);
+ long curTime = ur.mMinTime;
+ if (curTime < minTime) {
+ minTime = curTime;
+ }
+ }
+ long inclTime = (minTime*3)/2;
+ for (int i=records.size()-1; i>=0; i--) {
+ UpdateRecord ur = records.get(i);
+ if (ur.mMinTime <= inclTime) {
+ mTmpWorkSource.add(ur.mUid);
+ }
}
}
return minTime;
@@ -716,6 +965,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
final Receiver mReceiver;
final long mMinTime;
final float mMinDistance;
+ final boolean mSingleShot;
final int mUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
@@ -723,12 +973,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, long minTime, float minDistance,
+ UpdateRecord(String provider, long minTime, float minDistance, boolean singleShot,
Receiver receiver, int uid) {
mProvider = provider;
mReceiver = receiver;
mMinTime = minTime;
mMinDistance = minDistance;
+ mSingleShot = singleShot;
mUid = uid;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -756,13 +1007,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
public String toString() {
return "UpdateRecord{"
+ Integer.toHexString(System.identityHashCode(this))
- + " " + mProvider + " " + mReceiver + "}";
+ + " mProvider: " + mProvider + " mUid: " + mUid + "}";
}
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + this);
pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
+ pw.println(prefix + "mSingleShot=" + mSingleShot);
pw.println(prefix + "mUid=" + mUid);
pw.println(prefix + "mLastFixBroadcast:");
if (mLastFixBroadcast != null) {
@@ -818,12 +1070,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return false;
}
- public void requestLocationUpdates(String provider,
- long minTime, float minDistance, ILocationListener listener) {
-
+ public void requestLocationUpdates(String provider, Criteria criteria,
+ long minTime, float minDistance, boolean singleShot, ILocationListener listener) {
+ if (criteria != null) {
+ // FIXME - should we consider using multiple providers simultaneously
+ // rather than only the best one?
+ // Should we do anything different for single shot fixes?
+ provider = getBestProvider(criteria, true);
+ if (provider == null) {
+ throw new IllegalArgumentException("no providers found for criteria");
+ }
+ }
try {
synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
+ requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
+ getReceiver(listener));
}
} catch (SecurityException se) {
throw se;
@@ -834,11 +1095,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- public void requestLocationUpdatesPI(String provider,
- long minTime, float minDistance, PendingIntent intent) {
+ public void requestLocationUpdatesPI(String provider, Criteria criteria,
+ long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
+ if (criteria != null) {
+ // FIXME - should we consider using multiple providers simultaneously
+ // rather than only the best one?
+ // Should we do anything different for single shot fixes?
+ provider = getBestProvider(criteria, true);
+ if (provider == null) {
+ throw new IllegalArgumentException("no providers found for criteria");
+ }
+ }
try {
synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
+ requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
+ getReceiver(intent));
}
} catch (SecurityException se) {
throw se;
@@ -849,11 +1120,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private void requestLocationUpdatesLocked(String provider,
- long minTime, float minDistance, Receiver receiver) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "_requestLocationUpdates: listener = " + receiver);
- }
+ private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance,
+ boolean singleShot, Receiver receiver) {
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
@@ -867,7 +1135,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
boolean newUid = !providerHasListener(provider, callingUid, null);
long identity = Binder.clearCallingIdentity();
try {
- UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
+ UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, singleShot,
+ receiver, callingUid);
UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
if (oldRecord != null) {
oldRecord.disposeLocked();
@@ -880,12 +1149,19 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
if (isProviderEnabled) {
long minTimeForProvider = getMinTimeLocked(provider);
- p.setMinTime(minTimeForProvider);
- p.enableLocationTracking(true);
+ p.setMinTime(minTimeForProvider, mTmpWorkSource);
+ // try requesting single shot if singleShot is true, and fall back to
+ // regular location tracking if requestSingleShotFix() is not supported
+ if (!singleShot || !p.requestSingleShotFix()) {
+ p.enableLocationTracking(true);
+ }
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(provider, false);
}
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "_requestLocationUpdates: provider = " + provider + " listener = " + receiver);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -974,7 +1250,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
LocationProviderInterface p = mProvidersByName.get(provider);
if (p != null) {
if (hasOtherListener) {
- p.setMinTime(getMinTimeLocked(provider));
+ p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
} else {
p.enableLocationTracking(false);
}
@@ -1287,7 +1563,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface provider = mProviders.get(i);
- requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
+ requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f,
+ false, mProximityReceiver);
}
}
}
@@ -1368,8 +1645,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
} catch (SecurityException se) {
throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
} catch (Exception e) {
Slog.e(TAG, "isProviderEnabled got exception:", e);
return false;
@@ -1393,20 +1668,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
+ return false;
}
return isAllowedBySettingsLocked(provider);
}
public Location getLastKnownLocation(String provider) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "getLastKnownLocation: " + provider);
+ }
try {
synchronized (mLock) {
return _getLastKnownLocationLocked(provider);
}
} catch (SecurityException se) {
throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
} catch (Exception e) {
Slog.e(TAG, "getLastKnownLocation got exception:", e);
return null;
@@ -1418,7 +1694,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
+ return null;
}
if (!isAllowedBySettingsLocked(provider)) {
@@ -1485,6 +1761,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
for (int i=0; i<N; i++) {
UpdateRecord r = records.get(i);
Receiver receiver = r.mReceiver;
+ boolean receiverDead = false;
Location lastLoc = r.mLastFixBroadcast;
if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
@@ -1496,10 +1773,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
if (!receiver.callLocationChangedLocked(location)) {
Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- deadReceivers.add(receiver);
+ receiverDead = true;
}
}
@@ -1509,13 +1783,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
r.mLastStatusBroadcast = newStatusUpdateTime;
if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+ receiverDead = true;
Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- if (!deadReceivers.contains(receiver)) {
- deadReceivers.add(receiver);
- }
+ }
+ }
+
+ // remove receiver if it is dead or we just processed a single shot request
+ if (receiverDead || r.mSingleShot) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<Receiver>();
+ }
+ if (!deadReceivers.contains(receiver)) {
+ deadReceivers.add(receiver);
}
}
}
@@ -1554,6 +1833,19 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
handleLocationChangedLocked(location, passive);
}
}
+ } else if (msg.what == MESSAGE_PACKAGE_UPDATED) {
+ String packageName = (String) msg.obj;
+ String packageDot = packageName + ".";
+
+ // reconnect to external providers after their packages have been updated
+ if (mNetworkLocationProvider != null &&
+ mNetworkLocationProviderPackageName.startsWith(packageDot)) {
+ mNetworkLocationProvider.reconnect();
+ }
+ if (mGeocodeProvider != null &&
+ mGeocodeProviderPackageName.startsWith(packageDot)) {
+ mGeocodeProvider.reconnect();
+ }
}
} catch (Exception e) {
// Log, don't crash!
@@ -1653,6 +1945,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
};
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ // Called by main thread; divert work to LocationWorker.
+ Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
+ }
+ };
+
// Wake locks
private void incrementPendingBroadcasts() {
@@ -1692,6 +1992,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Geocoder
+ public boolean geocoderIsPresent() {
+ return mGeocodeProvider != null;
+ }
+
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
if (mGeocodeProvider != null) {
diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java
index 27a8a74..bdb5a24 100644
--- a/services/java/com/android/server/MasterClearReceiver.java
+++ b/services/java/com/android/server/MasterClearReceiver.java
@@ -29,7 +29,7 @@ public class MasterClearReceiver extends BroadcastReceiver {
private static final String TAG = "MasterClear";
@Override
- public void onReceive(Context context, Intent intent) {
+ public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
if (!"google.com".equals(intent.getStringExtra("from"))) {
Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
@@ -37,12 +37,23 @@ public class MasterClearReceiver extends BroadcastReceiver {
}
}
- try {
- Slog.w(TAG, "!!! FACTORY RESET !!!");
- RecoverySystem.rebootWipeUserData(context);
- Log.wtf(TAG, "Still running after master clear?!");
- } catch (IOException e) {
- Slog.e(TAG, "Can't perform master clear/factory reset", e);
- }
+ Slog.w(TAG, "!!! FACTORY RESET !!!");
+ // The reboot call is blocking, so we need to do it on another thread.
+ Thread thr = new Thread("Reboot") {
+ @Override
+ public void run() {
+ try {
+ if (intent.hasExtra("enableEFS")) {
+ RecoverySystem.rebootToggleEFS(context, intent.getBooleanExtra("enableEFS", false));
+ } else {
+ RecoverySystem.rebootWipeUserData(context);
+ }
+ Log.wtf(TAG, "Still running after master clear?!");
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't perform master clear/factory reset", e);
+ }
+ }
+ };
+ thr.start();
}
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index cb55808..85bb3aa 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,31 +16,56 @@
package com.android.server;
+import com.android.internal.app.IMediaContainerService;
import com.android.server.am.ActivityManagerService;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.content.res.ObbInfo;
import android.net.Uri;
-import android.os.storage.IMountService;
-import android.os.storage.IMountServiceListener;
-import android.os.storage.IMountShutdownObserver;
-import android.os.storage.StorageResultCode;
+import android.os.Binder;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Environment;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.os.storage.IMountShutdownObserver;
+import android.os.storage.IObbActionListener;
+import android.os.storage.OnObbStateChangeListener;
+import android.os.storage.StorageResultCode;
import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
/**
* MountService implements back-end services for platform storage
@@ -53,9 +78,12 @@ class MountService extends IMountService.Stub
private static final boolean LOCAL_LOGD = false;
private static final boolean DEBUG_UNMOUNT = false;
private static final boolean DEBUG_EVENTS = false;
-
+ private static final boolean DEBUG_OBB = true;
+
private static final String TAG = "MountService";
+ private static final String VOLD_TAG = "VoldConnector";
+
/*
* Internal vold volume state constants
*/
@@ -130,6 +158,114 @@ class MountService extends IMountService.Stub
*/
final private HashSet<String> mAsecMountSet = new HashSet<String>();
+ /**
+ * The size of the crypto algorithm key in bits for OBB files. Currently
+ * Twofish is used which takes 128-bit keys.
+ */
+ private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
+
+ /**
+ * The number of times to run SHA1 in the PBKDF2 function for OBB files.
+ * 1024 is reasonably secure and not too slow.
+ */
+ private static final int PBKDF2_HASH_ROUNDS = 1024;
+
+ /**
+ * Mounted OBB tracking information. Used to track the current state of all
+ * OBBs.
+ */
+ final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
+ final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
+
+ class ObbState implements IBinder.DeathRecipient {
+ public ObbState(String filename, int callerUid, IObbActionListener token, int nonce)
+ throws RemoteException {
+ this.filename = filename;
+ this.callerUid = callerUid;
+ this.token = token;
+ this.nonce = nonce;
+ }
+
+ // OBB source filename
+ String filename;
+
+ // Binder.callingUid()
+ final public int callerUid;
+
+ // Token of remote Binder caller
+ final IObbActionListener token;
+
+ // Identifier to pass back to the token
+ final int nonce;
+
+ public IBinder getBinder() {
+ return token.asBinder();
+ }
+
+ @Override
+ public void binderDied() {
+ ObbAction action = new UnmountObbAction(this, true);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+ }
+
+ public void link() throws RemoteException {
+ getBinder().linkToDeath(this, 0);
+ }
+
+ public void unlink() {
+ getBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("ObbState{");
+ sb.append("filename=");
+ sb.append(filename);
+ sb.append(",token=");
+ sb.append(token.toString());
+ sb.append(",callerUid=");
+ sb.append(callerUid);
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ // OBB Action Handler
+ final private ObbActionHandler mObbActionHandler;
+
+ // OBB action handler messages
+ private static final int OBB_RUN_ACTION = 1;
+ private static final int OBB_MCS_BOUND = 2;
+ private static final int OBB_MCS_UNBIND = 3;
+ private static final int OBB_MCS_RECONNECT = 4;
+ private static final int OBB_FLUSH_MOUNT_STATE = 5;
+
+ /*
+ * Default Container Service information
+ */
+ static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
+
+ final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
+
+ class DefaultContainerConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "onServiceConnected");
+ IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "onServiceDisconnected");
+ }
+ };
+
+ // Used in the ObbActionHandler
+ private IMediaContainerService mContainerService = null;
+
+ // Handler messages
private static final int H_UNMOUNT_PM_UPDATE = 1;
private static final int H_UNMOUNT_PM_DONE = 2;
private static final int H_UNMOUNT_MS = 3;
@@ -137,9 +273,9 @@ class MountService extends IMountService.Stub
private static final int MAX_UNMOUNT_RETRIES = 4;
class UnmountCallBack {
- String path;
+ final String path;
+ final boolean force;
int retries;
- boolean force;
UnmountCallBack(String path, boolean force) {
retries = 0;
@@ -154,7 +290,7 @@ class MountService extends IMountService.Stub
}
class UmsEnableCallBack extends UnmountCallBack {
- String method;
+ final String method;
UmsEnableCallBack(String path, String method, boolean force) {
super(path, force);
@@ -287,7 +423,7 @@ class MountService extends IMountService.Stub
Slog.w(TAG, "Waiting too long for mReady!");
}
}
-
+
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -344,12 +480,12 @@ class MountService extends IMountService.Stub
MountServiceBinderListener(IMountServiceListener listener) {
mListener = listener;
-
+
}
public void binderDied() {
if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
- synchronized(mListeners) {
+ synchronized (mListeners) {
mListeners.remove(this);
mListener.asBinder().unlinkToDeath(this, 0);
}
@@ -380,12 +516,23 @@ class MountService extends IMountService.Stub
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
return;
}
- // Update state on PackageManager
+
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
+ // Tell the package manager the media is gone.
mPms.updateExternalMediaStatus(false, false);
+
+ /*
+ * Some OBBs might have been unmounted when this volume was
+ * unmounted, so send a message to the handler to let it know to
+ * remove those from the list of mounted OBBS.
+ */
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
+ path));
} else if (Environment.MEDIA_MOUNTED.equals(state)) {
+ // Tell the package manager the media is available for use.
mPms.updateExternalMediaStatus(true, false);
}
+
String oldState = mLegacyState;
mLegacyState = state;
@@ -642,10 +789,21 @@ class MountService extends IMountService.Stub
}
private boolean doGetShareMethodAvailable(String method) {
- ArrayList<String> rsp = mConnector.doCommand("share status " + method);
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("share status " + method);
+ } catch (NativeDaemonConnectorException ex) {
+ Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
+ return false;
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ Slog.e(TAG, "Malformed response to share status " + method);
+ return false;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
@@ -727,6 +885,15 @@ class MountService extends IMountService.Stub
if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
return VoldResponseCode.OpFailedVolNotMounted;
}
+
+ /*
+ * Force a GC to make sure AssetManagers in other threads of the
+ * system_server are cleaned up. We have to do this since AssetManager
+ * instances are kept as a WeakReference and it's possible we have files
+ * open on the external storage.
+ */
+ Runtime.getRuntime().gc();
+
// Redundant probably. But no harm in updating state again.
mPms.updateExternalMediaStatus(false, false);
try {
@@ -770,10 +937,22 @@ class MountService extends IMountService.Stub
private boolean doGetVolumeShared(String path, String method) {
String cmd = String.format("volume shared %s %s", path, method);
- ArrayList<String> rsp = mConnector.doCommand(cmd);
+ ArrayList<String> rsp;
+
+ try {
+ rsp = mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException ex) {
+ Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
+ return false;
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
+ return false;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
@@ -782,9 +961,7 @@ class MountService extends IMountService.Stub
return false;
}
if (code == VoldResponseCode.ShareEnabledResult) {
- if (tok[2].equals("enabled"))
- return true;
- return false;
+ return "enabled".equals(tok[2]);
} else {
Slog.e(TAG, String.format("Unexpected response code %d", code));
return false;
@@ -873,6 +1050,9 @@ class MountService extends IMountService.Stub
mHandlerThread.start();
mHandler = new MountServiceHandler(mHandlerThread.getLooper());
+ // Add OBB Action Handler to MountService thread.
+ mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
+
/*
* Vold does not run in the simulator, so pretend the connector thread
* ran and did its thing.
@@ -883,9 +1063,15 @@ class MountService extends IMountService.Stub
return;
}
- mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
+ /*
+ * Create the connection to vold with a maximum queue of twice the
+ * amount of containers we'd ever expect to have. This keeps an
+ * "asec list" from blocking a thread repeatedly.
+ */
+ mConnector = new NativeDaemonConnector(this, "vold",
+ PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
mReady = false;
- Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
+ Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
}
@@ -1159,6 +1345,14 @@ class MountService extends IMountService.Stub
waitForReady();
warnOnNotMounted();
+ /*
+ * Force a GC to make sure AssetManagers in other threads of the
+ * system_server are cleaned up. We have to do this since AssetManager
+ * instances are kept as a WeakReference and it's possible we have files
+ * open on the external storage.
+ */
+ Runtime.getRuntime().gc();
+
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
@@ -1223,6 +1417,14 @@ class MountService extends IMountService.Stub
}
}
+ /*
+ * Force a GC to make sure AssetManagers in other threads of the
+ * system_server are cleaned up. We have to do this since AssetManager
+ * instances are kept as a WeakReference and it's possible we have files
+ * open on the external storage.
+ */
+ Runtime.getRuntime().gc();
+
int rc = StorageResultCode.OperationSucceeded;
String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
try {
@@ -1306,5 +1508,600 @@ class MountService extends IMountService.Stub
public void finishMediaUpdate() {
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
+
+ private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
+ if (callerUid == android.os.Process.SYSTEM_UID) {
+ return true;
+ }
+
+ if (packageName == null) {
+ return false;
+ }
+
+ final int packageUid = mPms.getPackageUid(packageName);
+
+ if (DEBUG_OBB) {
+ Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
+ packageUid + ", callerUid = " + callerUid);
+ }
+
+ return callerUid == packageUid;
+ }
+
+ public String getMountedObbPath(String filename) {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
+ waitForReady();
+ warnOnNotMounted();
+
+ try {
+ ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
+ String []tok = rsp.get(0).split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code != VoldResponseCode.AsecPathResult) {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ return tok[1];
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageNotFound) {
+ return null;
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ }
+
+ public boolean isObbMounted(String filename) {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
+ synchronized (mObbMounts) {
+ return mObbPathToStateMap.containsKey(filename);
+ }
+ }
+
+ public void mountObb(String filename, String key, IObbActionListener token, int nonce)
+ throws RemoteException {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
+ if (token == null) {
+ throw new IllegalArgumentException("token cannot be null");
+ }
+
+ final int callerUid = Binder.getCallingUid();
+ final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
+ final ObbAction action = new MountObbAction(obbState, key);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
+ }
+
+ public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
+ throws RemoteException {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
+ final int callerUid = Binder.getCallingUid();
+ final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
+ final ObbAction action = new UnmountObbAction(obbState, force);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
+ }
+
+ private void addObbStateLocked(ObbState obbState) throws RemoteException {
+ final IBinder binder = obbState.getBinder();
+ List<ObbState> obbStates = mObbMounts.get(binder);
+
+ if (obbStates == null) {
+ obbStates = new ArrayList<ObbState>();
+ mObbMounts.put(binder, obbStates);
+ } else {
+ for (final ObbState o : obbStates) {
+ if (o.filename.equals(obbState.filename)) {
+ throw new IllegalStateException("Attempt to add ObbState twice. "
+ + "This indicates an error in the MountService logic.");
+ }
+ }
+ }
+
+ obbStates.add(obbState);
+ try {
+ obbState.link();
+ } catch (RemoteException e) {
+ /*
+ * The binder died before we could link it, so clean up our state
+ * and return failure.
+ */
+ obbStates.remove(obbState);
+ if (obbStates.isEmpty()) {
+ mObbMounts.remove(binder);
+ }
+
+ // Rethrow the error so mountObb can get it
+ throw e;
+ }
+
+ mObbPathToStateMap.put(obbState.filename, obbState);
+ }
+
+ private void removeObbStateLocked(ObbState obbState) {
+ final IBinder binder = obbState.getBinder();
+ final List<ObbState> obbStates = mObbMounts.get(binder);
+ if (obbStates != null) {
+ if (obbStates.remove(obbState)) {
+ obbState.unlink();
+ }
+ if (obbStates.isEmpty()) {
+ mObbMounts.remove(binder);
+ }
+ }
+
+ mObbPathToStateMap.remove(obbState.filename);
+ }
+
+ private class ObbActionHandler extends Handler {
+ private boolean mBound = false;
+ private final List<ObbAction> mActions = new LinkedList<ObbAction>();
+
+ ObbActionHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OBB_RUN_ACTION: {
+ final ObbAction action = (ObbAction) msg.obj;
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
+
+ // If a bind was already initiated we don't really
+ // need to do anything. The pending install
+ // will be processed later on.
+ if (!mBound) {
+ // If this is the only one pending we might
+ // have to bind to the service again.
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ action.handleError();
+ return;
+ }
+ }
+
+ mActions.add(action);
+ break;
+ }
+ case OBB_MCS_BOUND: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_BOUND");
+ if (msg.obj != null) {
+ mContainerService = (IMediaContainerService) msg.obj;
+ }
+ if (mContainerService == null) {
+ // Something seriously wrong. Bail out
+ Slog.e(TAG, "Cannot bind to media container service");
+ for (ObbAction action : mActions) {
+ // Indicate service bind error
+ action.handleError();
+ }
+ mActions.clear();
+ } else if (mActions.size() > 0) {
+ final ObbAction action = mActions.get(0);
+ if (action != null) {
+ action.execute(this);
+ }
+ } else {
+ // Should never happen ideally.
+ Slog.w(TAG, "Empty queue");
+ }
+ break;
+ }
+ case OBB_MCS_RECONNECT: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_RECONNECT");
+ if (mActions.size() > 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ for (ObbAction action : mActions) {
+ // Indicate service bind error
+ action.handleError();
+ }
+ mActions.clear();
+ }
+ }
+ break;
+ }
+ case OBB_MCS_UNBIND: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_UNBIND");
+
+ // Delete pending install
+ if (mActions.size() > 0) {
+ mActions.remove(0);
+ }
+ if (mActions.size() == 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ } else {
+ // There are more pending requests in queue.
+ // Just post MCS_BOUND message to trigger processing
+ // of next pending install.
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+ }
+ break;
+ }
+ case OBB_FLUSH_MOUNT_STATE: {
+ final String path = (String) msg.obj;
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Flushing all OBB state for path " + path);
+
+ synchronized (mObbMounts) {
+ final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
+
+ final Iterator<Entry<String, ObbState>> i =
+ mObbPathToStateMap.entrySet().iterator();
+ while (i.hasNext()) {
+ final Entry<String, ObbState> obbEntry = i.next();
+
+ /*
+ * If this entry's source file is in the volume path
+ * that got unmounted, remove it because it's no
+ * longer valid.
+ */
+ if (obbEntry.getKey().startsWith(path)) {
+ obbStatesToRemove.add(obbEntry.getValue());
+ }
+ }
+
+ for (final ObbState obbState : obbStatesToRemove) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Removing state for " + obbState.filename);
+
+ removeObbStateLocked(obbState);
+
+ try {
+ obbState.token.onObbResult(obbState.filename, obbState.nonce,
+ OnObbStateChangeListener.UNMOUNTED);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Couldn't send unmount notification for OBB: "
+ + obbState.filename);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private boolean connectToService() {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Trying to bind to DefaultContainerService");
+
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+ if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
+ mBound = true;
+ return true;
+ }
+ return false;
+ }
+
+ private void disconnectService() {
+ mContainerService = null;
+ mBound = false;
+ mContext.unbindService(mDefContainerConn);
+ }
+ }
+
+ abstract class ObbAction {
+ private static final int MAX_RETRIES = 3;
+ private int mRetries;
+
+ ObbState mObbState;
+
+ ObbAction(ObbState obbState) {
+ mObbState = obbState;
+ }
+
+ public void execute(ObbActionHandler handler) {
+ try {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Starting to execute action: " + this.toString());
+ mRetries++;
+ if (mRetries > MAX_RETRIES) {
+ Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
+ handleError();
+ return;
+ } else {
+ handleExecute();
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Posting install MCS_UNBIND");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Posting install MCS_RECONNECT");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
+ } catch (Exception e) {
+ if (DEBUG_OBB)
+ Slog.d(TAG, "Error handling OBB action", e);
+ handleError();
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
+ }
+ }
+
+ abstract void handleExecute() throws RemoteException, IOException;
+ abstract void handleError();
+
+ protected ObbInfo getObbInfo() throws IOException {
+ ObbInfo obbInfo;
+ try {
+ obbInfo = mContainerService.getObbInfo(mObbState.filename);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
+ + mObbState.filename);
+ obbInfo = null;
+ }
+ if (obbInfo == null) {
+ throw new IOException("Couldn't read OBB file: " + mObbState.filename);
+ }
+ return obbInfo;
+ }
+
+ protected void sendNewStatusOrIgnore(int status) {
+ if (mObbState == null || mObbState.token == null) {
+ return;
+ }
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ }
+ }
+
+ class MountObbAction extends ObbAction {
+ private String mKey;
+
+ MountObbAction(ObbState obbState, String key) {
+ super(obbState);
+ mKey = key;
+ }
+
+ public void handleExecute() throws IOException, RemoteException {
+ waitForReady();
+ warnOnNotMounted();
+
+ final ObbInfo obbInfo = getObbInfo();
+
+ if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
+ Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
+ + " which is owned by " + obbInfo.packageName);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ return;
+ }
+
+ final boolean isMounted;
+ synchronized (mObbMounts) {
+ isMounted = mObbPathToStateMap.containsKey(obbInfo.filename);
+ }
+ if (isMounted) {
+ Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
+ return;
+ }
+
+ /*
+ * The filename passed in might not be the canonical name, so just
+ * set the filename to the canonicalized version.
+ */
+ mObbState.filename = obbInfo.filename;
+
+ final String hashedKey;
+ if (mKey == null) {
+ hashedKey = "none";
+ } else {
+ try {
+ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+
+ KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
+ PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
+ SecretKey key = factory.generateSecret(ks);
+ BigInteger bi = new BigInteger(key.getEncoded());
+ hashedKey = bi.toString(16);
+ } catch (NoSuchAlgorithmException e) {
+ Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
+ return;
+ } catch (InvalidKeySpecException e) {
+ Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
+ return;
+ }
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey,
+ mObbState.callerUid);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code != VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ if (DEBUG_OBB)
+ Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename);
+
+ synchronized (mObbMounts) {
+ addObbStateLocked(mObbState);
+ }
+
+ sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
+ } else {
+ Slog.e(TAG, "Couldn't mount OBB file: " + rc);
+
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
+ }
+ }
+
+ public void handleError() {
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("MountObbAction{");
+ sb.append("filename=");
+ sb.append(mObbState.filename);
+ sb.append(",callerUid=");
+ sb.append(mObbState.callerUid);
+ sb.append(",token=");
+ sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
+ sb.append(",binder=");
+ sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ class UnmountObbAction extends ObbAction {
+ private boolean mForceUnmount;
+
+ UnmountObbAction(ObbState obbState, boolean force) {
+ super(obbState);
+ mForceUnmount = force;
+ }
+
+ public void handleExecute() throws IOException {
+ waitForReady();
+ warnOnNotMounted();
+
+ final ObbInfo obbInfo = getObbInfo();
+
+ final ObbState obbState;
+ synchronized (mObbMounts) {
+ obbState = mObbPathToStateMap.get(obbInfo.filename);
+ }
+
+ if (obbState == null) {
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
+ return;
+ }
+
+ if (obbState.callerUid != mObbState.callerUid) {
+ Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename
+ + " (owned by " + obbInfo.packageName + ")");
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ return;
+ }
+
+ mObbState.filename = obbInfo.filename;
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb unmount %s%s", mObbState.filename,
+ (mForceUnmount ? " force" : ""));
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedStorageBusy;
+ } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
+ // If it's not mounted then we've already won.
+ rc = StorageResultCode.OperationSucceeded;
+ } else {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ synchronized (mObbMounts) {
+ removeObbStateLocked(obbState);
+ }
+
+ sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
+ } else {
+ Slog.w(TAG, "Could not mount OBB: " + mObbState.filename);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
+ }
+ }
+
+ public void handleError() {
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("UnmountObbAction{");
+ sb.append("filename=");
+ sb.append(mObbState.filename != null ? mObbState.filename : "null");
+ sb.append(",force=");
+ sb.append(mForceUnmount);
+ sb.append(",callerUid=");
+ sb.append(mObbState.callerUid);
+ sb.append(",token=");
+ sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
+ sb.append(",binder=");
+ sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump ActivityManager from from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " without permission " + android.Manifest.permission.DUMP);
+ return;
+ }
+
+ synchronized (mObbMounts) {
+ pw.println(" mObbMounts:");
+
+ final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator();
+ while (binders.hasNext()) {
+ Entry<IBinder, List<ObbState>> e = binders.next();
+ pw.print(" Key="); pw.println(e.getKey().toString());
+ final List<ObbState> obbStates = e.getValue();
+ for (final ObbState obbState : obbStates) {
+ pw.print(" "); pw.println(obbState.toString());
+ }
+ }
+
+ pw.println("");
+ pw.println(" mObbPathToStateMap:");
+ final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
+ while (maps.hasNext()) {
+ final Entry<String, ObbState> e = maps.next();
+ pw.print(" "); pw.print(e.getKey());
+ pw.print(" -> "); pw.println(e.getValue().toString());
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 08d7ce6..7b68d68 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -109,6 +109,10 @@ final class NativeDaemonConnector implements Runnable {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
if (count < 0) break;
+ // Add our starting point to the count and reset the start.
+ count += start;
+ start = 0;
+
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
String event = new String(buffer, start, i - start);
@@ -128,12 +132,11 @@ final class NativeDaemonConnector implements Runnable {
Slog.e(TAG, String.format(
"Error handling '%s'", event), ex);
}
- } else {
- try {
- mResponseQueue.put(event);
- } catch (InterruptedException ex) {
- Slog.e(TAG, "Failed to put response onto queue", ex);
- }
+ }
+ try {
+ mResponseQueue.put(event);
+ } catch (InterruptedException ex) {
+ Slog.e(TAG, "Failed to put response onto queue", ex);
}
} catch (NumberFormatException nfe) {
Slog.w(TAG, String.format("Bad msg (%s)", event));
@@ -141,6 +144,9 @@ final class NativeDaemonConnector implements Runnable {
start = i + 1;
}
}
+
+ // We should end at the amount we read. If not, compact then
+ // buffer and read again.
if (start != count) {
final int remaining = BUFFER_SIZE - start;
System.arraycopy(buffer, start, buffer, 0, remaining);
@@ -174,7 +180,8 @@ final class NativeDaemonConnector implements Runnable {
}
}
- private void sendCommand(String command) {
+ private void sendCommand(String command)
+ throws NativeDaemonConnectorException {
sendCommand(command, null);
}
@@ -184,11 +191,13 @@ final class NativeDaemonConnector implements Runnable {
* @param command The command to send to the daemon
* @param argument The argument to send with the command (or null)
*/
- private void sendCommand(String command, String argument) {
+ private void sendCommand(String command, String argument)
+ throws NativeDaemonConnectorException {
synchronized (this) {
if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument));
if (mOutputStream == null) {
Slog.e(TAG, "No connection to daemon", new IllegalStateException());
+ throw new NativeDaemonConnectorException("No output stream!");
} else {
StringBuilder builder = new StringBuilder(command);
if (argument != null) {
@@ -218,6 +227,7 @@ final class NativeDaemonConnector implements Runnable {
while (!complete) {
try {
+ // TODO - this should not block forever
String line = mResponseQueue.take();
if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line));
String[] tokens = line.split(" ");
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index cbbc7be..f0acdc0 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -35,6 +35,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
+import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import android.provider.Settings;
import android.content.ContentResolver;
@@ -46,6 +47,7 @@ import java.lang.IllegalStateException;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.concurrent.CountDownLatch;
/**
* @hide
@@ -53,6 +55,8 @@ import java.net.UnknownHostException;
class NetworkManagementService extends INetworkManagementService.Stub {
private static final String TAG = "NetworkManagmentService";
+ private static final boolean DBG = false;
+ private static final String NETD_TAG = "NetdConnector";
class NetdResponseCode {
public static final int InterfaceListResult = 110;
@@ -83,6 +87,9 @@ class NetworkManagementService extends INetworkManagementService.Stub {
*/
private NativeDaemonConnector mConnector;
+ private Thread mThread;
+ private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
+
private ArrayList<INetworkManagementEventObserver> mObservers;
/**
@@ -90,9 +97,8 @@ class NetworkManagementService extends INetworkManagementService.Stub {
*
* @param context Binder context for this service
*/
- public NetworkManagementService(Context context) {
+ private NetworkManagementService(Context context) {
mContext = context;
-
mObservers = new ArrayList<INetworkManagementEventObserver>();
if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
@@ -100,9 +106,18 @@ class NetworkManagementService extends INetworkManagementService.Stub {
}
mConnector = new NativeDaemonConnector(
- new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
- Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
- thread.start();
+ new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
+ mThread = new Thread(mConnector, NETD_TAG);
+ }
+
+ public static NetworkManagementService create(Context context) throws InterruptedException {
+ NetworkManagementService service = new NetworkManagementService(context);
+ if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
+ service.mThread.start();
+ if (DBG) Slog.d(TAG, "Awaiting socket connection");
+ service.mConnectedSignal.await();
+ if (DBG) Slog.d(TAG, "Connected");
+ return service;
}
public void registerObserver(INetworkManagementEventObserver obs) {
@@ -154,6 +169,14 @@ class NetworkManagementService extends INetworkManagementService.Stub {
}
}
+ /**
+ * Let us know the daemon is connected
+ */
+ protected void onConnected() {
+ if (DBG) Slog.d(TAG, "onConnected");
+ mConnectedSignal.countDown();
+ }
+
//
// Netd Callback handling
@@ -161,6 +184,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
public void onDaemonConnected() {
+ NetworkManagementService.this.onConnected();
new Thread() {
public void run() {
}
@@ -226,44 +250,61 @@ class NetworkManagementService extends INetworkManagementService.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+ try {
+ return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Cannot communicate with native daemon to list interfaces");
+ }
}
public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
- String rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+ String rsp;
+ try {
+ rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Cannot communicate with native daemon to get interface config");
+ }
Slog.d(TAG, String.format("rsp <%s>", rsp));
// Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
StringTokenizer st = new StringTokenizer(rsp);
+ InterfaceConfiguration cfg;
try {
- int code = Integer.parseInt(st.nextToken(" "));
- if (code != NetdResponseCode.InterfaceGetCfgResult) {
+ try {
+ int code = Integer.parseInt(st.nextToken(" "));
+ if (code != NetdResponseCode.InterfaceGetCfgResult) {
+ throw new IllegalStateException(
+ String.format("Expected code %d, but got %d",
+ NetdResponseCode.InterfaceGetCfgResult, code));
+ }
+ } catch (NumberFormatException nfe) {
throw new IllegalStateException(
- String.format("Expected code %d, but got %d",
- NetdResponseCode.InterfaceGetCfgResult, code));
+ String.format("Invalid response from daemon (%s)", rsp));
}
- } catch (NumberFormatException nfe) {
- throw new IllegalStateException(
- String.format("Invalid response from daemon (%s)", rsp));
- }
- InterfaceConfiguration cfg = new InterfaceConfiguration();
- cfg.hwAddr = st.nextToken(" ");
- try {
- cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
- } catch (UnknownHostException uhe) {
- Slog.e(TAG, "Failed to parse ipaddr", uhe);
- cfg.ipAddr = 0;
- }
+ cfg = new InterfaceConfiguration();
+ cfg.hwAddr = st.nextToken(" ");
+ try {
+ cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
+ } catch (UnknownHostException uhe) {
+ Slog.e(TAG, "Failed to parse ipaddr", uhe);
+ cfg.ipAddr = 0;
+ }
- try {
- cfg.netmask = stringToIpAddr(st.nextToken(" "));
- } catch (UnknownHostException uhe) {
- Slog.e(TAG, "Failed to parse netmask", uhe);
- cfg.netmask = 0;
+ try {
+ cfg.netmask = stringToIpAddr(st.nextToken(" "));
+ } catch (UnknownHostException uhe) {
+ Slog.e(TAG, "Failed to parse netmask", uhe);
+ cfg.netmask = 0;
+ }
+ cfg.interfaceFlags = st.nextToken("]").trim() +"]";
+ } catch (NoSuchElementException nsee) {
+ throw new IllegalStateException(
+ String.format("Invalid response from daemon (%s)", rsp));
}
- cfg.interfaceFlags = st.nextToken("]").trim() +"]";
Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
return cfg;
}
@@ -272,7 +313,12 @@ class NetworkManagementService extends INetworkManagementService.Stub {
String iface, InterfaceConfiguration cfg) throws IllegalStateException {
String cmd = String.format("interface setcfg %s %s %s %s", iface,
intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
- mConnector.doCommand(cmd);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate with native daemon to interface setcfg");
+ }
}
public void shutdown() {
@@ -289,20 +335,25 @@ class NetworkManagementService extends INetworkManagementService.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("ipfwd status");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate with native daemon to ipfwd status");
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ Slog.e(TAG, "Malformed response from native daemon: " + line);
+ return false;
+ }
+
int code = Integer.parseInt(tok[0]);
if (code == NetdResponseCode.IpFwdStatusResult) {
// 211 Forwarding <enabled/disabled>
- if (tok.length !=2) {
- throw new IllegalStateException(
- String.format("Malformatted list entry '%s'", line));
- }
- if (tok[2].equals("enabled"))
- return true;
- return false;
+ return "enabled".equals(tok[2]);
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
@@ -326,29 +377,45 @@ class NetworkManagementService extends INetworkManagementService.Stub {
for (String d : dhcpRange) {
cmd += " " + d;
}
- mConnector.doCommand(cmd);
+
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Unable to communicate to native daemon");
+ }
}
public void stopTethering() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether stop");
+ try {
+ mConnector.doCommand("tether stop");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
+ }
}
public boolean isTetheringStarted() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("tether status");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("tether status");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon to get tether status");
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ throw new IllegalStateException("Malformed response for tether status: " + line);
+ }
int code = Integer.parseInt(tok[0]);
if (code == NetdResponseCode.TetherStatusResult) {
// XXX: Tethering services <started/stopped> <TBD>...
- if (tok[2].equals("started"))
- return true;
- return false;
+ return "started".equals(tok[2]);
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
@@ -359,20 +426,35 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public void tetherInterface(String iface) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether interface add " + iface);
+ try {
+ mConnector.doCommand("tether interface add " + iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for adding tether interface");
+ }
}
public void untetherInterface(String iface) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether interface remove " + iface);
+ try {
+ mConnector.doCommand("tether interface remove " + iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for removing tether interface");
+ }
}
public String[] listTetheredInterfaces() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand(
- "tether interface list", NetdResponseCode.TetherInterfaceListResult);
+ try {
+ return mConnector.doListCommand(
+ "tether interface list", NetdResponseCode.TetherInterfaceListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing tether interfaces");
+ }
}
public void setDnsForwarders(String[] dns) throws IllegalStateException {
@@ -383,7 +465,12 @@ class NetworkManagementService extends INetworkManagementService.Stub {
for (String s : dns) {
cmd += " " + InetAddress.getByName(s).getHostAddress();
}
- mConnector.doCommand(cmd);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for setting tether dns");
+ }
} catch (UnknownHostException e) {
throw new IllegalStateException("Error resolving dns name", e);
}
@@ -392,30 +479,50 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public String[] getDnsForwarders() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand(
- "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
+ try {
+ return mConnector.doListCommand(
+ "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing tether dns");
+ }
}
public void enableNat(String internalInterface, String externalInterface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(
- String.format("nat enable %s %s", internalInterface, externalInterface));
+ try {
+ mConnector.doCommand(
+ String.format("nat enable %s %s", internalInterface, externalInterface));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for enabling NAT interface");
+ }
}
public void disableNat(String internalInterface, String externalInterface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(
- String.format("nat disable %s %s", internalInterface, externalInterface));
+ try {
+ mConnector.doCommand(
+ String.format("nat disable %s %s", internalInterface, externalInterface));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for disabling NAT interface");
+ }
}
public String[] listTtys() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
+ try {
+ return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing TTYs");
+ }
}
public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
@@ -430,31 +537,52 @@ class NetworkManagementService extends INetworkManagementService.Stub {
InetAddress.getByName(dns2Addr).getHostAddress()));
} catch (UnknownHostException e) {
throw new IllegalStateException("Error resolving addr", e);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
}
}
public void detachPppd(String tty) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("pppd detach %s", tty));
+ try {
+ mConnector.doCommand(String.format("pppd detach %s", tty));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
+ }
}
public void startUsbRNDIS() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("usb startrndis");
+ try {
+ mConnector.doCommand("usb startrndis");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating to native daemon for starting RNDIS", e);
+ }
}
public void stopUsbRNDIS() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("usb stoprndis");
+ try {
+ mConnector.doCommand("usb stoprndis");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
}
public boolean isUsbRNDISStarted() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("usb rndisstatus");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating to native daemon to check RNDIS status", e);
+ }
for (String line : rsp) {
String []tok = line.split(" ");
@@ -476,31 +604,35 @@ class NetworkManagementService extends INetworkManagementService.Stub {
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("softap stop " + wlanIface));
- mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
- mConnector.doCommand(String.format("softap start " + wlanIface));
- if (wifiConfig == null) {
- mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
- } else {
- /**
- * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
- * argv1 - wlan interface
- * argv2 - softap interface
- * argv3 - SSID
- * argv4 - Security
- * argv5 - Key
- * argv6 - Channel
- * argv7 - Preamble
- * argv8 - Max SCB
- */
- String str = String.format("softap set " + wlanIface + " " + softapIface +
- " %s %s %s", convertQuotedString(wifiConfig.SSID),
- wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
- "wpa2-psk" : "open",
- convertQuotedString(wifiConfig.preSharedKey));
- mConnector.doCommand(str);
- }
- mConnector.doCommand(String.format("softap startap"));
+ try {
+ mConnector.doCommand(String.format("softap stop " + wlanIface));
+ mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
+ mConnector.doCommand(String.format("softap start " + wlanIface));
+ if (wifiConfig == null) {
+ mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ } else {
+ /**
+ * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
+ * argv1 - wlan interface
+ * argv2 - softap interface
+ * argv3 - SSID
+ * argv4 - Security
+ * argv5 - Key
+ * argv6 - Channel
+ * argv7 - Preamble
+ * argv8 - Max SCB
+ */
+ String str = String.format("softap set " + wlanIface + " " + softapIface +
+ " %s %s %s", convertQuotedString(wifiConfig.SSID),
+ wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
+ "wpa2-psk" : "open",
+ convertQuotedString(wifiConfig.preSharedKey));
+ mConnector.doCommand(str);
+ }
+ mConnector.doCommand(String.format("softap startap"));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to start softap", e);
+ }
}
private String convertQuotedString(String s) {
@@ -516,7 +648,12 @@ class NetworkManagementService extends INetworkManagementService.Stub {
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- mConnector.doCommand("softap stopap");
+ try {
+ mConnector.doCommand("softap stopap");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
+ e);
+ }
}
public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
@@ -525,15 +662,19 @@ class NetworkManagementService extends INetworkManagementService.Stub {
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- if (wifiConfig == null) {
- mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
- } else {
- String str = String.format("softap set " + wlanIface + " " + softapIface +
- " %s %s %s", convertQuotedString(wifiConfig.SSID),
- wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
- "wpa2-psk" : "open",
- convertQuotedString(wifiConfig.preSharedKey));
- mConnector.doCommand(str);
+ try {
+ if (wifiConfig == null) {
+ mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ } else {
+ String str = String.format("softap set " + wlanIface + " " + softapIface
+ + " %s %s %s", convertQuotedString(wifiConfig.SSID),
+ wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open",
+ convertQuotedString(wifiConfig.preSharedKey));
+ mConnector.doCommand(str);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to set soft AP",
+ e);
}
}
@@ -541,9 +682,22 @@ class NetworkManagementService extends INetworkManagementService.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
try {
- String rsp = mConnector.doCommand(
- String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
- String []tok = rsp.split(" ");
+ String rsp;
+ try {
+ rsp = mConnector.doCommand(
+ String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
+ } catch (NativeDaemonConnectorException e1) {
+ Slog.e(TAG, "Error communicating with native daemon", e1);
+ return -1;
+ }
+
+ String[] tok = rsp.split(" ");
+ if (tok.length < 2) {
+ Slog.e(TAG, String.format("Malformed response for reading %s interface",
+ (rx ? "rx" : "tx")));
+ return -1;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
@@ -575,17 +729,34 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format(
- "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
+ try {
+ mConnector.doCommand(String.format(
+ "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
+ }
}
private int getInterfaceThrottle(String iface, boolean rx) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
try {
- String rsp = mConnector.doCommand(
- String.format("interface getthrottle %s %s", iface,(rx ? "rx" : "tx"))).get(0);
- String []tok = rsp.split(" ");
+ String rsp;
+ try {
+ rsp = mConnector.doCommand(
+ String.format("interface getthrottle %s %s", iface,
+ (rx ? "rx" : "tx"))).get(0);
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
+ return -1;
+ }
+
+ String[] tok = rsp.split(" ");
+ if (tok.length < 2) {
+ Slog.e(TAG, "Malformed response to getthrottle command");
+ return -1;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 348accd..4da5eb2 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -16,9 +16,8 @@
package com.android.server;
-import com.android.server.status.IconData;
-import com.android.server.status.NotificationData;
-import com.android.server.status.StatusBarService;
+import com.android.internal.statusbar.StatusBarNotification;
+import com.android.server.StatusBarManagerService;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
@@ -39,9 +38,11 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.hardware.Usb;
import android.media.AudioManager;
import android.net.Uri;
import android.os.BatteryManager;
+import android.os.Bundle;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -66,11 +67,14 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-class NotificationManagerService extends INotificationManager.Stub
+/** {@hide} */
+public class NotificationManagerService extends INotificationManager.Stub
{
private static final String TAG = "NotificationService";
private static final boolean DBG = false;
+ private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
+
// message codes
private static final int MESSAGE_TIMEOUT = 2;
@@ -86,7 +90,7 @@ class NotificationManagerService extends INotificationManager.Stub
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
- private StatusBarService mStatusBarService;
+ private StatusBarManagerService mStatusBar;
private LightsService mLightsService;
private LightsService.Light mBatteryLight;
private LightsService.Light mNotificationLight;
@@ -108,10 +112,12 @@ class NotificationManagerService extends INotificationManager.Stub
private boolean mScreenOn = true;
private boolean mInCall = false;
private boolean mNotificationPulseEnabled;
+ // This is true if we have received a new notification while the screen is off
+ // (that is, if mLedNotification was set while the screen was off)
+ // This is reset to false when the screen is turned on.
+ private boolean mPendingPulseNotification;
// for adb connected notifications
- private boolean mUsbConnected;
- private boolean mAdbEnabled = false;
private boolean mAdbNotificationShown = false;
private Notification mAdbNotification;
@@ -163,16 +169,21 @@ class NotificationManagerService extends INotificationManager.Stub
final String pkg;
final String tag;
final int id;
+ final int uid;
+ final int initialPid;
ITransientNotification callback;
int duration;
final Notification notification;
IBinder statusBarKey;
- NotificationRecord(String pkg, String tag, int id, Notification notification)
+ NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
+ Notification notification)
{
this.pkg = pkg;
this.tag = tag;
this.id = id;
+ this.uid = uid;
+ this.initialPid = initialPid;
this.notification = notification;
}
@@ -238,8 +249,8 @@ class NotificationManagerService extends INotificationManager.Stub
}
}
- private StatusBarService.NotificationCallbacks mNotificationCallbacks
- = new StatusBarService.NotificationCallbacks() {
+ private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
+ = new StatusBarManagerService.NotificationCallbacks() {
public void onSetDisabled(int status) {
synchronized (mNotificationList) {
@@ -302,6 +313,21 @@ class NotificationManagerService extends INotificationManager.Stub
updateLightsLocked();
}
}
+
+ public void onNotificationError(String pkg, String tag, int id,
+ int uid, int initialPid, String message) {
+ Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
+ + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
+ cancelNotification(pkg, tag, id, 0, 0);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
+ "Bad notification posted from package " + pkg
+ + ": " + message);
+ } catch (RemoteException e) {
+ }
+ Binder.restoreCallingIdentity(ident);
+ }
};
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -326,12 +352,14 @@ class NotificationManagerService extends INotificationManager.Stub
mBatteryFull = batteryFull;
updateLights();
}
- } else if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
- mUsbConnected = true;
- updateAdbNotification();
- } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
- mUsbConnected = false;
- updateAdbNotification();
+ } else if (action.equals(Usb.ACTION_USB_STATE)) {
+ Bundle extras = intent.getExtras();
+ boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED);
+ boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals(
+ extras.getString(Usb.USB_FUNCTION_ADB)));
+ updateAdbNotification(usbConnected && adbEnabled);
+ } else if (action.equals(Usb.ACTION_USB_DISCONNECTED)) {
+ updateAdbNotification(false);
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)
|| (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
@@ -377,8 +405,6 @@ class NotificationManagerService extends INotificationManager.Stub
void observe() {
ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ADB_ENABLED), false, this);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
update();
@@ -390,12 +416,6 @@ class NotificationManagerService extends INotificationManager.Stub
public void update() {
ContentResolver resolver = mContext.getContentResolver();
- boolean adbEnabled = Settings.Secure.getInt(resolver,
- Settings.Secure.ADB_ENABLED, 0) != 0;
- if (mAdbEnabled != adbEnabled) {
- mAdbEnabled = adbEnabled;
- updateAdbNotification();
- }
boolean pulseEnabled = Settings.System.getInt(resolver,
Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
if (mNotificationPulseEnabled != pulseEnabled) {
@@ -405,7 +425,7 @@ class NotificationManagerService extends INotificationManager.Stub
}
}
- NotificationManagerService(Context context, StatusBarService statusBar,
+ NotificationManagerService(Context context, StatusBarManagerService statusBar,
LightsService lights)
{
super();
@@ -417,7 +437,7 @@ class NotificationManagerService extends INotificationManager.Stub
mToastQueue = new ArrayList<ToastRecord>();
mHandler = new WorkerHandler();
- mStatusBarService = statusBar;
+ mStatusBar = statusBar;
statusBar.setNotificationCallbacks(mNotificationCallbacks);
mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
@@ -455,8 +475,7 @@ class NotificationManagerService extends INotificationManager.Stub
// register for battery changed notifications
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_UMS_CONNECTED);
- filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
+ filter.addAction(Usb.ACTION_USB_STATE);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
@@ -664,11 +683,40 @@ class NotificationManagerService extends INotificationManager.Stub
enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
}
- public void enqueueNotificationWithTag(String pkg, String tag, int id,
- Notification notification, int[] idOut)
+ public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
+ int[] idOut)
+ {
+ enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
+ tag, id, notification, idOut);
+ }
+
+ // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
+ // uid/pid of another application)
+ public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
+ String tag, int id, Notification notification, int[] idOut)
{
checkIncomingCall(pkg);
+ // Limit the number of notifications that any given package except the android
+ // package can enqueue. Prevents DOS attacks and deals with leaks.
+ if (!"android".equals(pkg)) {
+ synchronized (mNotificationList) {
+ int count = 0;
+ final int N = mNotificationList.size();
+ for (int i=0; i<N; i++) {
+ final NotificationRecord r = mNotificationList.get(i);
+ if (r.pkg.equals(pkg)) {
+ count++;
+ if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+ Slog.e(TAG, "Package has already posted " + count
+ + " notifications. Not showing more. package=" + pkg);
+ return;
+ }
+ }
+ }
+ }
+ }
+
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
if (!pkg.equals("com.android.providers.downloads")
@@ -692,7 +740,8 @@ class NotificationManagerService extends INotificationManager.Stub
}
synchronized (mNotificationList) {
- NotificationRecord r = new NotificationRecord(pkg, tag, id, notification);
+ NotificationRecord r = new NotificationRecord(pkg, tag, id,
+ callingUid, callingPid, notification);
NotificationRecord old = null;
int index = indexOfNotificationLocked(pkg, tag, id);
@@ -716,36 +765,13 @@ class NotificationManagerService extends INotificationManager.Stub
}
if (notification.icon != 0) {
- IconData icon = IconData.makeIcon(null, pkg, notification.icon,
- notification.iconLevel,
- notification.number);
- CharSequence truncatedTicker = notification.tickerText;
-
- // TODO: make this restriction do something smarter like never fill
- // more than two screens. "Why would anyone need more than 80 characters." :-/
- final int maxTickerLen = 80;
- if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) {
- truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen);
- }
-
- NotificationData n = new NotificationData();
- n.pkg = pkg;
- n.tag = tag;
- n.id = id;
- n.when = notification.when;
- n.tickerText = truncatedTicker;
- n.ongoingEvent = (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
- if (!n.ongoingEvent && (notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
- n.clearable = true;
- }
- n.contentView = notification.contentView;
- n.contentIntent = notification.contentIntent;
- n.deleteIntent = notification.deleteIntent;
+ StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
+ r.uid, r.initialPid, notification);
if (old != null && old.statusBarKey != null) {
r.statusBarKey = old.statusBarKey;
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.updateIcon(r.statusBarKey, icon, n);
+ mStatusBar.updateNotification(r.statusBarKey, n);
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -753,21 +779,19 @@ class NotificationManagerService extends INotificationManager.Stub
} else {
long identity = Binder.clearCallingIdentity();
try {
- r.statusBarKey = mStatusBarService.addIcon(icon, n);
+ r.statusBarKey = mStatusBar.addNotification(n);
mAttentionLight.pulse();
}
finally {
Binder.restoreCallingIdentity(identity);
}
}
-
sendAccessibilityEvent(notification, pkg);
-
} else {
if (old != null && old.statusBarKey != null) {
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.removeIcon(old.statusBarKey);
+ mStatusBar.removeNotification(old.statusBarKey);
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -875,7 +899,7 @@ class NotificationManagerService extends INotificationManager.Stub
if (r.notification.icon != 0) {
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.removeIcon(r.statusBarKey);
+ mStatusBar.removeNotification(r.statusBarKey);
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -1070,6 +1094,11 @@ class NotificationManagerService extends INotificationManager.Stub
mBatteryLight.turnOff();
}
+ // clear pending pulse notification if screen is on
+ if (mScreenOn || mLedNotification == null) {
+ mPendingPulseNotification = false;
+ }
+
// handle notification lights
if (mLedNotification == null) {
// get next notification, if any
@@ -1077,11 +1106,14 @@ class NotificationManagerService extends INotificationManager.Stub
if (n > 0) {
mLedNotification = mLights.get(n-1);
}
+ if (mLedNotification != null && !mScreenOn) {
+ mPendingPulseNotification = true;
+ }
}
// we only flash if screen is off and persistent pulsing is enabled
// and we are not currently in a call
- if (mLedNotification == null || mScreenOn || mInCall) {
+ if (!mPendingPulseNotification || mScreenOn || mInCall) {
mNotificationLight.turnOff();
} else {
int ledARGB = mLedNotification.notification.ledARGB;
@@ -1129,8 +1161,8 @@ class NotificationManagerService extends INotificationManager.Stub
// This is here instead of StatusBarPolicy because it is an important
// security feature that we don't want people customizing the platform
// to accidentally lose.
- private void updateAdbNotification() {
- if (mAdbEnabled && mUsbConnected) {
+ private void updateAdbNotification(boolean adbEnabled) {
+ if (adbEnabled) {
if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
return;
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index d23c16a..d324c2b 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -18,6 +18,7 @@ package com.android.server;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
+import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
@@ -90,6 +91,7 @@ import android.util.*;
import android.view.Display;
import android.view.WindowManager;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -112,6 +114,7 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -137,11 +140,11 @@ class PackageManagerService extends IPackageManager.Stub {
private static final boolean DEBUG_PREFERRED = false;
private static final boolean DEBUG_UPGRADE = false;
private static final boolean DEBUG_INSTALL = false;
- private static final boolean DEBUG_NATIVE = false;
private static final boolean MULTIPLE_APPLICATION_UIDS = true;
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
+ private static final int NFC_UID = Process.NFC_UID;
private static final int FIRST_APPLICATION_UID =
Process.FIRST_APPLICATION_UID;
private static final int MAX_APPLICATION_UIDS = 1000;
@@ -150,6 +153,8 @@ class PackageManagerService extends IPackageManager.Stub {
private static final boolean GET_CERTIFICATES = true;
+ private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
+
private static final int REMOVE_EVENTS =
FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM;
private static final int ADD_EVENTS =
@@ -179,13 +184,20 @@ class PackageManagerService extends IPackageManager.Stub {
static final int SCAN_UPDATE_SIGNATURE = 1<<3;
static final int SCAN_NEW_INSTALL = 1<<4;
static final int SCAN_NO_PATHS = 1<<5;
+ static final int SCAN_UPDATE_TIME = 1<<6;
static final int REMOVE_CHATTY = 1<<16;
-
+
+ static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
+
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
- "com.android.defcontainer",
+ DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
-
+
+ private static final String LIB_DIR_NAME = "lib";
+
+ static final String mTempContainerPrefix = "smdl2tmp";
+
final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
Process.THREAD_PRIORITY_BACKGROUND);
final PackageHandler mHandler;
@@ -204,12 +216,19 @@ class PackageManagerService extends IPackageManager.Stub {
// This is where all application persistent data goes.
final File mAppDataDir;
+ // If Encrypted File System feature is enabled, all application persistent data
+ // should go here instead.
+ final File mSecureAppDataDir;
+
// This is the object monitoring the framework dir.
final FileObserver mFrameworkInstallObserver;
// This is the object monitoring the system app dir.
final FileObserver mSystemInstallObserver;
+ // This is the object monitoring the system app dir.
+ final FileObserver mVendorInstallObserver;
+
// This is the object monitoring mAppInstallDir.
final FileObserver mAppInstallObserver;
@@ -222,6 +241,7 @@ class PackageManagerService extends IPackageManager.Stub {
final File mFrameworkDir;
final File mSystemAppDir;
+ final File mVendorAppDir;
final File mAppInstallDir;
final File mDalvikCacheDir;
@@ -670,13 +690,6 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
- static boolean isFwdLocked(int flags) {
- if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
- return true;
- }
- return false;
- }
-
public static final IPackageManager main(Context context, boolean factoryTest) {
PackageManagerService m = new PackageManagerService(context, factoryTest);
ServiceManager.addService("package", m);
@@ -728,6 +741,10 @@ class PackageManagerService extends IPackageManager.Stub {
MULTIPLE_APPLICATION_UIDS
? LOG_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
+ mSettings.addSharedUserLP("android.uid.nfc",
+ MULTIPLE_APPLICATION_UIDS
+ ? NFC_UID : FIRST_APPLICATION_UID,
+ ApplicationInfo.FLAG_SYSTEM);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -768,6 +785,7 @@ class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mSecureAppDataDir = new File(dataDir, "secure/data");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
if (mInstaller == null) {
@@ -777,6 +795,7 @@ class PackageManagerService extends IPackageManager.Stub {
File miscDir = new File(dataDir, "misc");
miscDir.mkdirs();
mAppDataDir.mkdirs();
+ mSecureAppDataDir.mkdirs();
mDrmAppPrivateInstallDir.mkdirs();
}
@@ -910,7 +929,7 @@ class PackageManagerService extends IPackageManager.Stub {
mFrameworkInstallObserver.startWatching();
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR,
- scanMode | SCAN_NO_DEX);
+ scanMode | SCAN_NO_DEX, 0);
// Collect all system packages.
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
@@ -918,8 +937,16 @@ class PackageManagerService extends IPackageManager.Stub {
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
+ // Collect all vendor packages.
+ mVendorAppDir = new File("/vendor/app");
+ mVendorInstallObserver = new AppDirObserver(
+ mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
+ mVendorInstallObserver.startWatching();
+ scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
+
if (mInstaller != null) {
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
@@ -937,7 +964,9 @@ class PackageManagerService extends IPackageManager.Stub {
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
if (mInstaller != null) {
- mInstaller.remove(ps.name);
+ // XXX how to set useEncryptedFSDir for packages that
+ // are not encrypted?
+ mInstaller.remove(ps.name, true);
}
}
}
@@ -963,12 +992,13 @@ class PackageManagerService extends IPackageManager.Stub {
mAppInstallObserver = new AppDirObserver(
mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
mAppInstallObserver.startWatching();
- scanDirLI(mAppInstallDir, 0, scanMode);
+ scanDirLI(mAppInstallDir, 0, scanMode, 0);
mDrmAppInstallObserver = new AppDirObserver(
mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
mDrmAppInstallObserver.startWatching();
- scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode);
+ scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
+ scanMode, 0);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
@@ -1020,7 +1050,8 @@ class PackageManagerService extends IPackageManager.Stub {
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
if (mInstaller != null) {
- int retCode = mInstaller.remove(ps.name);
+ boolean useSecureFS = useEncryptedFilesystemForPackage(ps.pkg);
+ int retCode = mInstaller.remove(ps.name, useSecureFS);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
@@ -1079,27 +1110,6 @@ class PackageManagerService extends IPackageManager.Stub {
final File permFile = new File(Environment.getRootDirectory(),
"etc/permissions/platform.xml");
readPermissionsFromXml(permFile);
-
- StringBuilder sb = new StringBuilder(128);
- sb.append("Libs:");
- Iterator<String> it = mSharedLibraries.keySet().iterator();
- while (it.hasNext()) {
- sb.append(' ');
- String name = it.next();
- sb.append(name);
- sb.append(':');
- sb.append(mSharedLibraries.get(name));
- }
- Log.i(TAG, sb.toString());
-
- sb.setLength(0);
- sb.append("Features:");
- it = mAvailableFeatures.keySet().iterator();
- while (it.hasNext()) {
- sb.append(' ');
- sb.append(it.next());
- }
- Log.i(TAG, sb.toString());
}
private void readPermissionsFromXml(File permFile) {
@@ -1316,14 +1326,15 @@ class PackageManagerService extends IPackageManager.Stub {
PackageInfo generatePackageInfo(PackageParser.Package p, int flags) {
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
// The package has been uninstalled but has retained data and resources.
- return PackageParser.generatePackageInfo(p, null, flags);
+ return PackageParser.generatePackageInfo(p, null, flags, 0, 0);
}
final PackageSetting ps = (PackageSetting)p.mExtras;
if (ps == null) {
return null;
}
final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- return PackageParser.generatePackageInfo(p, gp.gids, flags);
+ return PackageParser.generatePackageInfo(p, gp.gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime);
}
public PackageInfo getPackageInfo(String packageName, int flags) {
@@ -1484,6 +1495,8 @@ class PackageManagerService extends IPackageManager.Stub {
ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
ps.pkg.applicationInfo.sourceDir = ps.codePathString;
ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath();
+ ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
+ ps.pkg.mSetEnabled = ps.enabled;
}
return generatePackageInfo(ps.pkg, flags);
}
@@ -1603,6 +1616,18 @@ class PackageManagerService extends IPackageManager.Stub {
return null;
}
+ public ProviderInfo getProviderInfo(ComponentName component, int flags) {
+ synchronized (mPackages) {
+ PackageParser.Provider p = mProvidersByComponent.get(component);
+ if (Config.LOGV) Log.v(
+ TAG, "getProviderInfo " + component + ": " + p);
+ if (p != null && mSettings.isEnabledLP(p.info, flags)) {
+ return PackageParser.generateProviderInfo(p, flags);
+ }
+ }
+ return null;
+ }
+
public String[] getSystemSharedLibraryNames() {
Set<String> libSet;
synchronized (mPackages) {
@@ -1718,6 +1743,7 @@ class PackageManagerService extends IPackageManager.Stub {
static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
if (pi1.icon != pi2.icon) return false;
+ if (pi1.logo != pi2.logo) return false;
if (pi1.protectionLevel != pi2.protectionLevel) return false;
if (!compareStrings(pi1.name, pi2.name)) return false;
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
@@ -1997,7 +2023,11 @@ class PackageManagerService extends IPackageManager.Stub {
ActivityInfo ai = getActivityInfo(pa.mActivity, flags);
if (DEBUG_PREFERRED) {
Log.v(TAG, "Got preferred activity:");
- ai.dump(new LogPrinter(Log.INFO, TAG), " ");
+ if (ai != null) {
+ ai.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
+ } else {
+ Log.v(TAG, " null");
+ }
}
if (ai != null) {
for (int j=0; j<N; j++) {
@@ -2361,8 +2391,7 @@ class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
- && (!mSafeMode || (p.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) != 0)) {
+ && (!mSafeMode || isSystemApp(p))) {
finalList.add(PackageParser.generateApplicationInfo(p, flags));
}
}
@@ -2464,10 +2493,16 @@ class PackageManagerService extends IPackageManager.Stub {
return finalList;
}
- private void scanDirLI(File dir, int flags, int scanMode) {
- Log.d(TAG, "Scanning app dir " + dir);
-
+ private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
String[] files = dir.list();
+ if (files == null) {
+ Log.d(TAG, "No files in app dir " + dir);
+ return;
+ }
+
+ if (false) {
+ Log.d(TAG, "Scanning app dir " + dir);
+ }
int i;
for (i=0; i<files.length; i++) {
@@ -2477,7 +2512,7 @@ class PackageManagerService extends IPackageManager.Stub {
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
- flags|PackageParser.PARSE_MUST_BE_APK, scanMode);
+ flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
// Don't mess around with apps in system partition.
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
@@ -2518,7 +2553,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (GET_CERTIFICATES) {
if (ps != null
&& ps.codePath.equals(srcFile)
- && ps.getTimeStamp() == srcFile.lastModified()) {
+ && ps.timeStamp == srcFile.lastModified()) {
if (ps.signatures.mSignatures != null
&& ps.signatures.mSignatures.length != 0) {
// Optimization: reuse the existing cached certificates
@@ -2545,7 +2580,7 @@ class PackageManagerService extends IPackageManager.Stub {
* Returns null in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile,
- int parseFlags, int scanMode) {
+ int parseFlags, int scanMode, long currentTime) {
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
String scanPath = scanFile.getPath();
parseFlags |= mDefParseFlags;
@@ -2579,7 +2614,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
// First check if this is a system package that may involve an update
if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
- if (!ps.codePath.equals(scanFile)) {
+ if (ps != null && !ps.codePath.equals(scanFile)) {
// The path has changed from what was last scanned... check the
// version of the new path against what we have stored to determine
// what to do.
@@ -2587,7 +2622,7 @@ class PackageManagerService extends IPackageManager.Stub {
// The system package has been updated and the code path does not match
// Ignore entry. Skip it.
Log.i(TAG, "Package " + ps.name + " at " + scanFile
- + "ignored: updated version " + ps.versionCode
+ + " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
return null;
@@ -2606,9 +2641,9 @@ class PackageManagerService extends IPackageManager.Stub {
+ "reverting from " + ps.codePathString
+ ": new version " + pkg.mVersionCode
+ " better than installed " + ps.versionCode);
- InstallArgs args = new FileInstallArgs(ps.codePathString, ps.resourcePathString);
+ InstallArgs args = new FileInstallArgs(ps.codePathString,
+ ps.resourcePathString, ps.nativeLibraryPathString);
args.cleanUpResourcesLI();
- removeNativeBinariesLI(pkg);
mSettings.enableSystemPackageLP(ps.name);
}
}
@@ -2645,11 +2680,11 @@ class PackageManagerService extends IPackageManager.Stub {
// Set application objects path explicitly.
setApplicationInfoPaths(pkg, codePath, resPath);
// Note that we invoke the following method only if we are about to unpack an application
- return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
+ return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime);
}
- private static void setApplicationInfoPaths(PackageParser.Package pkg,
- String destCodePath, String destResPath) {
+ private static void setApplicationInfoPaths(PackageParser.Package pkg, String destCodePath,
+ String destResPath) {
pkg.mPath = pkg.mScanPath = destCodePath;
pkg.applicationInfo.sourceDir = destCodePath;
pkg.applicationInfo.publicSourceDir = destResPath;
@@ -2743,6 +2778,11 @@ class PackageManagerService extends IPackageManager.Stub {
return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
}
+
+ private static boolean useEncryptedFilesystemForPackage(PackageParser.Package pkg) {
+ return Environment.isEncryptedFilesystemEnabled() &&
+ ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_NEVER_ENCRYPT) == 0);
+ }
private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) {
if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -2760,11 +2800,18 @@ class PackageManagerService extends IPackageManager.Stub {
}
private File getDataPathForPackage(PackageParser.Package pkg) {
- return new File(mAppDataDir, pkg.packageName);
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
+ File dataPath;
+ if (useEncryptedFSDir) {
+ dataPath = new File(mSecureAppDataDir, pkg.packageName);
+ } else {
+ dataPath = new File(mAppDataDir, pkg.packageName);
+ }
+ return dataPath;
}
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
- int parseFlags, int scanMode) {
+ int parseFlags, int scanMode, long currentTime) {
File scanFile = new File(pkg.mScanPath);
if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
@@ -2821,10 +2868,8 @@ class PackageManagerService extends IPackageManager.Stub {
TAG, "Scanning package " + pkg.packageName);
if (mPackages.containsKey(pkg.packageName)
|| mSharedLibraries.containsKey(pkg.packageName)) {
- Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Application package " + pkg.packageName
+ " already installed. Skipping duplicate.");
- Slog.w(TAG, "*************************************************");
mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
return null;
}
@@ -2986,7 +3031,8 @@ class PackageManagerService extends IPackageManager.Stub {
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile,
- destResourceFile, pkg.applicationInfo.flags, true, false);
+ destResourceFile, pkg.applicationInfo.nativeLibraryDir,
+ pkg.applicationInfo.flags, true, false);
if (pkgSetting == null) {
Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -3096,9 +3142,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- long scanFileTime = scanFile.lastModified();
+ final long scanFileTime = scanFile.lastModified();
final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0;
- final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.getTimeStamp();
+ final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.timeStamp;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName,
@@ -3111,6 +3157,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
// This is a normal package, need to make its data directory.
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
dataPath = getDataPathForPackage(pkg);
boolean uidError = false;
@@ -3118,16 +3165,16 @@ class PackageManagerService extends IPackageManager.Stub {
if (dataPath.exists()) {
mOutPermissions[1] = 0;
FileUtils.getPermissions(dataPath.getPath(), mOutPermissions);
- if (mOutPermissions[1] == pkg.applicationInfo.uid
- || !Process.supportsProcesses()) {
- pkg.applicationInfo.dataDir = dataPath.getPath();
- } else {
+
+ // If we have mismatched owners for the data path, we have a
+ // problem (unless we're running in the simulator.)
+ if (mOutPermissions[1] != pkg.applicationInfo.uid && Process.supportsProcesses()) {
boolean recovered = false;
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
// If this is a system app, we can at least delete its
// current data so the application will still work.
if (mInstaller != null) {
- int ret = mInstaller.remove(pkgName);
+ int ret = mInstaller.remove(pkgName, useEncryptedFSDir);
if (ret >= 0) {
// Old data gone!
String msg = "System package " + pkg.packageName
@@ -3138,7 +3185,7 @@ class PackageManagerService extends IPackageManager.Stub {
recovered = true;
// And now re-install the app.
- ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+ ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
if (ret == -1) {
// Ack should not happen!
@@ -3158,6 +3205,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
+ pkg.applicationInfo.uid + "/fs_"
+ mOutPermissions[1];
+ pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
String msg = "Package " + pkg.packageName
+ " has mismatched uid: "
+ mOutPermissions[1] + " on disk, "
@@ -3178,7 +3226,7 @@ class PackageManagerService extends IPackageManager.Stub {
Log.v(TAG, "Want this data dir: " + dataPath);
//invoke installer to do the actual installation
if (mInstaller != null) {
- int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+ int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
if(ret < 0) {
// Error from installer
@@ -3201,31 +3249,73 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = null;
}
}
-
+
+ /*
+ * Set the data dir to the default "/data/data/<package name>/lib"
+ * if we got here without anyone telling us different (e.g., apps
+ * stored on SD card have their native libraries stored in the ASEC
+ * container with the APK).
+ *
+ * This happens during an upgrade from a package settings file that
+ * doesn't have a native library path attribute at all.
+ */
+ if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) {
+ if (pkgSetting.nativeLibraryPathString == null) {
+ final String nativeLibraryPath = new File(dataPath, LIB_DIR_NAME).getPath();
+ pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
+ pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+ } else {
+ pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
+ }
+ }
+
pkgSetting.uidError = uidError;
}
- // Perform shared library installation and dex validation and
- // optimization, if this is not a system app.
+ // If we're running in the simulator, we don't need to unpack anything.
if (mInstaller != null) {
String path = scanFile.getPath();
- if (scanFileNewer) {
- // Note: We don't want to unpack the native binaries for
- // system applications, unless they have been updated
- // (the binaries are already under /system/lib).
- //
- // In other words, we're going to unpack the binaries
- // only for non-system apps and system app upgrades.
- //
- int flags = pkg.applicationInfo.flags;
- if ((flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
- (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
- Log.i(TAG, path + " changed; unpacking");
- int err = cachePackageSharedLibsLI(pkg, scanFile);
- if (err != PackageManager.INSTALL_SUCCEEDED) {
- mLastScanError = err;
- return null;
- }
+ /* Note: We don't want to unpack the native binaries for
+ * system applications, unless they have been updated
+ * (the binaries are already under /system/lib).
+ * Also, don't unpack libs for apps on the external card
+ * since they should have their libraries in the ASEC
+ * container already.
+ *
+ * In other words, we're going to unpack the binaries
+ * only for non-system apps and system app upgrades.
+ */
+ if (pkg.applicationInfo.nativeLibraryDir != null) {
+ final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
+ final String dataPathString = dataPath.getPath();
+
+ if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
+ /*
+ * Upgrading from a previous version of the OS sometimes
+ * leaves native libraries in the /data/data/<app>/lib
+ * directory for system apps even when they shouldn't be.
+ * Recent changes in the JNI library search path
+ * necessitates we remove those to match previous behavior.
+ */
+ if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
+ Log.i(TAG, "removed obsolete native libraries for system package " + path);
+ }
+ } else if (nativeLibraryDir.getParent().equals(dataPathString)) {
+ /*
+ * If this is an internal application or our
+ * nativeLibraryPath points to our data directory, unpack
+ * the libraries. The native library path pointing to the
+ * data directory for an application in an ASEC container
+ * can happen for older apps that existed before an OTA to
+ * Gingerbread.
+ */
+ Slog.i(TAG, "Unpacking native libraries for " + path);
+ mInstaller.unlinkNativeLibraryDirectory(dataPathString);
+ NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
+ } else {
+ Slog.i(TAG, "Linking native library dir for " + path);
+ mInstaller.linkNativeLibraryDirectory(dataPathString,
+ pkg.applicationInfo.nativeLibraryDir);
}
}
pkg.mScanPath = path;
@@ -3263,6 +3353,24 @@ class PackageManagerService extends IPackageManager.Stub {
// Make sure we don't accidentally delete its data.
mSettings.mPackagesToBeCleaned.remove(pkgName);
+ // Take care of first install / last update times.
+ if (currentTime != 0) {
+ if (pkgSetting.firstInstallTime == 0) {
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
+ } else if ((scanMode&SCAN_UPDATE_TIME) != 0) {
+ pkgSetting.lastUpdateTime = currentTime;
+ }
+ } else if (pkgSetting.firstInstallTime == 0) {
+ // We need *something*. Take time time stamp of the file.
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
+ } else if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+ if (scanFileTime != pkgSetting.timeStamp) {
+ // A package on the system image has changed; consider this
+ // to be an update.
+ pkgSetting.lastUpdateTime = scanFileTime;
+ }
+ }
+
int N = pkg.providers.size();
StringBuilder r = null;
int i;
@@ -3489,6 +3597,7 @@ class PackageManagerService extends IPackageManager.Stub {
a.info.sourceDir = pkg.applicationInfo.sourceDir;
a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
a.info.dataDir = pkg.applicationInfo.dataDir;
+ a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
mInstrumentation.put(a.getComponentName(), a);
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
@@ -3529,281 +3638,16 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- // The following constants are returned by cachePackageSharedLibsForAbiLI
- // to indicate if native shared libraries were found in the package.
- // Values are:
- // PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES => native libraries found and installed
- // PACKAGE_INSTALL_NATIVE_NO_LIBRARIES => no native libraries in package
- // PACKAGE_INSTALL_NATIVE_ABI_MISMATCH => native libraries for another ABI found
- // in package (and not installed)
- //
- private static final int PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES = 0;
- private static final int PACKAGE_INSTALL_NATIVE_NO_LIBRARIES = 1;
- private static final int PACKAGE_INSTALL_NATIVE_ABI_MISMATCH = 2;
-
// Return the path of the directory that will contain the native binaries
// of a given installed package. This is relative to the data path.
//
- private static File getNativeBinaryDirForPackage(PackageParser.Package pkg) {
- return new File(pkg.applicationInfo.dataDir + "/lib");
- }
-
- // Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk
- // and automatically copy them to /data/data/<appname>/lib if present.
- //
- // NOTE: this method may throw an IOException if the library cannot
- // be copied to its final destination, e.g. if there isn't enough
- // room left on the data partition, or a ZipException if the package
- // file is malformed.
- //
- private int cachePackageSharedLibsForAbiLI(PackageParser.Package pkg,
- File scanFile, String cpuAbi) throws IOException, ZipException {
- File sharedLibraryDir = getNativeBinaryDirForPackage(pkg);
- final String apkLib = "lib/";
- final int apkLibLen = apkLib.length();
- final int cpuAbiLen = cpuAbi.length();
- final String libPrefix = "lib";
- final int libPrefixLen = libPrefix.length();
- final String libSuffix = ".so";
- final int libSuffixLen = libSuffix.length();
- boolean hasNativeLibraries = false;
- boolean installedNativeLibraries = false;
-
- // the minimum length of a valid native shared library of the form
- // lib/<something>/lib<name>.so.
- final int minEntryLen = apkLibLen + 2 + libPrefixLen + 1 + libSuffixLen;
-
- ZipFile zipFile = new ZipFile(scanFile);
- Enumeration<ZipEntry> entries =
- (Enumeration<ZipEntry>) zipFile.entries();
-
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- // skip directories
- if (entry.isDirectory()) {
- continue;
- }
- String entryName = entry.getName();
-
- // check that the entry looks like lib/<something>/lib<name>.so
- // here, but don't check the ABI just yet.
- //
- // - must be sufficiently long
- // - must end with libSuffix, i.e. ".so"
- // - must start with apkLib, i.e. "lib/"
- if (entryName.length() < minEntryLen ||
- !entryName.endsWith(libSuffix) ||
- !entryName.startsWith(apkLib) ) {
- continue;
- }
-
- // file name must start with libPrefix, i.e. "lib"
- int lastSlash = entryName.lastIndexOf('/');
-
- if (lastSlash < 0 ||
- !entryName.regionMatches(lastSlash+1, libPrefix, 0, libPrefixLen) ) {
- continue;
- }
-
- hasNativeLibraries = true;
-
- // check the cpuAbi now, between lib/ and /lib<name>.so
- //
- if (lastSlash != apkLibLen + cpuAbiLen ||
- !entryName.regionMatches(apkLibLen, cpuAbi, 0, cpuAbiLen) )
- continue;
-
- // extract the library file name, ensure it doesn't contain
- // weird characters. we're guaranteed here that it doesn't contain
- // a directory separator though.
- String libFileName = entryName.substring(lastSlash+1);
- if (!FileUtils.isFilenameSafe(new File(libFileName))) {
- continue;
- }
-
- installedNativeLibraries = true;
-
- String sharedLibraryFilePath = sharedLibraryDir.getPath() +
- File.separator + libFileName;
- File sharedLibraryFile = new File(sharedLibraryFilePath);
- if (! sharedLibraryFile.exists() ||
- sharedLibraryFile.length() != entry.getSize() ||
- sharedLibraryFile.lastModified() != entry.getTime()) {
- if (Config.LOGD) {
- Log.d(TAG, "Caching shared lib " + entry.getName());
- }
- if (mInstaller == null) {
- sharedLibraryDir.mkdir();
- }
- cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir,
- sharedLibraryFile);
- }
- }
- if (!hasNativeLibraries)
- return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
-
- if (!installedNativeLibraries)
- return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH;
-
- return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
- }
-
- // Find the gdbserver executable program in a package at
- // lib/<cpuAbi>/gdbserver and copy it to /data/data/<name>/lib/gdbserver
- //
- // Returns PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES on success,
- // or PACKAGE_INSTALL_NATIVE_NO_LIBRARIES otherwise.
- //
- private int cachePackageGdbServerLI(PackageParser.Package pkg,
- File scanFile, String cpuAbi) throws IOException, ZipException {
- File installGdbServerDir = getNativeBinaryDirForPackage(pkg);
- final String GDBSERVER = "gdbserver";
- final String apkGdbServerPath = "lib/" + cpuAbi + "/" + GDBSERVER;
-
- ZipFile zipFile = new ZipFile(scanFile);
- Enumeration<ZipEntry> entries =
- (Enumeration<ZipEntry>) zipFile.entries();
-
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- // skip directories
- if (entry.isDirectory()) {
- continue;
- }
- String entryName = entry.getName();
-
- if (!entryName.equals(apkGdbServerPath)) {
- continue;
- }
-
- String installGdbServerPath = installGdbServerDir.getPath() +
- "/" + GDBSERVER;
- File installGdbServerFile = new File(installGdbServerPath);
- if (! installGdbServerFile.exists() ||
- installGdbServerFile.length() != entry.getSize() ||
- installGdbServerFile.lastModified() != entry.getTime()) {
- if (Config.LOGD) {
- Log.d(TAG, "Caching gdbserver " + entry.getName());
- }
- if (mInstaller == null) {
- installGdbServerDir.mkdir();
- }
- cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir,
- installGdbServerFile);
- }
- return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
- }
- return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
- }
-
- // extract shared libraries stored in the APK as lib/<cpuAbi>/lib<name>.so
- // and copy them to /data/data/<appname>/lib.
- //
- // This function will first try the main CPU ABI defined by Build.CPU_ABI
- // (which corresponds to ro.product.cpu.abi), and also try an alternate
- // one if ro.product.cpu.abi2 is defined.
- //
- private int cachePackageSharedLibsLI(PackageParser.Package pkg, File scanFile) {
- String cpuAbi = Build.CPU_ABI;
- try {
- int result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi);
-
- // some architectures are capable of supporting several CPU ABIs
- // for example, 'armeabi-v7a' also supports 'armeabi' native code
- // this is indicated by the definition of the ro.product.cpu.abi2
- // system property.
- //
- // only scan the package twice in case of ABI mismatch
- if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
- final String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2",null);
- if (cpuAbi2 != null) {
- result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi2);
- }
-
- if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
- Slog.w(TAG,"Native ABI mismatch from package file");
- return PackageManager.INSTALL_FAILED_INVALID_APK;
- }
-
- if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) {
- cpuAbi = cpuAbi2;
- }
- }
-
- // for debuggable packages, also extract gdbserver from lib/<abi>
- // into /data/data/<appname>/lib too.
- if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES &&
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- int result2 = cachePackageGdbServerLI(pkg, scanFile, cpuAbi);
- if (result2 == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_NATIVE_DEBUGGABLE;
- }
- }
- } catch (ZipException e) {
- Slog.w(TAG, "Failed to extract data from package file", e);
- return PackageManager.INSTALL_FAILED_INVALID_APK;
- } catch (IOException e) {
- Slog.w(TAG, "Failed to cache package shared libs", e);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- private void cacheNativeBinaryLI(PackageParser.Package pkg,
- ZipFile zipFile, ZipEntry entry,
- File binaryDir,
- File binaryFile) throws IOException {
- InputStream inputStream = zipFile.getInputStream(entry);
- try {
- File tempFile = File.createTempFile("tmp", "tmp", binaryDir);
- String tempFilePath = tempFile.getPath();
- // XXX package manager can't change owner, so the executable files for
- // now need to be left as world readable and owned by the system.
- if (! FileUtils.copyToFile(inputStream, tempFile) ||
- ! tempFile.setLastModified(entry.getTime()) ||
- FileUtils.setPermissions(tempFilePath,
- FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
- |FileUtils.S_IXUSR|FileUtils.S_IXGRP|FileUtils.S_IXOTH
- |FileUtils.S_IROTH, -1, -1) != 0 ||
- ! tempFile.renameTo(binaryFile)) {
- // Failed to properly write file.
- tempFile.delete();
- throw new IOException("Couldn't create cached binary "
- + binaryFile + " in " + binaryDir);
- }
- } finally {
- inputStream.close();
- }
- }
-
- // Remove the native binaries of a given package. This simply
- // gets rid of the files in the 'lib' sub-directory.
- private void removeNativeBinariesLI(PackageParser.Package pkg) {
- File binaryDir = getNativeBinaryDirForPackage(pkg);
-
- if (DEBUG_NATIVE) {
- Slog.w(TAG,"Deleting native binaries from: " + binaryDir.getPath());
- }
-
- // Just remove any file in the directory. Since the directory
- // is owned by the 'system' UID, the application is not supposed
- // to have written anything there.
- //
- if (binaryDir.exists()) {
- File[] binaries = binaryDir.listFiles();
- if (binaries != null) {
- for (int nn=0; nn < binaries.length; nn++) {
- if (DEBUG_NATIVE) {
- Slog.d(TAG," Deleting " + binaries[nn].getName());
- }
- if (!binaries[nn].delete()) {
- Slog.w(TAG,"Could not delete native binary: " +
- binaries[nn].getPath());
- }
- }
- }
- // Do not delete 'lib' directory itself, or this will prevent
- // installation of future updates.
+ private File getNativeBinaryDirForPackage(PackageParser.Package pkg) {
+ final String nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
+ if (nativeLibraryDir != null) {
+ return new File(nativeLibraryDir);
+ } else {
+ // Fall back for old packages
+ return new File(pkg.applicationInfo.dataDir, LIB_DIR_NAME);
}
}
@@ -4105,18 +3949,22 @@ class PackageManagerService extends IPackageManager.Stub {
allowed = false;
} else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
|| bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- allowed = (checkSignaturesLP(bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
+ allowed = (checkSignaturesLP(
+ bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
|| (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
- if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (!allowed && bp.protectionLevel
+ == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+ if (isSystemApp(pkg)) {
// For updated system applications, the signatureOrSystem permission
// is granted only if it had been defined by the original application.
- if ((pkg.applicationInfo.flags
- & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
- PackageSetting sysPs = mSettings.getDisabledSystemPkg(pkg.packageName);
- if(sysPs.grantedPermissions.contains(perm)) {
+ if (isUpdatedSystemApp(pkg)) {
+ PackageSetting sysPs = mSettings.getDisabledSystemPkg(
+ pkg.packageName);
+ final GrantedPermissions origGp = sysPs.sharedUser != null
+ ? sysPs.sharedUser : sysPs;
+ if (origGp.grantedPermissions.contains(perm)) {
allowed = true;
} else {
allowed = false;
@@ -4552,6 +4400,8 @@ class PackageManagerService extends IPackageManager.Stub {
}
};
+ private static final boolean DEBUG_OBB = false;
+
private static final void sendPackageBroadcast(String action, String pkg,
Bundle extras, IIntentReceiver finishedReceiver) {
IActivityManager am = ActivityManagerNative.getDefault();
@@ -4665,7 +4515,8 @@ class PackageManagerService extends IPackageManager.Stub {
| PackageParser.PARSE_IS_SYSTEM_DIR: 0) |
PackageParser.PARSE_CHATTY |
PackageParser.PARSE_MUST_BE_APK,
- SCAN_MONITOR | SCAN_NO_PATHS);
+ SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
+ System.currentTimeMillis());
if (p != null) {
synchronized (mPackages) {
updatePermissionsLP(p.packageName, p,
@@ -4726,6 +4577,29 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
+ public void setPackageObbPath(String packageName, String path) {
+ if (DEBUG_OBB)
+ Log.v(TAG, "Setting .obb path for " + packageName + " to: " + path);
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ final int permission = mContext.checkCallingPermission(
+ android.Manifest.permission.INSTALL_PACKAGES);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (!allowedByPermission && (uid != pkgSetting.userId)) {
+ throw new SecurityException("Permission denial: attempt to set .obb file from pid="
+ + Binder.getCallingPid() + ", uid=" + uid + ", package uid="
+ + pkgSetting.userId);
+ }
+ pkgSetting.obbPathString = path;
+ mSettings.writeLP();
+ }
+ }
+
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
@@ -4877,7 +4751,7 @@ class PackageManagerService extends IPackageManager.Stub {
// App explictly prefers external. Let policy decide
} else {
// Prefer previous location
- if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ if (isExternal(pkg)) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
@@ -4918,7 +4792,15 @@ class PackageManagerService extends IPackageManager.Stub {
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
// Remote call to find out default install location
- PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags);
+ final PackageInfoLite pkgLite;
+ try {
+ mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags);
+ } finally {
+ mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION){
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
@@ -4960,7 +4842,12 @@ class PackageManagerService extends IPackageManager.Stub {
@Override
void handleReturnCode() {
- processPendingInstall(mArgs, mRet);
+ // If mArgs is null, then MCS couldn't be reached. When it
+ // reconnects, it will try again to install. At that point, this
+ // will succeed.
+ if (mArgs != null) {
+ processPendingInstall(mArgs, mRet);
+ }
}
@Override
@@ -4984,16 +4871,16 @@ class PackageManagerService extends IPackageManager.Stub {
final InstallArgs srcArgs;
final InstallArgs targetArgs;
int mRet;
- MoveParams(InstallArgs srcArgs,
- IPackageMoveObserver observer,
- int flags, String packageName) {
+
+ MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
+ String packageName, String dataDir) {
this.srcArgs = srcArgs;
this.observer = observer;
this.flags = flags;
this.packageName = packageName;
if (srcArgs != null) {
Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath()));
- targetArgs = createInstallArgs(packageUri, flags, packageName);
+ targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir);
} else {
targetArgs = null;
}
@@ -5049,21 +4936,22 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath) {
+ private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath,
+ String nativeLibraryPath) {
if (installOnSd(flags)) {
- return new SdInstallArgs(fullCodePath, fullResourcePath);
+ return new SdInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath);
} else {
- return new FileInstallArgs(fullCodePath, fullResourcePath);
+ return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath);
}
}
- private InstallArgs createInstallArgs(Uri packageURI, int flags,
- String pkgName) {
+ // Used by package mover
+ private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir) {
if (installOnSd(flags)) {
String cid = getNextCodePath(null, pkgName, "/" + SdInstallArgs.RES_FILE_NAME);
return new SdInstallArgs(packageURI, cid);
} else {
- return new FileInstallArgs(packageURI, pkgName);
+ return new FileInstallArgs(packageURI, pkgName, dataDir);
}
}
@@ -5090,6 +4978,7 @@ class PackageManagerService extends IPackageManager.Stub {
abstract int doPostInstall(int status);
abstract String getCodePath();
abstract String getResourcePath();
+ abstract String getNativeLibraryPath();
// Need installer lock especially for dex file removal.
abstract void cleanUpResourcesLI();
abstract boolean doPostDeleteLI(boolean delete);
@@ -5100,6 +4989,7 @@ class PackageManagerService extends IPackageManager.Stub {
File installDir;
String codeFileName;
String resourceFileName;
+ String libraryPath;
boolean created = false;
FileInstallArgs(InstallParams params) {
@@ -5107,25 +4997,32 @@ class PackageManagerService extends IPackageManager.Stub {
params.flags, params.installerPackageName);
}
- FileInstallArgs(String fullCodePath, String fullResourcePath) {
+ FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
super(null, null, 0, null);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
resourceFileName = fullResourcePath;
+ libraryPath = nativeLibraryPath;
}
- FileInstallArgs(Uri packageURI, String pkgName) {
+ FileInstallArgs(Uri packageURI, String pkgName, String dataDir) {
super(packageURI, null, 0, null);
- boolean fwdLocked = isFwdLocked(flags);
- installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
+ installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
resourceFileName = getResourcePathFromCodePath();
+ libraryPath = new File(dataDir, LIB_DIR_NAME).getPath();
}
- boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- return imcs.checkFreeStorage(false, packageURI);
+ boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+ try {
+ mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ return imcs.checkFreeStorage(false, packageURI);
+ } finally {
+ mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
}
String getCodePath() {
@@ -5133,8 +5030,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
void createCopyFile() {
- boolean fwdLocked = isFwdLocked(flags);
- installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
+ installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
codeFileName = createTempPackageFile(installDir).getPath();
resourceFileName = getResourcePathFromCodePath();
created = true;
@@ -5162,8 +5058,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
ParcelFileDescriptor out = null;
try {
- out = ParcelFileDescriptor.open(codeFile,
- ParcelFileDescriptor.MODE_READ_WRITE);
+ out = ParcelFileDescriptor.open(codeFile, ParcelFileDescriptor.MODE_READ_WRITE);
} catch (FileNotFoundException e) {
Slog.e(TAG, "Failed to create file descritpor for : " + codeFileName);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -5171,12 +5066,16 @@ class PackageManagerService extends IPackageManager.Stub {
// Copy the resource now
int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
try {
+ mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (imcs.copyResource(packageURI, out)) {
ret = PackageManager.INSTALL_SUCCEEDED;
}
} finally {
try { if (out != null) out.close(); } catch (IOException e) {}
+ mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
+
return ret;
}
@@ -5232,6 +5131,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ @Override
+ String getNativeLibraryPath() {
+ return libraryPath;
+ }
+
private boolean cleanUp() {
boolean ret = true;
String sourceDir = getCodePath();
@@ -5272,7 +5176,7 @@ class PackageManagerService extends IPackageManager.Stub {
private boolean setPermissions() {
// TODO Do this in a more elegant way later on. for now just a hack
- if (!isFwdLocked(flags)) {
+ if (!isFwdLocked()) {
final int filePermissions =
FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
|FileUtils.S_IROTH;
@@ -5290,35 +5194,42 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean doPostDeleteLI(boolean delete) {
+ // XXX err, shouldn't we respect the delete flag?
cleanUpResourcesLI();
return true;
}
+
+ private boolean isFwdLocked() {
+ return (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
+ }
}
class SdInstallArgs extends InstallArgs {
- String cid;
- String cachePath;
static final String RES_FILE_NAME = "pkg.apk";
+ String cid;
+ String packagePath;
+ String libraryPath;
+
SdInstallArgs(InstallParams params) {
super(params.packageURI, params.observer,
params.flags, params.installerPackageName);
}
- SdInstallArgs(String fullCodePath, String fullResourcePath) {
+ SdInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
super(null, null, PackageManager.INSTALL_EXTERNAL, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
int sidx = subStr1.lastIndexOf("/");
cid = subStr1.substring(sidx+1, eidx);
- cachePath = subStr1;
+ setCachePath(subStr1);
}
SdInstallArgs(String cid) {
super(null, null, PackageManager.INSTALL_EXTERNAL, null);
this.cid = cid;
- cachePath = PackageHelper.getSdDir(cid);
+ setCachePath(PackageHelper.getSdDir(cid));
}
SdInstallArgs(Uri packageURI, String cid) {
@@ -5330,29 +5241,52 @@ class PackageManagerService extends IPackageManager.Stub {
cid = getTempContainerId();
}
- boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- return imcs.checkFreeStorage(true, packageURI);
+ boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+ try {
+ mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ return imcs.checkFreeStorage(true, packageURI);
+ } finally {
+ mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (temp) {
createCopyFile();
}
- cachePath = imcs.copyResourceToContainer(
- packageURI, cid,
- getEncryptKey(), RES_FILE_NAME);
- return (cachePath == null) ? PackageManager.INSTALL_FAILED_CONTAINER_ERROR :
- PackageManager.INSTALL_SUCCEEDED;
+
+ final String newCachePath;
+ try {
+ mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ newCachePath = imcs.copyResourceToContainer(packageURI, cid,
+ getEncryptKey(), RES_FILE_NAME);
+ } finally {
+ mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+
+ if (newCachePath != null) {
+ setCachePath(newCachePath);
+ return PackageManager.INSTALL_SUCCEEDED;
+ } else {
+ return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+ }
}
@Override
String getCodePath() {
- return cachePath + "/" + RES_FILE_NAME;
+ return packagePath;
}
@Override
String getResourcePath() {
- return cachePath + "/" + RES_FILE_NAME;
+ return packagePath;
+ }
+
+ @Override
+ String getNativeLibraryPath() {
+ return libraryPath;
}
int doPreInstall(int status) {
@@ -5362,8 +5296,11 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
boolean mounted = PackageHelper.isContainerMounted(cid);
if (!mounted) {
- cachePath = PackageHelper.mountSdDir(cid, getEncryptKey(), Process.SYSTEM_UID);
- if (cachePath == null) {
+ String newCachePath = PackageHelper.mountSdDir(cid, getEncryptKey(),
+ Process.SYSTEM_UID);
+ if (newCachePath != null) {
+ setCachePath(newCachePath);
+ } else {
return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
}
}
@@ -5409,13 +5346,19 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
Log.i(TAG, "Succesfully renamed " + cid +
- " at path: " + cachePath + " to " + newCacheId +
+ " to " + newCacheId +
" at new path: " + newCachePath);
cid = newCacheId;
- cachePath = newCachePath;
+ setCachePath(newCachePath);
return true;
}
+ private void setCachePath(String newCachePath) {
+ File cachePath = new File(newCachePath);
+ libraryPath = new File(cachePath, LIB_DIR_NAME).getPath();
+ packagePath = new File(cachePath, RES_FILE_NAME).getPath();
+ }
+
int doPostInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
@@ -5590,7 +5533,8 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
- PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode);
+ PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
+ System.currentTimeMillis());
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -5610,7 +5554,7 @@ class PackageManagerService extends IPackageManager.Stub {
deletePackageLI(
pkgName, false,
dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,
- res.removedInfo);
+ res.removedInfo, true);
}
}
}
@@ -5631,7 +5575,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
}
- boolean sysPkg = ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+ boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
} else {
@@ -5653,16 +5597,24 @@ class PackageManagerService extends IPackageManager.Stub {
oldInstallerPackageName = mSettings.getInstallerPackageName(pkgName);
}
+ long origUpdateTime;
+ if (pkg.mExtras != null) {
+ origUpdateTime = ((PackageSetting)pkg.mExtras).lastUpdateTime;
+ } else {
+ origUpdateTime = 0;
+ }
+
// First delete the existing package while retaining the data directory
if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA,
- res.removedInfo)) {
- // If the existing package was'nt successfully deleted
+ res.removedInfo, true)) {
+ // If the existing package wasn't successfully deleted
res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
deletedPkg = false;
} else {
// Successfully deleted the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
- newPackage = scanPackageLI(pkg, parseFlags, scanMode);
+ newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
+ System.currentTimeMillis());
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -5685,7 +5637,7 @@ class PackageManagerService extends IPackageManager.Stub {
deletePackageLI(
pkgName, true,
PackageManager.DONT_DELETE_DATA,
- res.removedInfo);
+ res.removedInfo, true);
}
// Since we failed to install the new package we need to restore the old
// package that we deleted.
@@ -5700,8 +5652,10 @@ class PackageManagerService extends IPackageManager.Stub {
int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY |
(isForwardLocked(deletedPackage) ? PackageParser.PARSE_FORWARD_LOCK : 0) |
(oldOnSd ? PackageParser.PARSE_ON_SDCARD : 0);
- int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE;
- if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode) == null) {
+ int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
+ | SCAN_UPDATE_TIME;
+ if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
+ origUpdateTime) == null) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
return;
}
@@ -5752,13 +5706,18 @@ class PackageManagerService extends IPackageManager.Stub {
// Successfully disabled the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- newPackage = scanPackageLI(pkg, parseFlags, scanMode);
+ newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
+ if (newPackage.mExtras != null) {
+ final PackageSetting newPkgSetting = (PackageSetting)newPackage.mExtras;
+ newPkgSetting.firstInstallTime = oldPkgSetting.firstInstallTime;
+ newPkgSetting.lastUpdateTime = System.currentTimeMillis();
+ }
updateSettingsLI(newPackage, installerPackageName, res);
updatedSettings = true;
}
@@ -5770,12 +5729,10 @@ class PackageManagerService extends IPackageManager.Stub {
removePackageLI(newPackage, true);
}
// Add back the old system package
- scanPackageLI(oldPkg, parseFlags,
- SCAN_MONITOR
- | SCAN_UPDATE_SIGNATURE);
+ scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0);
// Restore the old system information in Settings
synchronized(mPackages) {
- if(updatedSettings) {
+ if (updatedSettings) {
mSettings.enableSystemPackageLP(packageName);
mSettings.setInstallerPackageName(packageName,
oldPkgSetting.installerPackageName);
@@ -5789,9 +5746,8 @@ class PackageManagerService extends IPackageManager.Stub {
PackageSetting ps = mSettings.getDisabledSystemPkg(packageName);
if (ps != null && ps.codePathString != null &&
!ps.codePathString.equals(oldPkgSetting.codePathString)) {
- int installFlags = 0;
res.removedInfo.args = createInstallArgs(0, oldPkgSetting.codePathString,
- oldPkgSetting.resourcePathString);
+ oldPkgSetting.resourcePathString, oldPkgSetting.nativeLibraryPathString);
}
}
}
@@ -5803,8 +5759,17 @@ class PackageManagerService extends IPackageManager.Stub {
if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
if (retCode != 0) {
- Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ if (mNoDexOpt) {
+ /*
+ * If we're in an engineering build, programs are lazily run
+ * through dexopt. If the .dex file doesn't exist yet, it
+ * will be created when the program is run next.
+ */
+ Slog.i(TAG, "dex file doesn't exist, skipping move: " + newPackage.mPath);
+ } else {
+ Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ }
}
}
return PackageManager.INSTALL_SUCCEEDED;
@@ -5933,6 +5898,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Set application objects path explicitly after the rename
setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
+ pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode,
installerPackageName, res);
@@ -5947,8 +5913,7 @@ class PackageManagerService extends IPackageManager.Stub {
int retCode = 0;
// TODO Gross hack but fix later. Ideally move this to be a post installation
// check after alloting uid.
- if ((newPackage.applicationInfo.flags
- & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
+ if (isForwardLocked(newPackage)) {
File destResourceFile = new File(newPackage.applicationInfo.publicSourceDir);
try {
extractPublicFiles(newPackage, destResourceFile);
@@ -5983,18 +5948,26 @@ class PackageManagerService extends IPackageManager.Stub {
return PackageManager.INSTALL_SUCCEEDED;
}
- private boolean isForwardLocked(PackageParser.Package pkg) {
- return ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+ private static boolean isForwardLocked(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0;
}
- private boolean isExternal(PackageParser.Package pkg) {
- return ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+ private static boolean isExternal(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+ }
+
+ private static boolean isSystemApp(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ private static boolean isUpdatedSystemApp(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
private void extractPublicFiles(PackageParser.Package newPackage,
File publicZipFile) throws IOException {
- final ZipOutputStream publicZipOutStream =
- new ZipOutputStream(new FileOutputStream(publicZipFile));
+ final FileOutputStream fstr = new FileOutputStream(publicZipFile);
+ final ZipOutputStream publicZipOutStream = new ZipOutputStream(fstr);
final ZipFile privateZip = new ZipFile(newPackage.mPath);
// Copy manifest, resources.arsc and res directory to public zip
@@ -6019,6 +5992,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ publicZipOutStream.finish();
+ publicZipOutStream.flush();
+ FileUtils.sync(fstr);
publicZipOutStream.close();
FileUtils.setPermissions(
publicZipFile.getAbsolutePath(),
@@ -6136,7 +6112,7 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) {
res = deletePackageLI(packageName, deleteCodeAndResources,
- flags | REMOVE_CHATTY, info);
+ flags | REMOVE_CHATTY, info, true);
}
if(res && sendBroadCast) {
@@ -6197,7 +6173,7 @@ class PackageManagerService extends IPackageManager.Stub {
* delete a partially installed application.
*/
private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo,
- int flags) {
+ int flags, boolean writeSettings) {
String packageName = p.packageName;
if (outInfo != null) {
outInfo.removedPackage = packageName;
@@ -6209,24 +6185,24 @@ class PackageManagerService extends IPackageManager.Stub {
deletedPs = mSettings.mPackages.get(packageName);
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
- int retCode = mInstaller.remove(packageName);
+ int retCode = mInstaller.remove(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
+ packageName + ", retcode=" + retCode);
// we don't consider this to be a failure of the core package deletion
}
} else {
- //for emulator
+ // for simulator
PackageParser.Package pkg = mPackages.get(packageName);
File dataDir = new File(pkg.applicationInfo.dataDir);
dataDir.delete();
}
+ schedulePackageCleaning(packageName);
}
synchronized (mPackages) {
if (deletedPs != null) {
- schedulePackageCleaning(packageName);
-
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (outInfo != null) {
outInfo.removedUid = mSettings.removePackageLP(packageName);
@@ -6250,8 +6226,10 @@ class PackageManagerService extends IPackageManager.Stub {
mSettings.mPreferredActivities.removeFilter(pa);
}
}
- // Save settings now
- mSettings.writeLP();
+ if (writeSettings) {
+ // Save settings now
+ mSettings.writeLP();
+ }
}
}
@@ -6259,7 +6237,7 @@ class PackageManagerService extends IPackageManager.Stub {
* Tries to delete system package.
*/
private boolean deleteSystemPackageLI(PackageParser.Package p,
- int flags, PackageRemovedInfo outInfo) {
+ int flags, PackageRemovedInfo outInfo, boolean writeSettings) {
ApplicationInfo applicationInfo = p.applicationInfo;
//applicable for non-partially installed applications only
if (applicationInfo == null) {
@@ -6281,33 +6259,31 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Delete the updated package
outInfo.isRemovedPackageSystemUpdate = true;
- boolean deleteCodeAndResources = false;
- if (ps.versionCode < p.mVersionCode) {
+ final boolean deleteCodeAndResources;
+ if (ps.versionCode < p.mVersionCode) {
// Delete code and resources for downgrades
deleteCodeAndResources = true;
- if ((flags & PackageManager.DONT_DELETE_DATA) == 0) {
- flags &= ~PackageManager.DONT_DELETE_DATA;
- }
+ flags &= ~PackageManager.DONT_DELETE_DATA;
} else {
// Preserve data by setting flag
- if ((flags & PackageManager.DONT_DELETE_DATA) == 0) {
- flags |= PackageManager.DONT_DELETE_DATA;
- }
+ deleteCodeAndResources = false;
+ flags |= PackageManager.DONT_DELETE_DATA;
}
- boolean ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo);
+ boolean ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo,
+ writeSettings);
if (!ret) {
return false;
}
synchronized (mPackages) {
// Reinstate the old system package
mSettings.enableSystemPackageLP(p.packageName);
- // Remove any native libraries.
- removeNativeBinariesLI(p);
+ // Remove any native libraries from the upgraded package.
+ NativeLibraryHelper.removeNativeBinariesLI(p.applicationInfo.nativeLibraryDir);
}
// Install the system package
PackageParser.Package newPkg = scanPackageLI(ps.codePath,
PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
- SCAN_MONITOR | SCAN_NO_PATHS);
+ SCAN_MONITOR | SCAN_NO_PATHS, 0);
if (newPkg == null) {
Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
@@ -6315,13 +6291,16 @@ class PackageManagerService extends IPackageManager.Stub {
}
synchronized (mPackages) {
updatePermissionsLP(newPkg.packageName, newPkg, true, true, false);
- mSettings.writeLP();
+ if (writeSettings) {
+ mSettings.writeLP();
+ }
}
return true;
}
private boolean deleteInstalledPackageLI(PackageParser.Package p,
- boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) {
+ boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo,
+ boolean writeSettings) {
ApplicationInfo applicationInfo = p.applicationInfo;
if (applicationInfo == null) {
Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
@@ -6332,17 +6311,15 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Delete package data from internal structures and also remove data if flag is set
- removePackageDataLI(p, outInfo, flags);
+ removePackageDataLI(p, outInfo, flags, writeSettings);
// Delete application code and resources
if (deleteCodeAndResources) {
// TODO can pick up from PackageSettings as well
- int installFlags = ((p.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE)!=0) ?
- PackageManager.INSTALL_EXTERNAL : 0;
- installFlags |= ((p.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK)!=0) ?
- PackageManager.INSTALL_FORWARD_LOCK : 0;
- outInfo.args = createInstallArgs(installFlags,
- applicationInfo.sourceDir, applicationInfo.publicSourceDir);
+ int installFlags = isExternal(p) ? PackageManager.INSTALL_EXTERNAL : 0;
+ installFlags |= isForwardLocked(p) ? PackageManager.INSTALL_FORWARD_LOCK : 0;
+ outInfo.args = createInstallArgs(installFlags, applicationInfo.sourceDir,
+ applicationInfo.publicSourceDir, applicationInfo.nativeLibraryDir);
}
return true;
}
@@ -6351,7 +6328,8 @@ class PackageManagerService extends IPackageManager.Stub {
* This method handles package deletion in general
*/
private boolean deletePackageLI(String packageName,
- boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) {
+ boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo,
+ boolean writeSettings) {
if (packageName == null) {
Slog.w(TAG, "Attempt to delete null packageName.");
return false;
@@ -6378,7 +6356,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (dataOnly) {
// Delete application data first
- removePackageDataLI(p, outInfo, flags);
+ removePackageDataLI(p, outInfo, flags, writeSettings);
return true;
}
// At this point the package should have ApplicationInfo associated with it
@@ -6387,16 +6365,17 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
boolean ret = false;
- if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (isSystemApp(p)) {
Log.i(TAG, "Removing system package:"+p.packageName);
// When an updated system application is deleted we delete the existing resources as well and
// fall back to existing code in system partition
- ret = deleteSystemPackageLI(p, flags, outInfo);
+ ret = deleteSystemPackageLI(p, flags, outInfo, writeSettings);
} else {
Log.i(TAG, "Removing non-system package:"+p.packageName);
// Kill application pre-emptively especially for apps on sd.
killApplication(packageName, p.applicationInfo.uid);
- ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo);
+ ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo,
+ writeSettings);
}
return ret;
}
@@ -6451,6 +6430,7 @@ class PackageManagerService extends IPackageManager.Stub {
p = ps.pkg;
}
}
+ boolean useEncryptedFSDir = false;
if(!dataOnly) {
//need to check this only for fully installed applications
@@ -6463,9 +6443,10 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
+ useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
}
if (mInstaller != null) {
- int retCode = mInstaller.clearUserData(packageName);
+ int retCode = mInstaller.clearUserData(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -6516,8 +6497,9 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
- int retCode = mInstaller.deleteCacheFiles(packageName);
+ int retCode = mInstaller.deleteCacheFiles(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -6579,9 +6561,10 @@ class PackageManagerService extends IPackageManager.Stub {
}
publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null;
}
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
int res = mInstaller.getSizeInfo(packageName, p.mPath,
- publicSrcDir, pStats);
+ publicSrcDir, pStats, useEncryptedFSDir);
if (res < 0) {
return false;
} else {
@@ -6814,6 +6797,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
pkgSetting.enabled = newState;
+ pkgSetting.pkg.mSetEnabled = newState;
} else {
// We're dealing with a component level state change
switch (newState) {
@@ -6972,7 +6956,18 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
+ boolean dumpStar = true;
+ boolean dumpLibs = false;
+ boolean dumpFeatures = false;
+ boolean dumpResolvers = false;
+ boolean dumpPermissions = false;
+ boolean dumpPackages = false;
+ boolean dumpSharedUsers = false;
+ boolean dumpMessages = false;
+ boolean dumpProviders = false;
+
String packageName = null;
+ boolean showFilters = false;
int opti = 0;
while (opti < args.length) {
@@ -6985,10 +6980,22 @@ class PackageManagerService extends IPackageManager.Stub {
// Right now we only know how to print all.
} else if ("-h".equals(opt)) {
pw.println("Package manager dump options:");
- pw.println(" [-h] [cmd] ...");
+ pw.println(" [-h] [-f] [cmd] ...");
+ pw.println(" -f: print details of intent filters");
+ pw.println(" -h: print this help");
pw.println(" cmd may be one of:");
- pw.println(" [package.name]: info about given package");
+ pw.println(" l[ibraries]: list known shared libraries");
+ pw.println(" f[ibraries]: list device features");
+ pw.println(" r[esolvers]: dump intent resolvers");
+ pw.println(" perm[issions]: dump permissions");
+ pw.println(" prov[iders]: dump content providers");
+ pw.println(" p[ackages]: dump installed packages");
+ pw.println(" s[hared-users]: dump shared user IDs");
+ pw.println(" m[essages]: print collected runtime messages");
+ pw.println(" <package.name>: info about given package");
return;
+ } else if ("-f".equals(opt)) {
+ showFilters = true;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
@@ -7001,32 +7008,87 @@ class PackageManagerService extends IPackageManager.Stub {
// Is this a package name?
if ("android".equals(cmd) || cmd.contains(".")) {
packageName = cmd;
+ } else if ("l".equals(cmd) || "libraries".equals(cmd)) {
+ dumpStar = false;
+ dumpLibs = true;
+ } else if ("f".equals(cmd) || "features".equals(cmd)) {
+ dumpStar = false;
+ dumpFeatures = true;
+ } else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
+ dumpStar = false;
+ dumpResolvers = true;
+ } else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
+ dumpStar = false;
+ dumpPermissions = true;
+ } else if ("p".equals(cmd) || "packages".equals(cmd)) {
+ dumpStar = false;
+ dumpPackages = true;
+ } else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
+ dumpStar = false;
+ dumpSharedUsers = true;
+ } else if ("prov".equals(cmd) || "providers".equals(cmd)) {
+ dumpStar = false;
+ dumpProviders = true;
+ } else if ("m".equals(cmd) || "messages".equals(cmd)) {
+ dumpStar = false;
+ dumpMessages = true;
}
}
boolean printedTitle = false;
synchronized (mPackages) {
- if (mActivities.dump(pw, "Activity Resolver Table:", " ", packageName)) {
- printedTitle = true;
- }
- if (mReceivers.dump(pw, printedTitle
- ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
- " ", packageName)) {
+ if ((dumpStar || dumpLibs) && packageName == null) {
+ if (printedTitle) pw.println(" ");
printedTitle = true;
+ pw.println("Libraries:");
+ Iterator<String> it = mSharedLibraries.keySet().iterator();
+ while (it.hasNext()) {
+ String name = it.next();
+ pw.print(" ");
+ pw.print(name);
+ pw.print(" -> ");
+ pw.println(mSharedLibraries.get(name));
+ }
}
- if (mServices.dump(pw, printedTitle
- ? "\nService Resolver Table:" : "Service Resolver Table:",
- " ", packageName)) {
+
+ if ((dumpStar || dumpFeatures) && packageName == null) {
+ if (printedTitle) pw.println(" ");
printedTitle = true;
+ pw.println("Features:");
+ Iterator<String> it = mAvailableFeatures.keySet().iterator();
+ while (it.hasNext()) {
+ String name = it.next();
+ pw.print(" ");
+ pw.println(name);
+ }
}
- if (mSettings.mPreferredActivities.dump(pw, printedTitle
- ? "\nPreferred Activities:" : "Preferred Activities:",
- " ", packageName)) {
- printedTitle = true;
+
+ if (dumpStar || dumpResolvers) {
+ if (mActivities.dump(pw, printedTitle
+ ? "\nActivity Resolver Table:" : "Activity Resolver Table:",
+ " ", packageName, showFilters)) {
+ printedTitle = true;
+ }
+ if (mReceivers.dump(pw, printedTitle
+ ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
+ " ", packageName, showFilters)) {
+ printedTitle = true;
+ }
+ if (mServices.dump(pw, printedTitle
+ ? "\nService Resolver Table:" : "Service Resolver Table:",
+ " ", packageName, showFilters)) {
+ printedTitle = true;
+ }
+ if (mSettings.mPreferredActivities.dump(pw, printedTitle
+ ? "\nPreferred Activities:" : "Preferred Activities:",
+ " ", packageName, showFilters)) {
+ printedTitle = true;
+ }
}
+
boolean printedSomething = false;
- {
+ if (dumpStar || dumpPermissions) {
for (BasePermission p : mSettings.mPermissions.values()) {
if (packageName != null && !packageName.equals(p.sourcePackage)) {
continue;
@@ -7053,9 +7115,29 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ if (dumpStar || dumpProviders) {
+ printedSomething = false;
+ for (PackageParser.Provider p : mProviders.values()) {
+ if (packageName != null && !packageName.equals(p.info.packageName)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Registered ContentProviders:");
+ printedSomething = true;
+ printedTitle = true;
+ }
+ pw.print(" ["); pw.print(p.info.authority); pw.print("]: ");
+ pw.println(p.toString());
+ }
+ }
+
printedSomething = false;
SharedUserSetting packageSharedUser = null;
- {
+ if (dumpStar || dumpPackages) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Date date = new Date();
for (PackageSetting ps : mSettings.mPackages.values()) {
if (packageName != null && !packageName.equals(ps.realName)
&& !packageName.equals(ps.name)) {
@@ -7082,12 +7164,23 @@ class PackageManagerService extends IPackageManager.Stub {
pw.print(" pkg="); pw.println(ps.pkg);
pw.print(" codePath="); pw.println(ps.codePathString);
pw.print(" resourcePath="); pw.println(ps.resourcePathString);
+ pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString);
+ pw.print(" obbPath="); pw.println(ps.obbPathString);
if (ps.pkg != null) {
pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion);
+ if (ps.pkg.mOperationPending) {
+ pw.println(" mOperationPending=true");
+ }
pw.print(" supportsScreens=[");
boolean first = true;
if ((ps.pkg.applicationInfo.flags &
+ ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ if (!first) pw.print(", ");
+ first = false;
+ pw.print("small");
+ }
+ if ((ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
if (!first) pw.print(", ");
first = false;
@@ -7100,10 +7193,10 @@ class PackageManagerService extends IPackageManager.Stub {
pw.print("large");
}
if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
if (!first) pw.print(", ");
first = false;
- pw.print("small");
+ pw.print("xlarge");
}
if ((ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
@@ -7119,13 +7212,20 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
pw.println("]");
- pw.print(" timeStamp="); pw.println(ps.getTimeStampStr());
+ pw.print(" timeStamp=");
+ date.setTime(ps.timeStamp); pw.println(sdf.format(date));
+ pw.print(" firstInstallTime=");
+ date.setTime(ps.firstInstallTime); pw.println(sdf.format(date));
+ pw.print(" lastUpdateTime=");
+ date.setTime(ps.lastUpdateTime); pw.println(sdf.format(date));
pw.print(" signatures="); pw.println(ps.signatures);
pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
pw.print(" haveGids="); pw.println(ps.haveGids);
pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
pw.print(" installStatus="); pw.print(ps.installStatus);
pw.print(" enabled="); pw.println(ps.enabled);
+ pw.print(" versionCode="); pw.print(ps.versionCode);
+ pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
if (ps.disabledComponents.size() > 0) {
pw.println(" disabledComponents:");
for (String s : ps.disabledComponents) {
@@ -7147,52 +7247,54 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
printedSomething = false;
- if (mSettings.mRenamedPackages.size() > 0) {
- for (HashMap.Entry<String, String> e
- : mSettings.mRenamedPackages.entrySet()) {
- if (packageName != null && !packageName.equals(e.getKey())
- && !packageName.equals(e.getValue())) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Renamed packages:");
- printedSomething = true;
- printedTitle = true;
+ if (dumpStar || dumpPackages) {
+ if (mSettings.mRenamedPackages.size() > 0) {
+ for (HashMap.Entry<String, String> e
+ : mSettings.mRenamedPackages.entrySet()) {
+ if (packageName != null && !packageName.equals(e.getKey())
+ && !packageName.equals(e.getValue())) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Renamed packages:");
+ printedSomething = true;
+ printedTitle = true;
+ }
+ pw.print(" "); pw.print(e.getKey()); pw.print(" -> ");
+ pw.println(e.getValue());
}
- pw.print(" "); pw.print(e.getKey()); pw.print(" -> ");
- pw.println(e.getValue());
}
- }
- printedSomething = false;
- if (mSettings.mDisabledSysPackages.size() > 0) {
- for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
- if (packageName != null && !packageName.equals(ps.realName)
- && !packageName.equals(ps.name)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Hidden system packages:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" Package [");
- pw.print(ps.realName != null ? ps.realName : ps.name);
- pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(ps)));
- pw.println("):");
- if (ps.realName != null) {
- pw.print(" compat name="); pw.println(ps.name);
+ printedSomething = false;
+ if (mSettings.mDisabledSysPackages.size() > 0) {
+ for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
+ if (packageName != null && !packageName.equals(ps.realName)
+ && !packageName.equals(ps.name)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Hidden system packages:");
+ printedSomething = true;
+ printedTitle = true;
+ }
+ pw.print(" Package [");
+ pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(ps)));
+ pw.println("):");
+ if (ps.realName != null) {
+ pw.print(" compat name="); pw.println(ps.name);
+ }
+ pw.print(" userId="); pw.println(ps.userId);
+ pw.print(" sharedUser="); pw.println(ps.sharedUser);
+ pw.print(" codePath="); pw.println(ps.codePathString);
+ pw.print(" resourcePath="); pw.println(ps.resourcePathString);
}
- pw.print(" userId="); pw.println(ps.userId);
- pw.print(" sharedUser="); pw.println(ps.sharedUser);
- pw.print(" codePath="); pw.println(ps.codePathString);
- pw.print(" resourcePath="); pw.println(ps.resourcePathString);
}
}
printedSomething = false;
- {
+ if (dumpStar || dumpSharedUsers) {
for (SharedUserSetting su : mSettings.mSharedUsers.values()) {
if (packageName != null && su != packageSharedUser) {
continue;
@@ -7215,11 +7317,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if (packageName == null) {
+ if ((dumpStar || dumpMessages) && packageName == null) {
if (printedTitle) pw.println(" ");
printedTitle = true;
pw.println("Settings parse messages:");
- pw.println(mSettings.mReadMessages.toString());
+ pw.print(mSettings.mReadMessages.toString());
pw.println(" ");
pw.println("Package warning messages:");
@@ -7230,29 +7332,12 @@ class PackageManagerService extends IPackageManager.Stub {
int avail = in.available();
byte[] data = new byte[avail];
in.read(data);
- pw.println(new String(data));
+ pw.print(new String(data));
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
}
-
- synchronized (mProviders) {
- boolean printedSomething = false;
- for (PackageParser.Provider p : mProviders.values()) {
- if (packageName != null && !packageName.equals(p.info.packageName)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Registered ContentProviders:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" ["); pw.print(p.info.authority); pw.print("]: ");
- pw.println(p.toString());
- }
- }
}
static final class BasePermission {
@@ -7627,7 +7712,8 @@ class PackageManagerService extends IPackageManager.Stub {
this.pkgFlags = pkgFlags & (
ApplicationInfo.FLAG_SYSTEM |
ApplicationInfo.FLAG_FORWARD_LOCK |
- ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+ ApplicationInfo.FLAG_EXTERNAL_STORAGE |
+ ApplicationInfo.FLAG_NEVER_ENCRYPT);
}
}
@@ -7641,8 +7727,11 @@ class PackageManagerService extends IPackageManager.Stub {
String codePathString;
File resourcePath;
String resourcePathString;
- private long timeStamp;
- private String timeStampString = "0";
+ String nativeLibraryPathString;
+ String obbPathString;
+ long timeStamp;
+ long firstInstallTime;
+ long lastUpdateTime;
int versionCode;
boolean uidError;
@@ -7665,21 +7754,23 @@ class PackageManagerService extends IPackageManager.Stub {
String installerPackageName;
PackageSettingBase(String name, String realName, File codePath, File resourcePath,
- int pVersionCode, int pkgFlags) {
+ String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
super(pkgFlags);
this.name = name;
this.realName = realName;
- init(codePath, resourcePath, pVersionCode);
+ init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
}
- void init(File codePath, File resourcePath, int pVersionCode) {
+ void init(File codePath, File resourcePath, String nativeLibraryPathString,
+ int pVersionCode) {
this.codePath = codePath;
this.codePathString = codePath.toString();
this.resourcePath = resourcePath;
this.resourcePathString = resourcePath.toString();
+ this.nativeLibraryPathString = nativeLibraryPathString;
this.versionCode = pVersionCode;
}
-
+
public void setInstallerPackageName(String packageName) {
installerPackageName = packageName;
}
@@ -7697,23 +7788,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public void setTimeStamp(long newStamp) {
- if (newStamp != timeStamp) {
- timeStamp = newStamp;
- timeStampString = Long.toString(newStamp);
- }
- }
-
- public void setTimeStamp(long newStamp, String newStampStr) {
timeStamp = newStamp;
- timeStampString = newStampStr;
- }
-
- public long getTimeStamp() {
- return timeStamp;
- }
-
- public String getTimeStampStr() {
- return timeStampString;
}
public void copyFrom(PackageSettingBase base) {
@@ -7721,7 +7796,8 @@ class PackageManagerService extends IPackageManager.Stub {
gids = base.gids;
timeStamp = base.timeStamp;
- timeStampString = base.timeStampString;
+ firstInstallTime = base.firstInstallTime;
+ lastUpdateTime = base.lastUpdateTime;
signatures = base.signatures;
permissionsFixed = base.permissionsFixed;
haveGids = base.haveGids;
@@ -7769,8 +7845,9 @@ class PackageManagerService extends IPackageManager.Stub {
SharedUserSetting sharedUser;
PackageSetting(String name, String realName, File codePath, File resourcePath,
- int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, pVersionCode, pkgFlags);
+ String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+ pkgFlags);
}
@Override
@@ -7882,8 +7959,9 @@ class PackageManagerService extends IPackageManager.Stub {
final int sharedId;
PendingPackage(String name, String realName, File codePath, File resourcePath,
- int sharedId, int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, pVersionCode, pkgFlags);
+ String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString,
+ pVersionCode, pkgFlags);
this.sharedId = sharedId;
}
}
@@ -7894,11 +7972,17 @@ class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
// TODO(oam): This secure dir creation needs to be moved somewhere else (later)
+ File systemSecureDir = new File(dataDir, "secure/system");
systemDir.mkdirs();
+ systemSecureDir.mkdirs();
FileUtils.setPermissions(systemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
+ FileUtils.setPermissions(systemSecureDir.toString(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG
+ |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+ -1, -1);
mSettingsFilename = new File(systemDir, "packages.xml");
mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
mPackageListFilename = new File(systemDir, "packages.list");
@@ -7906,10 +7990,10 @@ class PackageManagerService extends IPackageManager.Stub {
PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- int pkgFlags, boolean create, boolean add) {
+ String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
final String name = pkg.packageName;
PackageSetting p = getPackageLP(name, origPackage, realName, sharedUser, codePath,
- resourcePath, pkg.mVersionCode, pkgFlags, create, add);
+ resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
return p;
}
@@ -8005,14 +8089,14 @@ class PackageManagerService extends IPackageManager.Stub {
if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- PackageSetting ret = addPackageLP(name, p.realName, p.codePath,
- p.resourcePath, p.userId, p.versionCode, p.pkgFlags);
+ PackageSetting ret = addPackageLP(name, p.realName, p.codePath, p.resourcePath,
+ p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags);
mDisabledSysPackages.remove(name);
return ret;
}
- PackageSetting addPackageLP(String name, String realName, File codePath,
- File resourcePath, int uid, int vc, int pkgFlags) {
+ PackageSetting addPackageLP(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (p.userId == uid) {
@@ -8022,7 +8106,8 @@ class PackageManagerService extends IPackageManager.Stub {
"Adding duplicate package, keeping first: " + name);
return null;
}
- p = new PackageSetting(name, realName, codePath, resourcePath, vc, pkgFlags);
+ p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
+ vc, pkgFlags);
p.userId = uid;
if (addUserIdLP(uid, p, name)) {
mPackages.put(name, p);
@@ -8077,12 +8162,12 @@ class PackageManagerService extends IPackageManager.Stub {
private PackageSetting getPackageLP(String name, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- int vc, int pkgFlags, boolean create, boolean add) {
+ String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (!p.codePath.equals(codePath)) {
// Check to see if its a disabled system app
- if((p != null) && ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+ if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
// This is an updated system app with versions in both system
// and data partition. Just let the most recent version
// take precedence.
@@ -8093,6 +8178,13 @@ class PackageManagerService extends IPackageManager.Stub {
// let's log a message about it.
Slog.i(TAG, "Package " + name + " codePath changed from " + p.codePath
+ " to " + codePath + "; Retaining data and using new");
+ /*
+ * Since we've changed paths, we need to prefer the new
+ * native library path over the one stored in the
+ * package settings since we might have moved from
+ * internal to external storage or vice versa.
+ */
+ p.nativeLibraryPathString = nativeLibraryPathString;
}
}
if (p.sharedUser != sharedUser) {
@@ -8120,8 +8212,8 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (origPackage != null) {
// We are consuming the data from an existing package.
- p = new PackageSetting(origPackage.name, name, codePath,
- resourcePath, vc, pkgFlags);
+ p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
+ nativeLibraryPathString, vc, pkgFlags);
if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name
+ " is adopting original package " + origPackage.name);
// Note that we will retain the new package's signature so
@@ -8137,7 +8229,8 @@ class PackageManagerService extends IPackageManager.Stub {
// Update new package state.
p.setTimeStamp(codePath.lastModified());
} else {
- p = new PackageSetting(name, realName, codePath, resourcePath, vc, pkgFlags);
+ p = new PackageSetting(name, realName, codePath, resourcePath,
+ nativeLibraryPathString, vc, pkgFlags);
p.setTimeStamp(codePath.lastModified());
p.sharedUser = sharedUser;
if (sharedUser != null) {
@@ -8185,8 +8278,9 @@ class PackageManagerService extends IPackageManager.Stub {
private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) {
p.pkg = pkg;
- String codePath = pkg.applicationInfo.sourceDir;
- String resourcePath = pkg.applicationInfo.publicSourceDir;
+ pkg.mSetEnabled = p.enabled;
+ final String codePath = pkg.applicationInfo.sourceDir;
+ final String resourcePath = pkg.applicationInfo.publicSourceDir;
// Update code path if needed
if (!codePath.equalsIgnoreCase(p.codePathString)) {
Slog.w(TAG, "Code path for pkg : " + p.pkg.packageName +
@@ -8201,6 +8295,12 @@ class PackageManagerService extends IPackageManager.Stub {
p.resourcePath = new File(resourcePath);
p.resourcePathString = resourcePath;
}
+ // Update the native library path if needed
+ final String nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir;
+ if (nativeLibraryPath != null
+ && !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) {
+ p.nativeLibraryPathString = nativeLibraryPath;
+ }
// Update version code if needed
if (pkg.mVersionCode != p.versionCode) {
p.versionCode = pkg.mVersionCode;
@@ -8396,7 +8496,8 @@ class PackageManagerService extends IPackageManager.Stub {
mPastSignatures.clear();
try {
- FileOutputStream str = new FileOutputStream(mSettingsFilename);
+ FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
+ BufferedOutputStream str = new BufferedOutputStream(fstr);
//XmlSerializer serializer = XmlUtils.serializerInstance();
XmlSerializer serializer = new FastXmlSerializer();
@@ -8477,6 +8578,7 @@ class PackageManagerService extends IPackageManager.Stub {
serializer.endDocument();
str.flush();
+ FileUtils.sync(fstr);
str.close();
// New settings successfully written, old ones are no longer
@@ -8493,12 +8595,13 @@ class PackageManagerService extends IPackageManager.Stub {
File tempFile = new File(mPackageListFilename.toString() + ".tmp");
JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
- str = new FileOutputStream(journal.chooseForWrite());
+ fstr = new FileOutputStream(journal.chooseForWrite());
+ str = new BufferedOutputStream(fstr);
try {
StringBuilder sb = new StringBuilder();
for (PackageSetting pkg : mPackages.values()) {
ApplicationInfo ai = pkg.pkg.applicationInfo;
- String dataPath = ai.dataDir;
+ String dataPath = ai.dataDir;
boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Avoid any application that has a space in its path
@@ -8529,6 +8632,7 @@ class PackageManagerService extends IPackageManager.Stub {
str.write(sb.toString().getBytes());
}
str.flush();
+ FileUtils.sync(fstr);
str.close();
journal.commit();
}
@@ -8566,11 +8670,16 @@ class PackageManagerService extends IPackageManager.Stub {
serializer.attribute(null, "realName", pkg.realName);
}
serializer.attribute(null, "codePath", pkg.codePathString);
- serializer.attribute(null, "ts", pkg.getTimeStampStr());
+ serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
+ serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
+ serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
if (!pkg.resourcePathString.equals(pkg.codePathString)) {
serializer.attribute(null, "resourcePath", pkg.resourcePathString);
}
+ if (pkg.nativeLibraryPathString != null) {
+ serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
+ }
if (pkg.sharedUser == null) {
serializer.attribute(null, "userId",
Integer.toString(pkg.userId));
@@ -8610,9 +8719,14 @@ class PackageManagerService extends IPackageManager.Stub {
if (!pkg.resourcePathString.equals(pkg.codePathString)) {
serializer.attribute(null, "resourcePath", pkg.resourcePathString);
}
+ if (pkg.nativeLibraryPathString != null) {
+ serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
+ }
serializer.attribute(null, "flags",
Integer.toString(pkg.pkgFlags));
- serializer.attribute(null, "ts", pkg.getTimeStampStr());
+ serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
+ serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
+ serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
if (pkg.sharedUser == null) {
serializer.attribute(null, "userId",
@@ -8635,6 +8749,9 @@ class PackageManagerService extends IPackageManager.Stub {
if (pkg.installerPackageName != null) {
serializer.attribute(null, "installer", pkg.installerPackageName);
}
+ if (pkg.obbPathString != null) {
+ serializer.attribute(null, "obbPath", pkg.obbPathString);
+ }
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
serializer.startTag(null, "perms");
@@ -8730,7 +8847,7 @@ class PackageManagerService extends IPackageManager.Stub {
try {
str = new FileInputStream(mBackupSettingsFilename);
mReadMessages.append("Reading from backup settings file\n");
- Log.i(TAG, "Reading from backup settings file!");
+ reportSettingsProblem(Log.INFO, "Need to read from backup settings file");
if (mSettingsFilename.exists()) {
// If both the backup and settings file exist, we
// ignore the settings since it might have been
@@ -8749,7 +8866,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (str == null) {
if (!mSettingsFilename.exists()) {
mReadMessages.append("No settings file found\n");
- Slog.i(TAG, "No current settings file!");
+ reportSettingsProblem(Log.INFO, "No settings file; creating initial state");
return false;
}
str = new FileInputStream(mSettingsFilename);
@@ -8765,7 +8882,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (type != XmlPullParser.START_TAG) {
mReadMessages.append("No start tag found in settings file\n");
- Slog.e(TAG, "No start tag found in package manager settings");
+ reportSettingsProblem(Log.WARN, "No start tag found in package manager settings");
return false;
}
@@ -8828,10 +8945,12 @@ class PackageManagerService extends IPackageManager.Stub {
} catch(XmlPullParserException e) {
mReadMessages.append("Error reading: " + e.toString());
+ reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Slog.e(TAG, "Error reading package manager settings", e);
} catch(java.io.IOException e) {
mReadMessages.append("Error reading: " + e.toString());
+ reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Slog.e(TAG, "Error reading package manager settings", e);
}
@@ -8842,10 +8961,10 @@ class PackageManagerService extends IPackageManager.Stub {
Object idObj = getUserIdLP(pp.sharedId);
if (idObj != null && idObj instanceof SharedUserSetting) {
PackageSetting p = getPackageLP(pp.name, null, pp.realName,
- (SharedUserSetting)idObj, pp.codePath, pp.resourcePath,
- pp.versionCode, pp.pkgFlags, true, true);
+ (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
+ pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
if (p == null) {
- Slog.w(TAG, "Unable to create application package for "
+ reportSettingsProblem(Log.WARN, "Unable to create application package for "
+ pp.name);
continue;
}
@@ -8855,13 +8974,13 @@ class PackageManagerService extends IPackageManager.Stub {
+ " has shared uid " + pp.sharedId
+ " that is not a shared uid\n";
mReadMessages.append(msg);
- Slog.e(TAG, msg);
+ reportSettingsProblem(Log.ERROR, msg);
} else {
String msg = "Bad package setting: package " + pp.name
+ " has shared uid " + pp.sharedId
+ " that is not defined\n";
mReadMessages.append(msg);
- Slog.e(TAG, msg);
+ reportSettingsProblem(Log.ERROR, msg);
}
}
mPendingPackages.clear();
@@ -8948,6 +9067,7 @@ class PackageManagerService extends IPackageManager.Stub {
String realName = parser.getAttributeValue(null, "realName");
String codePathStr = parser.getAttributeValue(null, "codePath");
String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
if (resourcePathStr == null) {
resourcePathStr = codePathStr;
}
@@ -8962,14 +9082,36 @@ class PackageManagerService extends IPackageManager.Stub {
int pkgFlags = 0;
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- PackageSetting ps = new PackageSetting(name, realName,
- new File(codePathStr),
- new File(resourcePathStr), versionCode, pkgFlags);
- String timeStampStr = parser.getAttributeValue(null, "ts");
+ PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
+ new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags);
+ String timeStampStr = parser.getAttributeValue(null, "ft");
+ if (timeStampStr != null) {
+ try {
+ long timeStamp = Long.parseLong(timeStampStr, 16);
+ ps.setTimeStamp(timeStamp);
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ timeStampStr = parser.getAttributeValue(null, "ts");
+ if (timeStampStr != null) {
+ try {
+ long timeStamp = Long.parseLong(timeStampStr);
+ ps.setTimeStamp(timeStamp);
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "it");
if (timeStampStr != null) {
try {
- long timeStamp = Long.parseLong(timeStampStr);
- ps.setTimeStamp(timeStamp, timeStampStr);
+ ps.firstInstallTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "ut");
+ if (timeStampStr != null) {
+ try {
+ ps.lastUpdateTime = Long.parseLong(timeStampStr, 16);
} catch (NumberFormatException e) {
}
}
@@ -9011,12 +9153,15 @@ class PackageManagerService extends IPackageManager.Stub {
String sharedIdStr = null;
String codePathStr = null;
String resourcePathStr = null;
+ String nativeLibraryPathStr = null;
+ String obbPathStr = null;
String systemStr = null;
String installerPackageName = null;
String uidError = null;
int pkgFlags = 0;
- String timeStampStr;
long timeStamp = 0;
+ long firstInstallTime = 0;
+ long lastUpdateTime = 0;
PackageSettingBase packageSetting = null;
String version = null;
int versionCode = 0;
@@ -9028,6 +9173,8 @@ class PackageManagerService extends IPackageManager.Stub {
sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
codePathStr = parser.getAttributeValue(null, "codePath");
resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ obbPathStr = parser.getAttributeValue(null, "obbPath");
version = parser.getAttributeValue(null, "version");
if (version != null) {
try {
@@ -9054,10 +9201,32 @@ class PackageManagerService extends IPackageManager.Stub {
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
}
}
- timeStampStr = parser.getAttributeValue(null, "ts");
+ String timeStampStr = parser.getAttributeValue(null, "ft");
if (timeStampStr != null) {
try {
- timeStamp = Long.parseLong(timeStampStr);
+ timeStamp = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ timeStampStr = parser.getAttributeValue(null, "ts");
+ if (timeStampStr != null) {
+ try {
+ timeStamp = Long.parseLong(timeStampStr);
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "it");
+ if (timeStampStr != null) {
+ try {
+ firstInstallTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "ut");
+ if (timeStampStr != null) {
+ try {
+ lastUpdateTime = Long.parseLong(timeStampStr, 16);
} catch (NumberFormatException e) {
}
}
@@ -9079,9 +9248,9 @@ class PackageManagerService extends IPackageManager.Stub {
"Error in package manager settings: <package> has no codePath at "
+ parser.getPositionDescription());
} else if (userId > 0) {
- packageSetting = addPackageLP(name.intern(), realName,
- new File(codePathStr), new File(resourcePathStr),
- userId, versionCode, pkgFlags);
+ packageSetting = addPackageLP(name.intern(), realName, new File(codePathStr),
+ new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode,
+ pkgFlags);
if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
+ ": userId=" + userId + " pkg=" + packageSetting);
if (packageSetting == null) {
@@ -9090,7 +9259,9 @@ class PackageManagerService extends IPackageManager.Stub {
+ " while parsing settings at "
+ parser.getPositionDescription());
} else {
- packageSetting.setTimeStamp(timeStamp, timeStampStr);
+ packageSetting.setTimeStamp(timeStamp);
+ packageSetting.firstInstallTime = firstInstallTime;
+ packageSetting.lastUpdateTime = lastUpdateTime;
}
} else if (sharedIdStr != null) {
userId = sharedIdStr != null
@@ -9098,8 +9269,10 @@ class PackageManagerService extends IPackageManager.Stub {
if (userId > 0) {
packageSetting = new PendingPackage(name.intern(), realName,
new File(codePathStr), new File(resourcePathStr),
- userId, versionCode, pkgFlags);
- packageSetting.setTimeStamp(timeStamp, timeStampStr);
+ nativeLibraryPathStr, userId, versionCode, pkgFlags);
+ packageSetting.setTimeStamp(timeStamp);
+ packageSetting.firstInstallTime = firstInstallTime;
+ packageSetting.lastUpdateTime = lastUpdateTime;
mPendingPackages.add((PendingPackage) packageSetting);
if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
+ ": sharedUserId=" + userId + " pkg="
@@ -9125,6 +9298,8 @@ class PackageManagerService extends IPackageManager.Stub {
if (packageSetting != null) {
packageSetting.uidError = "true".equals(uidError);
packageSetting.installerPackageName = installerPackageName;
+ packageSetting.nativeLibraryPathString = nativeLibraryPathStr;
+ packageSetting.obbPathString = obbPathStr;
final String enabledStr = parser.getAttributeValue(null, "enabled");
if (enabledStr != null) {
if (enabledStr.equalsIgnoreCase("true")) {
@@ -9404,6 +9579,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean isEnabledLP(ComponentInfo componentInfo, int flags) {
+ if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
+ return true;
+ }
final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
if (Config.LOGV) {
Log.v(TAG, "isEnabledLock - packageName = " + componentInfo.packageName
@@ -9419,30 +9597,37 @@ class PackageManagerService extends IPackageManager.Stub {
Debug.waitForDebugger();
Log.i(TAG, "We will crash!");
}
+ return false;
+ }
+ if (packageSettings.enabled == COMPONENT_ENABLED_STATE_DISABLED
+ || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
+ && packageSettings.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
+ return false;
+ }
+ if (packageSettings.enabledComponents.contains(componentInfo.name)) {
+ return true;
}
- return ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0)
- || ((componentInfo.enabled
- && ((packageSettings.enabled == COMPONENT_ENABLED_STATE_ENABLED)
- || (componentInfo.applicationInfo.enabled
- && packageSettings.enabled != COMPONENT_ENABLED_STATE_DISABLED))
- && !packageSettings.disabledComponents.contains(componentInfo.name))
- || packageSettings.enabledComponents.contains(componentInfo.name));
+ if (packageSettings.disabledComponents.contains(componentInfo.name)) {
+ return false;
+ }
+ return componentInfo.enabled;
}
}
// ------- apps on sdcard specific code -------
static final boolean DEBUG_SD_INSTALL = false;
- final private String mSdEncryptKey = "AppsOnSD";
- final private String mSdEncryptAlg = "AES";
+ private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
+ private static final String SD_ENCRYPTION_ALGORITHM = "AES";
+ static final int MAX_CONTAINERS = 250;
private boolean mMediaMounted = false;
- private static final int MAX_CONTAINERS = 250;
private String getEncryptKey() {
try {
- String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
+ String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
+ SD_ENCRYPTION_KEYSTORE_NAME);
if (sdEncKey == null) {
- sdEncKey = SystemKeyStore.getInstance().
- generateNewKeyHexString(128, mSdEncryptAlg, mSdEncryptKey);
+ sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128,
+ SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME);
if (sdEncKey == null) {
Slog.e(TAG, "Failed to create encryption keys");
return null;
@@ -9452,51 +9637,36 @@ class PackageManagerService extends IPackageManager.Stub {
} catch (NoSuchAlgorithmException nsae) {
Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae);
return null;
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Failed to retrieve encryption keys with exception: "
+ + ioe);
+ return null;
}
+
}
- static String getTempContainerId() {
- String prefix = "smdl2tmp";
- int tmpIdx = 1;
- String list[] = PackageHelper.getSecureContainerList();
- if (list != null) {
- int idx = 0;
- int idList[] = new int[MAX_CONTAINERS];
- boolean neverFound = true;
- for (String name : list) {
- // Ignore null entries
- if (name == null) {
- continue;
- }
- int sidx = name.indexOf(prefix);
- if (sidx == -1) {
- // Not a temp file. just ignore
- continue;
- }
- String subStr = name.substring(sidx + prefix.length());
- idList[idx] = -1;
- if (subStr != null) {
- try {
- int cid = Integer.parseInt(subStr);
- idList[idx++] = cid;
- neverFound = false;
- } catch (NumberFormatException e) {
- }
- }
- }
- if (!neverFound) {
- // Sort idList
- Arrays.sort(idList);
- for (int j = 1; j <= idList.length; j++) {
- if (idList[j-1] != j) {
- tmpIdx = j;
- break;
- }
- }
- }
- }
- return prefix + tmpIdx;
- }
+ /* 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;
+ }
/*
* Update media status on PackageManager.
@@ -9661,7 +9831,7 @@ class PackageManagerService extends IPackageManager.Stub {
doGc = true;
synchronized (mInstallLock) {
final PackageParser.Package pkg = scanPackageLI(new File(codePath),
- parseFlags, 0);
+ parseFlags, 0, 0);
// Scan the package
if (pkg != null) {
synchronized (mPackages) {
@@ -9711,10 +9881,15 @@ class PackageManagerService extends IPackageManager.Stub {
if (doGc) {
Runtime.getRuntime().gc();
}
- // List stale containers.
+ // List stale containers and destroy stale temporary containers.
if (removeCids != null) {
for (String cid : removeCids) {
- Log.w(TAG, "Container " + cid + " is stale");
+ if (cid.startsWith(mTempContainerPrefix)) {
+ Log.i(TAG, "Destroying stale temporary container " + cid);
+ PackageHelper.destroySdDir(cid);
+ } else {
+ Log.w(TAG, "Container " + cid + " is stale");
+ }
}
}
}
@@ -9754,7 +9929,7 @@ class PackageManagerService extends IPackageManager.Stub {
PackageRemovedInfo outInfo = new PackageRemovedInfo();
synchronized (mInstallLock) {
boolean res = deletePackageLI(pkgName, false,
- PackageManager.DONT_DELETE_DATA, outInfo);
+ PackageManager.DONT_DELETE_DATA, outInfo, false);
if (res) {
pkgList.add(pkgName);
} else {
@@ -9763,6 +9938,13 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ synchronized (mPackages) {
+ // We didn't update the settings after removing each package;
+ // write them now for all packages.
+ mSettings.writeLP();
+ }
+
// We have to absolutely send UPDATED_MEDIA_STATUS only
// after confirming that all the receivers processed the ordered
// broadcast when packages get disabled, force a gc to clean things up.
@@ -9796,14 +9978,15 @@ class PackageManagerService extends IPackageManager.Stub {
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
} else {
// Disable moving fwd locked apps and system packages
- if (pkg.applicationInfo != null &&
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (pkg.applicationInfo != null && isSystemApp(pkg)) {
Slog.w(TAG, "Cannot move system application");
returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
- } else if (pkg.applicationInfo != null &&
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
+ } else if (pkg.applicationInfo != null && isForwardLocked(pkg)) {
Slog.w(TAG, "Cannot move forward locked app.");
returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED;
+ } else if (pkg.mOperationPending) {
+ Slog.w(TAG, "Attempt to move package which has pending operations");
+ returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING;
} else {
// Find install location first
if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 &&
@@ -9813,23 +9996,26 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ?
PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
- currFlags = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
- PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
+ currFlags = isExternal(pkg) ? PackageManager.INSTALL_EXTERNAL
+ : PackageManager.INSTALL_INTERNAL;
if (newFlags == currFlags) {
Slog.w(TAG, "No move required. Trying to move to same location");
returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
}
}
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+ pkg.mOperationPending = true;
+ }
}
}
if (returnCode != PackageManager.MOVE_SUCCEEDED) {
- processPendingMove(new MoveParams(null, observer, 0, packageName), returnCode);
+ processPendingMove(new MoveParams(null, observer, 0, packageName, null), returnCode);
} else {
Message msg = mHandler.obtainMessage(INIT_COPY);
InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
- pkg.applicationInfo.publicSourceDir);
- MoveParams mp = new MoveParams(srcArgs, observer, newFlags,
- packageName);
+ pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir);
+ MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
+ pkg.applicationInfo.dataDir);
msg.obj = mp;
mHandler.sendMessage(msg);
}
@@ -9847,7 +10033,7 @@ class PackageManagerService extends IPackageManager.Stub {
ArrayList<String> pkgList = null;
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(mp.packageName);
- if (pkg == null ) {
+ if (pkg == null) {
Slog.w(TAG, " Package " + mp.packageName +
" doesn't exist. Aborting move");
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
@@ -9870,36 +10056,58 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(mp.packageName);
// Recheck for package again.
- if (pkg == null ) {
- Slog.w(TAG, " Package " + mp.packageName +
- " doesn't exist. Aborting move");
- returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
+ if (pkg == null) {
+ Slog.w(TAG, " Package " + mp.packageName
+ + " doesn't exist. Aborting move");
+ returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
} else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
Slog.w(TAG, "Package " + mp.packageName + " code path changed from " +
mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir +
" Aborting move and returning error");
returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
} else {
- String oldCodePath = pkg.mPath;
- String newCodePath = mp.targetArgs.getCodePath();
- String newResPath = mp.targetArgs.getResourcePath();
- pkg.mPath = newCodePath;
- // Move dex files around
- if (moveDexFilesLI(pkg)
- != PackageManager.INSTALL_SUCCEEDED) {
- // Moving of dex files failed. Set
- // error code and abort move.
- pkg.mPath = pkg.mScanPath;
- returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
- } else {
+ final String oldCodePath = pkg.mPath;
+ final String newCodePath = mp.targetArgs.getCodePath();
+ final String newResPath = mp.targetArgs.getResourcePath();
+ final String newNativePath = mp.targetArgs.getNativeLibraryPath();
+
+ if ((mp.flags & PackageManager.INSTALL_EXTERNAL) == 0) {
+ if (mInstaller
+ .unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) {
+ returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ } else {
+ NativeLibraryHelper.copyNativeBinariesLI(
+ new File(newCodePath), new File(newNativePath));
+ }
+ } else {
+ if (mInstaller.linkNativeLibraryDirectory(
+ pkg.applicationInfo.dataDir, newNativePath) < 0) {
+ returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ }
+ }
+
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+ pkg.mPath = newCodePath;
+ // Move dex files around
+ if (moveDexFilesLI(pkg) != PackageManager.INSTALL_SUCCEEDED) {
+ // Moving of dex files failed. Set
+ // error code and abort move.
+ pkg.mPath = pkg.mScanPath;
+ returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ }
+ }
+
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
pkg.mScanPath = newCodePath;
pkg.applicationInfo.sourceDir = newCodePath;
pkg.applicationInfo.publicSourceDir = newResPath;
+ pkg.applicationInfo.nativeLibraryDir = newNativePath;
PackageSetting ps = (PackageSetting) pkg.mExtras;
ps.codePath = new File(pkg.applicationInfo.sourceDir);
ps.codePathString = ps.codePath.getPath();
ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir);
ps.resourcePathString = ps.resourcePath.getPath();
+ ps.nativeLibraryPathString = newNativePath;
// Set the application info flag correctly.
if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
@@ -9932,6 +10140,18 @@ class PackageManagerService extends IPackageManager.Stub {
mp.srcArgs.doPostDeleteLI(true);
}
}
+
+ // Allow more operations on this file if we didn't fail because
+ // an operation was already pending for this package.
+ if (returnCode != PackageManager.MOVE_FAILED_OPERATION_PENDING) {
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ if (pkg != null) {
+ pkg.mOperationPending = false;
+ }
+ }
+ }
+
IPackageMoveObserver observer = mp.observer;
if (observer != null) {
try {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 493a348..fadbb34 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -25,6 +25,7 @@ import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.ContentQueryMap;
import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -36,6 +37,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Environment;
@@ -50,6 +52,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings;
import android.util.EventLog;
@@ -62,6 +65,8 @@ import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE;
import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN;
+import static android.provider.Settings.System.WINDOW_ANIMATION_SCALE;
+import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -108,6 +113,9 @@ class PowerManagerService extends IPowerManager.Stub
// Cached secure settings; see updateSettingsValues()
private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;
+ // Default timeout for screen off, if not found in settings database = 15 seconds.
+ private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000;
+
// flags for setPowerState
private static final int SCREEN_ON_BIT = 0x00000001;
private static final int SCREEN_BRIGHT_BIT = 0x00000002;
@@ -135,9 +143,7 @@ class PowerManagerService extends IPowerManager.Stub
// used for noChangeLights in setPowerState()
private static final int LIGHTS_MASK = SCREEN_BRIGHT_BIT | BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT;
- static final boolean ANIMATE_SCREEN_LIGHTS = true;
- static final boolean ANIMATE_BUTTON_LIGHTS = false;
- static final boolean ANIMATE_KEYBOARD_LIGHTS = false;
+ boolean mAnimateScreenLights = true;
static final int ANIM_STEPS = 60/4;
// Slower animation for autobrightness changes
@@ -151,6 +157,7 @@ class PowerManagerService extends IPowerManager.Stub
static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;
private final int MY_UID;
+ private final int MY_PID;
private boolean mDoneBooting = false;
private boolean mBootCompleted = false;
@@ -194,15 +201,12 @@ class PowerManagerService extends IPowerManager.Stub
private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
private UnsynchronizedWakeLock mProximityPartialLock;
private HandlerThread mHandlerThread;
+ private HandlerThread mScreenOffThread;
+ private Handler mScreenOffHandler;
private Handler mHandler;
private final TimeoutTask mTimeoutTask = new TimeoutTask();
- private final LightAnimator mLightAnimator = new LightAnimator();
private final BrightnessState mScreenBrightness
= new BrightnessState(SCREEN_BRIGHT_BIT);
- private final BrightnessState mKeyboardBrightness
- = new BrightnessState(KEYBOARD_BRIGHT_BIT);
- private final BrightnessState mButtonBrightness
- = new BrightnessState(BUTTON_BRIGHT_BIT);
private boolean mStillNeedSleepNotification;
private boolean mIsPowered = false;
private IActivityManager mActivityService;
@@ -213,6 +217,7 @@ class PowerManagerService extends IPowerManager.Stub
private Sensor mLightSensor;
private boolean mLightSensorEnabled;
private float mLightSensorValue = -1;
+ private boolean mProxIgnoredBecauseScreenTurnedOff = false;
private int mHighestLightSensorValue = -1;
private float mLightSensorPendingValue = -1;
private int mLightSensorScreenBrightness = -1;
@@ -237,6 +242,14 @@ class PowerManagerService extends IPowerManager.Stub
private int[] mButtonBacklightValues;
private int[] mKeyboardBacklightValues;
private int mLightSensorWarmupTime;
+ boolean mUnplugTurnsOnScreen;
+ private int mWarningSpewThrottleCount;
+ private long mWarningSpewThrottleTime;
+ private int mAnimationSetting = ANIM_SETTING_OFF;
+
+ // Must match with the ISurfaceComposer constants in C++.
+ private static final int ANIM_SETTING_ON = 0x01;
+ private static final int ANIM_SETTING_OFF = 0x10;
// Used when logging number and duration of touch-down cycles
private long mTotalTouchDownTime;
@@ -245,8 +258,12 @@ class PowerManagerService extends IPowerManager.Stub
// could be either static or controllable at runtime
private static final boolean mSpew = false;
- private static final boolean mDebugProximitySensor = (true || mSpew);
+ private static final boolean mDebugProximitySensor = (false || mSpew);
private static final boolean mDebugLightSensor = (false || mSpew);
+
+ private native void nativeInit();
+ private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
+ private native void nativeStartSurfaceFlingerAnimation(int mode);
/*
static PrintStream mLog;
@@ -306,7 +323,7 @@ class PowerManagerService extends IPowerManager.Stub
long ident = Binder.clearCallingIdentity();
try {
PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
- MY_UID, mTag);
+ MY_UID, MY_PID, mTag, null);
mHeld = true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -353,8 +370,12 @@ class PowerManagerService extends IPowerManager.Stub
// user activity when screen was already on.
// temporarily set mUserActivityAllowed to true so this will work
// even when the keyguard is on.
+ // However, you can also set config_unplugTurnsOnScreen to have it
+ // turn on. Some devices want this because they don't have a
+ // charging LED.
synchronized (mLocks) {
- if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0) {
+ if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0 ||
+ mUnplugTurnsOnScreen) {
forceUserActivityLocked();
}
}
@@ -406,36 +427,57 @@ class PowerManagerService extends IPowerManager.Stub
}
private class SettingsObserver implements Observer {
- private int getInt(String name) {
- return mSettings.getValues(name).getAsInteger(Settings.System.VALUE);
+ private int getInt(String name, int defValue) {
+ ContentValues values = mSettings.getValues(name);
+ Integer iVal = values != null ? values.getAsInteger(Settings.System.VALUE) : null;
+ return iVal != null ? iVal : defValue;
+ }
+
+ private float getFloat(String name, float defValue) {
+ ContentValues values = mSettings.getValues(name);
+ Float fVal = values != null ? values.getAsFloat(Settings.System.VALUE) : null;
+ return fVal != null ? fVal : defValue;
}
public void update(Observable o, Object arg) {
synchronized (mLocks) {
- // STAY_ON_WHILE_PLUGGED_IN
- mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN);
+ // STAY_ON_WHILE_PLUGGED_IN, default to when plugged into AC
+ mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN,
+ BatteryManager.BATTERY_PLUGGED_AC);
updateWakeLockLocked();
- // SCREEN_OFF_TIMEOUT
- mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT);
+ // SCREEN_OFF_TIMEOUT, default to 15 seconds
+ mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT);
- // DIM_SCREEN
+ // DIM_SCREEN
//mDimScreen = getInt(DIM_SCREEN) != 0;
- // SCREEN_BRIGHTNESS_MODE
- setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE));
+ // SCREEN_BRIGHTNESS_MODE, default to manual
+ setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL));
// recalculate everything
setScreenOffTimeoutsLocked();
+
+ final float windowScale = getFloat(WINDOW_ANIMATION_SCALE, 1.0f);
+ final float transitionScale = getFloat(TRANSITION_ANIMATION_SCALE, 1.0f);
+ mAnimationSetting = 0;
+ if (windowScale > 0.5f) {
+ mAnimationSetting |= ANIM_SETTING_OFF;
+ }
+ if (transitionScale > 0.5f) {
+ // Uncomment this if you want the screen-on animation.
+ // mAnimationSetting |= ANIM_SETTING_ON;
+ }
}
}
}
- PowerManagerService()
- {
+ PowerManagerService() {
// Hack to get our uid... should have a func for this.
long token = Binder.clearCallingIdentity();
- MY_UID = Binder.getCallingUid();
+ MY_UID = Process.myUid();
+ MY_PID = Process.myPid();
Binder.restoreCallingIdentity(token);
// XXX remove this when the kernel doesn't timeout wake locks
@@ -463,6 +505,35 @@ class PowerManagerService extends IPowerManager.Stub
mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD);
mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
+ nativeInit();
+ synchronized (mLocks) {
+ updateNativePowerStateLocked();
+ }
+
+ mInitComplete = false;
+ mScreenOffThread = new HandlerThread("PowerManagerService.mScreenOffThread") {
+ @Override
+ protected void onLooperPrepared() {
+ mScreenOffHandler = new Handler();
+ synchronized (mScreenOffThread) {
+ mInitComplete = true;
+ mScreenOffThread.notifyAll();
+ }
+ }
+ };
+ mScreenOffThread.start();
+
+ synchronized (mScreenOffThread) {
+ while (!mInitComplete) {
+ try {
+ mScreenOffThread.wait();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+
+ mInitComplete = false;
mHandlerThread = new HandlerThread("PowerManagerService") {
@Override
protected void onLooperPrepared() {
@@ -481,6 +552,11 @@ class PowerManagerService extends IPowerManager.Stub
}
}
}
+
+ nativeInit();
+ synchronized (mLocks) {
+ updateNativePowerStateLocked();
+ }
}
void initInThread() {
@@ -504,6 +580,12 @@ class PowerManagerService extends IPowerManager.Stub
Resources resources = mContext.getResources();
+ mAnimateScreenLights = resources.getBoolean(
+ com.android.internal.R.bool.config_animateScreenLights);
+
+ mUnplugTurnsOnScreen = resources.getBoolean(
+ com.android.internal.R.bool.config_unplugTurnsOnScreen);
+
// read settings for auto-brightness
mUseSoftwareAutoBrightness = resources.getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
@@ -525,9 +607,11 @@ class PowerManagerService extends IPowerManager.Stub
"(" + Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ + Settings.System.NAME + "=?) or ("
+ + Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?)",
new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN,
- SCREEN_BRIGHTNESS_MODE},
+ SCREEN_BRIGHTNESS_MODE, WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE},
null);
mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler);
SettingsObserver settingsObserver = new SettingsObserver();
@@ -565,13 +649,13 @@ class PowerManagerService extends IPowerManager.Stub
private class WakeLock implements IBinder.DeathRecipient
{
- WakeLock(int f, IBinder b, String t, int u) {
+ WakeLock(int f, IBinder b, String t, int u, int p) {
super();
flags = f;
binder = b;
tag = t;
uid = u == MY_UID ? Process.SYSTEM_UID : u;
- pid = Binder.getCallingPid();
+ pid = p;
if (u != MY_UID || (
!"KEEP_SCREEN_ON_FLAG".equals(tag)
&& !"KeyInputQueue".equals(tag))) {
@@ -598,6 +682,7 @@ class PowerManagerService extends IPowerManager.Stub
final int uid;
final int pid;
final int monitorType;
+ WorkSource ws;
boolean activated = true;
int minState;
}
@@ -618,38 +703,90 @@ class PowerManagerService extends IPowerManager.Stub
int n = flags & LOCK_MASK;
return n == PowerManager.FULL_WAKE_LOCK
|| n == PowerManager.SCREEN_BRIGHT_WAKE_LOCK
- || n == PowerManager.SCREEN_DIM_WAKE_LOCK;
+ || n == PowerManager.SCREEN_DIM_WAKE_LOCK
+ || n == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
+ }
+
+ void enforceWakeSourcePermission(int uid, int pid) {
+ if (uid == Process.myUid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+ pid, uid, null);
}
- public void acquireWakeLock(int flags, IBinder lock, String tag) {
+ public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) {
int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
}
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
long ident = Binder.clearCallingIdentity();
try {
synchronized (mLocks) {
- acquireWakeLockLocked(flags, lock, uid, tag);
+ acquireWakeLockLocked(flags, lock, uid, pid, tag, ws);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) {
- int acquireUid = -1;
- String acquireName = null;
- int acquireType = -1;
+ void noteStartWakeLocked(WakeLock wl, WorkSource ws) {
+ if (wl.monitorType >= 0) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ if (ws != null) {
+ mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag,
+ wl.monitorType);
+ } else {
+ mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
+ }
+ } catch (RemoteException e) {
+ // Ignore
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ void noteStopWakeLocked(WakeLock wl, WorkSource ws) {
+ if (wl.monitorType >= 0) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ if (ws != null) {
+ mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag,
+ wl.monitorType);
+ } else {
+ mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
+ }
+ } catch (RemoteException e) {
+ // Ignore
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+ public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag,
+ WorkSource ws) {
if (mSpew) {
Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag);
}
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+
int index = mLocks.getIndex(lock);
WakeLock wl;
boolean newlock;
+ boolean diffsource;
+ WorkSource oldsource;
if (index < 0) {
- wl = new WakeLock(flags, lock, tag, uid);
+ wl = new WakeLock(flags, lock, tag, uid, pid);
switch (wl.flags & LOCK_MASK)
{
case PowerManager.FULL_WAKE_LOCK:
@@ -676,35 +813,64 @@ class PowerManagerService extends IPowerManager.Stub
return;
}
mLocks.addLock(wl);
+ if (ws != null) {
+ wl.ws = new WorkSource(ws);
+ }
newlock = true;
+ diffsource = false;
+ oldsource = null;
} else {
wl = mLocks.get(index);
newlock = false;
+ oldsource = wl.ws;
+ if (oldsource != null) {
+ if (ws == null) {
+ wl.ws = null;
+ diffsource = true;
+ } else {
+ diffsource = oldsource.diff(ws);
+ }
+ } else if (ws != null) {
+ diffsource = true;
+ } else {
+ diffsource = false;
+ }
+ if (diffsource) {
+ wl.ws = new WorkSource(ws);
+ }
}
if (isScreenLock(flags)) {
// if this causes a wakeup, we reactivate all of the locks and
// set it to whatever they want. otherwise, we modulate that
// by the current state so we never turn it more on than
// it already is.
- if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
- int oldWakeLockState = mWakeLockState;
- mWakeLockState = mLocks.reactivateScreenLocksLocked();
- if (mSpew) {
- Slog.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
- + " mWakeLockState=0x"
- + Integer.toHexString(mWakeLockState)
- + " previous wakeLockState=0x" + Integer.toHexString(oldWakeLockState));
+ if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
+ mProximityWakeLockCount++;
+ if (mProximityWakeLockCount == 1) {
+ enableProximityLockLocked();
}
} else {
- if (mSpew) {
- Slog.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState)
- + " mLocks.gatherState()=0x"
- + Integer.toHexString(mLocks.gatherState())
- + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
+ if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
+ int oldWakeLockState = mWakeLockState;
+ mWakeLockState = mLocks.reactivateScreenLocksLocked();
+ if (mSpew) {
+ Slog.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
+ + " mWakeLockState=0x"
+ + Integer.toHexString(mWakeLockState)
+ + " previous wakeLockState=0x"
+ + Integer.toHexString(oldWakeLockState));
+ }
+ } else {
+ if (mSpew) {
+ Slog.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState)
+ + " mLocks.gatherState()=0x"
+ + Integer.toHexString(mLocks.gatherState())
+ + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
+ }
+ mWakeLockState = (mUserState | mWakeLockState) & mLocks.gatherState();
}
- mWakeLockState = (mUserState | mWakeLockState) & mLocks.gatherState();
+ setPowerState(mWakeLockState | mUserState);
}
- setPowerState(mWakeLockState | mUserState);
}
else if ((flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) {
if (newlock) {
@@ -714,24 +880,37 @@ class PowerManagerService extends IPowerManager.Stub
}
}
Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);
- } else if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
- mProximityWakeLockCount++;
- if (mProximityWakeLockCount == 1) {
- enableProximityLockLocked();
- }
}
- if (newlock) {
- acquireUid = wl.uid;
- acquireName = wl.tag;
- acquireType = wl.monitorType;
+
+ if (diffsource) {
+ // If the lock sources have changed, need to first release the
+ // old ones.
+ noteStopWakeLocked(wl, oldsource);
+ }
+ if (newlock || diffsource) {
+ noteStartWakeLocked(wl, ws);
}
+ }
- if (acquireType >= 0) {
- try {
- mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType);
- } catch (RemoteException e) {
- // Ignore
+ public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
+ synchronized (mLocks) {
+ int index = mLocks.getIndex(lock);
+ if (index < 0) {
+ throw new IllegalArgumentException("Wake lock not active");
}
+ WakeLock wl = mLocks.get(index);
+ WorkSource oldsource = wl.ws;
+ wl.ws = ws != null ? new WorkSource(ws) : null;
+ noteStopWakeLocked(wl, oldsource);
+ noteStartWakeLocked(wl, ws);
}
}
@@ -747,10 +926,6 @@ class PowerManagerService extends IPowerManager.Stub
}
private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
- int releaseUid;
- String releaseName;
- int releaseType;
-
WakeLock wl = mLocks.removeLock(lock);
if (wl == null) {
return;
@@ -762,12 +937,27 @@ class PowerManagerService extends IPowerManager.Stub
}
if (isScreenLock(wl.flags)) {
- mWakeLockState = mLocks.gatherState();
- // goes in the middle to reduce flicker
- if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) {
- userActivity(SystemClock.uptimeMillis(), false);
+ if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
+ mProximityWakeLockCount--;
+ if (mProximityWakeLockCount == 0) {
+ if (mProximitySensorActive &&
+ ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0)) {
+ // wait for proximity sensor to go negative before disabling sensor
+ if (mDebugProximitySensor) {
+ Slog.d(TAG, "waiting for proximity sensor to go negative");
+ }
+ } else {
+ disableProximityLockLocked();
+ }
+ }
+ } else {
+ mWakeLockState = mLocks.gatherState();
+ // goes in the middle to reduce flicker
+ if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) {
+ userActivity(SystemClock.uptimeMillis(), -1, false, OTHER_EVENT, false);
+ }
+ setPowerState(mWakeLockState | mUserState);
}
- setPowerState(mWakeLockState | mUserState);
}
else if ((wl.flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) {
mPartialCount--;
@@ -775,36 +965,11 @@ class PowerManagerService extends IPowerManager.Stub
if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 0, wl.tag);
Power.releaseWakeLock(PARTIAL_NAME);
}
- } else if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
- mProximityWakeLockCount--;
- if (mProximityWakeLockCount == 0) {
- if (mProximitySensorActive &&
- ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0)) {
- // wait for proximity sensor to go negative before disabling sensor
- if (mDebugProximitySensor) {
- Slog.d(TAG, "waiting for proximity sensor to go negative");
- }
- } else {
- disableProximityLockLocked();
- }
- }
}
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
- releaseUid = wl.uid;
- releaseName = wl.tag;
- releaseType = wl.monitorType;
- if (releaseType >= 0) {
- long origId = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType);
- } catch (RemoteException e) {
- // Ignore
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
+ noteStopWakeLocked(wl, wl.ws);
}
private class PokeLock implements IBinder.DeathRecipient
@@ -982,8 +1147,6 @@ class PowerManagerService extends IPowerManager.Stub
pw.println(" mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness);
pw.println(" mAutoBrightessEnabled=" + mAutoBrightessEnabled);
mScreenBrightness.dump(pw, " mScreenBrightness: ");
- mKeyboardBrightness.dump(pw, " mKeyboardBrightness: ");
- mButtonBrightness.dump(pw, " mButtonBrightness: ");
int N = mLocks.size();
pw.println();
@@ -1022,36 +1185,76 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- private void setTimeoutLocked(long now, int nextState)
- {
+ private void setTimeoutLocked(long now, int nextState) {
+ setTimeoutLocked(now, -1, nextState);
+ }
+
+ // If they gave a timeoutOverride it is the number of seconds
+ // to screen-off. Figure out where in the countdown cycle we
+ // should jump to.
+ private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) {
+ long timeoutOverride = originalTimeoutOverride;
if (mBootCompleted) {
- mHandler.removeCallbacks(mTimeoutTask);
- mTimeoutTask.nextState = nextState;
- long when = now;
- switch (nextState)
- {
- case SCREEN_BRIGHT:
- when += mKeylightDelay;
- break;
- case SCREEN_DIM:
- if (mDimDelay >= 0) {
- when += mDimDelay;
- break;
- } else {
- Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ synchronized (mLocks) {
+ long when = 0;
+ if (timeoutOverride <= 0) {
+ switch (nextState)
+ {
+ case SCREEN_BRIGHT:
+ when = now + mKeylightDelay;
+ break;
+ case SCREEN_DIM:
+ if (mDimDelay >= 0) {
+ when = now + mDimDelay;
+ break;
+ } else {
+ Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ }
+ case SCREEN_OFF:
+ synchronized (mLocks) {
+ when = now + mScreenOffDelay;
+ }
+ break;
+ default:
+ when = now;
+ break;
}
- case SCREEN_OFF:
- synchronized (mLocks) {
- when += mScreenOffDelay;
+ } else {
+ override: {
+ if (timeoutOverride <= mScreenOffDelay) {
+ when = now + timeoutOverride;
+ nextState = SCREEN_OFF;
+ break override;
+ }
+ timeoutOverride -= mScreenOffDelay;
+
+ if (mDimDelay >= 0) {
+ if (timeoutOverride <= mDimDelay) {
+ when = now + timeoutOverride;
+ nextState = SCREEN_DIM;
+ break override;
+ }
+ timeoutOverride -= mDimDelay;
+ }
+
+ when = now + timeoutOverride;
+ nextState = SCREEN_BRIGHT;
}
- break;
- }
- if (mSpew) {
- Slog.d(TAG, "setTimeoutLocked now=" + now + " nextState=" + nextState
- + " when=" + when);
+ }
+ if (mSpew) {
+ Slog.d(TAG, "setTimeoutLocked now=" + now
+ + " timeoutOverride=" + timeoutOverride
+ + " nextState=" + nextState + " when=" + when);
+ }
+
+ mHandler.removeCallbacks(mTimeoutTask);
+ mTimeoutTask.nextState = nextState;
+ mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0
+ ? (originalTimeoutOverride - timeoutOverride)
+ : -1;
+ mHandler.postAtTime(mTimeoutTask, when);
+ mNextTimeout = when; // for debugging
}
- mHandler.postAtTime(mTimeoutTask, when);
- mNextTimeout = when; // for debugging
}
}
@@ -1064,6 +1267,7 @@ class PowerManagerService extends IPowerManager.Stub
private class TimeoutTask implements Runnable
{
int nextState; // access should be synchronized on mLocks
+ long remainingTimeoutOverride;
public void run()
{
synchronized (mLocks) {
@@ -1084,11 +1288,11 @@ class PowerManagerService extends IPowerManager.Stub
{
case SCREEN_BRIGHT:
if (mDimDelay >= 0) {
- setTimeoutLocked(now, SCREEN_DIM);
+ setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM);
break;
}
case SCREEN_DIM:
- setTimeoutLocked(now, SCREEN_OFF);
+ setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF);
break;
}
}
@@ -1557,14 +1761,23 @@ class PowerManagerService extends IPowerManager.Stub
}
}
}
+
+ updateNativePowerStateLocked();
}
}
+
+ private void updateNativePowerStateLocked() {
+ nativeSetPowerState(
+ (mPowerState & SCREEN_ON_BIT) != 0,
+ (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
+ }
private int screenOffFinishedAnimatingLocked(int reason) {
// I don't think we need to check the current state here because all of these
// Power.setScreenState and sendNotificationLocked can both handle being
// called multiple times in the same state. -joeo
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, mTotalTouchDownTime, mTouchCycles);
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, mTotalTouchDownTime,
+ mTouchCycles);
mLastTouchDown = 0;
int err = setScreenStateLocked(false);
if (err == 0) {
@@ -1581,8 +1794,12 @@ class PowerManagerService extends IPowerManager.Stub
private void updateLightsLocked(int newState, int forceState) {
final int oldState = mPowerState;
- newState = applyButtonState(newState);
- newState = applyKeyboardState(newState);
+ if ((newState & SCREEN_ON_BIT) != 0) {
+ // Only turn on the buttons or keyboard if the screen is also on.
+ // We should never see the buttons on but not the screen.
+ newState = applyButtonState(newState);
+ newState = applyKeyboardState(newState);
+ }
final int realDifference = (newState ^ oldState);
final int difference = realDifference | forceState;
if (difference == 0) {
@@ -1594,145 +1811,105 @@ class PowerManagerService extends IPowerManager.Stub
int onMask = 0;
int preferredBrightness = getPreferredBrightness();
- boolean startAnimation = false;
if ((difference & KEYBOARD_BRIGHT_BIT) != 0) {
- if (ANIMATE_KEYBOARD_LIGHTS) {
- if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
- mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
- ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS,
- Power.BRIGHTNESS_ON);
- } else {
- mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_ON,
- ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS,
- Power.BRIGHTNESS_OFF);
- }
- startAnimation = true;
+ if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
+ offMask |= KEYBOARD_BRIGHT_BIT;
} else {
- if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
- offMask |= KEYBOARD_BRIGHT_BIT;
- } else {
- onMask |= KEYBOARD_BRIGHT_BIT;
- }
+ onMask |= KEYBOARD_BRIGHT_BIT;
}
}
if ((difference & BUTTON_BRIGHT_BIT) != 0) {
- if (ANIMATE_BUTTON_LIGHTS) {
- if ((newState & BUTTON_BRIGHT_BIT) == 0) {
- mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
- ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
- Power.BRIGHTNESS_ON);
- } else {
- mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_ON,
- ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
- Power.BRIGHTNESS_OFF);
- }
- startAnimation = true;
+ if ((newState & BUTTON_BRIGHT_BIT) == 0) {
+ offMask |= BUTTON_BRIGHT_BIT;
} else {
- if ((newState & BUTTON_BRIGHT_BIT) == 0) {
- offMask |= BUTTON_BRIGHT_BIT;
- } else {
- onMask |= BUTTON_BRIGHT_BIT;
- }
+ onMask |= BUTTON_BRIGHT_BIT;
}
}
if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
- if (ANIMATE_SCREEN_LIGHTS) {
- int nominalCurrentValue = -1;
- // If there was an actual difference in the light state, then
- // figure out the "ideal" current value based on the previous
- // state. Otherwise, this is a change due to the brightness
- // override, so we want to animate from whatever the current
- // value is.
- if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
- switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) {
- case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT:
- nominalCurrentValue = preferredBrightness;
- break;
- case SCREEN_ON_BIT:
- nominalCurrentValue = Power.BRIGHTNESS_DIM;
- break;
- case 0:
- nominalCurrentValue = Power.BRIGHTNESS_OFF;
- break;
- case SCREEN_BRIGHT_BIT:
- default:
- // not possible
- nominalCurrentValue = (int)mScreenBrightness.curValue;
- break;
- }
+ int nominalCurrentValue = -1;
+ // If there was an actual difference in the light state, then
+ // figure out the "ideal" current value based on the previous
+ // state. Otherwise, this is a change due to the brightness
+ // override, so we want to animate from whatever the current
+ // value is.
+ if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
+ switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) {
+ case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT:
+ nominalCurrentValue = preferredBrightness;
+ break;
+ case SCREEN_ON_BIT:
+ nominalCurrentValue = Power.BRIGHTNESS_DIM;
+ break;
+ case 0:
+ nominalCurrentValue = Power.BRIGHTNESS_OFF;
+ break;
+ case SCREEN_BRIGHT_BIT:
+ default:
+ // not possible
+ nominalCurrentValue = (int)mScreenBrightness.curValue;
+ break;
}
- int brightness = preferredBrightness;
- int steps = ANIM_STEPS;
- if ((newState & SCREEN_BRIGHT_BIT) == 0) {
- // dim or turn off backlight, depending on if the screen is on
- // the scale is because the brightness ramp isn't linear and this biases
- // it so the later parts take longer.
- final float scale = 1.5f;
- float ratio = (((float)Power.BRIGHTNESS_DIM)/preferredBrightness);
- if (ratio > 1.0f) ratio = 1.0f;
- if ((newState & SCREEN_ON_BIT) == 0) {
- if ((oldState & SCREEN_BRIGHT_BIT) != 0) {
- // was bright
- steps = ANIM_STEPS;
- } else {
- // was dim
- steps = (int)(ANIM_STEPS*ratio*scale);
- }
- brightness = Power.BRIGHTNESS_OFF;
+ }
+ int brightness = preferredBrightness;
+ int steps = ANIM_STEPS;
+ if ((newState & SCREEN_BRIGHT_BIT) == 0) {
+ // dim or turn off backlight, depending on if the screen is on
+ // the scale is because the brightness ramp isn't linear and this biases
+ // it so the later parts take longer.
+ final float scale = 1.5f;
+ float ratio = (((float)Power.BRIGHTNESS_DIM)/preferredBrightness);
+ if (ratio > 1.0f) ratio = 1.0f;
+ if ((newState & SCREEN_ON_BIT) == 0) {
+ if ((oldState & SCREEN_BRIGHT_BIT) != 0) {
+ // was bright
+ steps = ANIM_STEPS;
} else {
- if ((oldState & SCREEN_ON_BIT) != 0) {
- // was bright
- steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale);
- } else {
- // was dim
- steps = (int)(ANIM_STEPS*ratio);
- }
- if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) {
- // If the "stay on while plugged in" option is
- // turned on, then the screen will often not
- // automatically turn off while plugged in. To
- // still have a sense of when it is inactive, we
- // will then count going dim as turning off.
- mScreenOffTime = SystemClock.elapsedRealtime();
- }
- brightness = Power.BRIGHTNESS_DIM;
+ // was dim
+ steps = (int)(ANIM_STEPS*ratio*scale);
}
- }
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(brightness);
- } catch (RemoteException e) {
- // Nothing interesting to do.
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- if (mScreenBrightness.setTargetLocked(brightness,
- steps, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue)) {
- startAnimation = true;
- }
- } else {
- if ((newState & SCREEN_BRIGHT_BIT) == 0) {
- // dim or turn off backlight, depending on if the screen is on
- if ((newState & SCREEN_ON_BIT) == 0) {
- offMask |= SCREEN_BRIGHT_BIT;
+ brightness = Power.BRIGHTNESS_OFF;
+ } else {
+ if ((oldState & SCREEN_ON_BIT) != 0) {
+ // was bright
+ steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale);
} else {
- dimMask |= SCREEN_BRIGHT_BIT;
+ // was dim
+ steps = (int)(ANIM_STEPS*ratio);
}
- } else {
- onMask |= SCREEN_BRIGHT_BIT;
+ if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) {
+ // If the "stay on while plugged in" option is
+ // turned on, then the screen will often not
+ // automatically turn off while plugged in. To
+ // still have a sense of when it is inactive, we
+ // will then count going dim as turning off.
+ mScreenOffTime = SystemClock.elapsedRealtime();
+ }
+ brightness = Power.BRIGHTNESS_DIM;
}
}
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteScreenBrightness(brightness);
+ } catch (RemoteException e) {
+ // Nothing interesting to do.
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ mScreenBrightness.setTargetLocked(brightness, steps,
+ INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue);
}
- if (startAnimation) {
- if (mSpew) {
- Slog.i(TAG, "Scheduling light animator!");
- }
- mHandler.removeCallbacks(mLightAnimator);
- mHandler.post(mLightAnimator);
+ if (mSpew) {
+ Slog.d(TAG, "offMask=0x" + Integer.toHexString(offMask)
+ + " dimMask=0x" + Integer.toHexString(dimMask)
+ + " onMask=0x" + Integer.toHexString(onMask)
+ + " difference=0x" + Integer.toHexString(difference)
+ + " realDifference=0x" + Integer.toHexString(realDifference)
+ + " forceState=0x" + Integer.toHexString(forceState)
+ );
}
if (offMask != 0) {
@@ -1774,7 +1951,7 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- class BrightnessState {
+ class BrightnessState implements Runnable {
final int mask;
boolean initialized;
@@ -1794,13 +1971,13 @@ class PowerManagerService extends IPowerManager.Stub
+ " delta=" + delta);
}
- boolean setTargetLocked(int target, int stepsToTarget, int initialValue,
+ void setTargetLocked(int target, int stepsToTarget, int initialValue,
int nominalCurrentValue) {
if (!initialized) {
initialized = true;
curValue = (float)initialValue;
} else if (targetValue == target) {
- return false;
+ return;
}
targetValue = target;
delta = (targetValue -
@@ -1808,13 +1985,18 @@ class PowerManagerService extends IPowerManager.Stub
/ stepsToTarget;
if (mSpew) {
String noticeMe = nominalCurrentValue == curValue ? "" : " ******************";
- Slog.i(TAG, "Setting target " + mask + ": cur=" + curValue
- + " target=" + targetValue + " delta=" + delta
+ Slog.i(TAG, "setTargetLocked mask=" + mask + " curValue=" + curValue
+ + " target=" + target + " targetValue=" + targetValue + " delta=" + delta
+ " nominalCurrentValue=" + nominalCurrentValue
+ noticeMe);
}
animating = true;
- return true;
+
+ if (mSpew) {
+ Slog.i(TAG, "scheduling light animator");
+ }
+ mScreenOffHandler.removeCallbacks(this);
+ mScreenOffHandler.post(this);
}
boolean stepLocked() {
@@ -1840,31 +2022,51 @@ class PowerManagerService extends IPowerManager.Stub
more = false;
}
}
- //Slog.i(TAG, "Animating brightess " + curIntValue + ": " + mask);
+ if (mSpew) Slog.d(TAG, "Animating curIntValue=" + curIntValue + ": " + mask);
setLightBrightness(mask, curIntValue);
+ finishAnimationLocked(more, curIntValue);
+ return more;
+ }
+
+ void jumpToTargetLocked() {
+ if (mSpew) Slog.d(TAG, "jumpToTargetLocked targetValue=" + targetValue + ": " + mask);
+ setLightBrightness(mask, targetValue);
+ final int tv = targetValue;
+ curValue = tv;
+ targetValue = -1;
+ finishAnimationLocked(false, tv);
+ }
+
+ private void finishAnimationLocked(boolean more, int curIntValue) {
animating = more;
if (!more) {
if (mask == SCREEN_BRIGHT_BIT && curIntValue == Power.BRIGHTNESS_OFF) {
screenOffFinishedAnimatingLocked(mScreenOffReason);
}
}
- return more;
}
- }
- private class LightAnimator implements Runnable {
public void run() {
- synchronized (mLocks) {
- long now = SystemClock.uptimeMillis();
- boolean more = mScreenBrightness.stepLocked();
- if (mKeyboardBrightness.stepLocked()) {
- more = true;
- }
- if (mButtonBrightness.stepLocked()) {
- more = true;
+ if (mAnimateScreenLights) {
+ synchronized (mLocks) {
+ long now = SystemClock.uptimeMillis();
+ boolean more = mScreenBrightness.stepLocked();
+ if (more) {
+ mScreenOffHandler.postAtTime(this, now+(1000/60));
+ }
}
- if (more) {
- mHandler.postAtTime(mLightAnimator, now+(1000/60));
+ } else {
+ synchronized (mLocks) {
+ // we're turning off
+ final boolean animate = animating && targetValue == Power.BRIGHTNESS_OFF;
+ if (animate) {
+ // It's pretty scary to hold mLocks for this long, and we should
+ // redesign this, but it works for now.
+ nativeStartSurfaceFlingerAnimation(
+ mScreenOffReason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR
+ ? 0 : mAnimationSetting);
+ }
+ mScreenBrightness.jumpToTargetLocked();
}
}
}
@@ -1945,6 +2147,21 @@ class PowerManagerService extends IPowerManager.Stub
return (mScreenBrightness.animating && mScreenBrightness.targetValue == 0);
}
+ private boolean shouldLog(long time) {
+ synchronized (mLocks) {
+ if (time > (mWarningSpewThrottleTime + (60*60*1000))) {
+ mWarningSpewThrottleTime = time;
+ mWarningSpewThrottleCount = 0;
+ return true;
+ } else if (mWarningSpewThrottleCount < 30) {
+ mWarningSpewThrottleCount++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
private void forceUserActivityLocked() {
if (isScreenTurningOffLocked()) {
// cancel animation so userActivity will succeed
@@ -1958,19 +2175,42 @@ class PowerManagerService extends IPowerManager.Stub
public void userActivityWithForce(long time, boolean noChangeLights, boolean force) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- userActivity(time, noChangeLights, OTHER_EVENT, force);
+ userActivity(time, -1, noChangeLights, OTHER_EVENT, force);
}
public void userActivity(long time, boolean noChangeLights) {
- userActivity(time, noChangeLights, OTHER_EVENT, false);
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (shouldLog(time)) {
+ Slog.w(TAG, "Caller does not have DEVICE_POWER permission. pid="
+ + Binder.getCallingPid() + " uid=" + Binder.getCallingUid());
+ }
+ return;
+ }
+
+ userActivity(time, -1, noChangeLights, OTHER_EVENT, false);
}
public void userActivity(long time, boolean noChangeLights, int eventType) {
- userActivity(time, noChangeLights, eventType, false);
+ userActivity(time, -1, noChangeLights, eventType, false);
}
public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) {
- //mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ userActivity(time, -1, noChangeLights, eventType, force);
+ }
+
+ /*
+ * Reset the user activity timeout to now + timeout. This overrides whatever else is going
+ * on with user activity. Don't use this function.
+ */
+ public void clearUserActivityTimeout(long now, long timeout) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now");
+ userActivity(now, timeout, false, OTHER_EVENT, false);
+ }
+
+ private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
+ int eventType, boolean force) {
if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)
&& (eventType == CHEEK_EVENT || eventType == TOUCH_EVENT)) {
@@ -2004,6 +2244,7 @@ class PowerManagerService extends IPowerManager.Stub
+ " mUserState=0x" + Integer.toHexString(mUserState)
+ " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
+ " mProximitySensorActive=" + mProximitySensorActive
+ + " timeoutOverride=" + timeoutOverride
+ " force=" + force);
}
// ignore user activity if we are in the process of turning off the screen
@@ -2041,7 +2282,7 @@ class PowerManagerService extends IPowerManager.Stub
mWakeLockState = mLocks.reactivateScreenLocksLocked();
setPowerState(mUserState | mWakeLockState, noChangeLights,
WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- setTimeoutLocked(time, SCREEN_BRIGHT);
+ setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);
}
}
}
@@ -2144,49 +2385,15 @@ class PowerManagerService extends IPowerManager.Stub
Slog.d(TAG, "keyboardValue " + keyboardValue);
}
- boolean startAnimation = false;
if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) {
- if (ANIMATE_SCREEN_LIGHTS) {
- if (mScreenBrightness.setTargetLocked(lcdValue,
- AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_SCREEN_BRIGHTNESS,
- (int)mScreenBrightness.curValue)) {
- startAnimation = true;
- }
- } else {
- int brightnessMode = (mAutoBrightessEnabled
- ? LightsService.BRIGHTNESS_MODE_SENSOR
- : LightsService.BRIGHTNESS_MODE_USER);
- mLcdLight.setBrightness(lcdValue, brightnessMode);
- }
+ mScreenBrightness.setTargetLocked(lcdValue, AUTOBRIGHTNESS_ANIM_STEPS,
+ INITIAL_SCREEN_BRIGHTNESS, (int)mScreenBrightness.curValue);
}
if (mButtonBrightnessOverride < 0) {
- if (ANIMATE_BUTTON_LIGHTS) {
- if (mButtonBrightness.setTargetLocked(buttonValue,
- AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
- (int)mButtonBrightness.curValue)) {
- startAnimation = true;
- }
- } else {
- mButtonLight.setBrightness(buttonValue);
- }
+ mButtonLight.setBrightness(buttonValue);
}
if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) {
- if (ANIMATE_KEYBOARD_LIGHTS) {
- if (mKeyboardBrightness.setTargetLocked(keyboardValue,
- AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
- (int)mKeyboardBrightness.curValue)) {
- startAnimation = true;
- }
- } else {
- mKeyboardLight.setBrightness(keyboardValue);
- }
- }
- if (startAnimation) {
- if (mDebugLightSensor) {
- Slog.i(TAG, "lightSensorChangedLocked scheduling light animator");
- }
- mHandler.removeCallbacks(mLightAnimator);
- mHandler.post(mLightAnimator);
+ mKeyboardLight.setBrightness(keyboardValue);
}
}
}
@@ -2274,11 +2481,23 @@ class PowerManagerService extends IPowerManager.Stub
mWakeLockState = SCREEN_OFF;
int N = mLocks.size();
int numCleared = 0;
+ boolean proxLock = false;
for (int i=0; i<N; i++) {
WakeLock wl = mLocks.get(i);
if (isScreenLock(wl.flags)) {
- mLocks.get(i).activated = false;
- numCleared++;
+ if (((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)
+ && reason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
+ proxLock = true;
+ } else {
+ mLocks.get(i).activated = false;
+ numCleared++;
+ }
+ }
+ }
+ if (!proxLock) {
+ mProxIgnoredBecauseScreenTurnedOff = true;
+ if (mDebugProximitySensor) {
+ Slog.d(TAG, "setting mProxIgnoredBecauseScreenTurnedOff");
}
}
EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numCleared);
@@ -2469,6 +2688,11 @@ class PowerManagerService extends IPowerManager.Stub
result |= wl.minState;
}
}
+ if (mDebugProximitySensor) {
+ Slog.d(TAG, "reactivateScreenLocksLocked mProxIgnoredBecauseScreenTurnedOff="
+ + mProxIgnoredBecauseScreenTurnedOff);
+ }
+ mProxIgnoredBecauseScreenTurnedOff = false;
return result;
}
}
@@ -2537,6 +2761,7 @@ class PowerManagerService extends IPowerManager.Stub
}
}
+ // for watchdog
public void monitor() {
synchronized (mLocks) { }
}
@@ -2556,34 +2781,25 @@ class PowerManagerService extends IPowerManager.Stub
public void setBacklightBrightness(int brightness) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
// Don't let applications turn the screen all the way off
- brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
- mLcdLight.setBrightness(brightness);
- mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0);
- mButtonLight.setBrightness(brightness);
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(brightness);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ synchronized (mLocks) {
+ brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
+ mLcdLight.setBrightness(brightness);
+ mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0);
+ mButtonLight.setBrightness(brightness);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteScreenBrightness(brightness);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
- // update our animation state
- if (ANIMATE_SCREEN_LIGHTS) {
- mScreenBrightness.curValue = brightness;
- mScreenBrightness.animating = false;
- mScreenBrightness.targetValue = -1;
- }
- if (ANIMATE_KEYBOARD_LIGHTS) {
- mKeyboardBrightness.curValue = brightness;
- mKeyboardBrightness.animating = false;
- mKeyboardBrightness.targetValue = -1;
- }
- if (ANIMATE_BUTTON_LIGHTS) {
- mButtonBrightness.curValue = brightness;
- mButtonBrightness.animating = false;
- mButtonBrightness.targetValue = -1;
+ // update our animation state
+ synchronized (mLocks) {
+ mScreenBrightness.targetValue = brightness;
+ mScreenBrightness.jumpToTargetLocked();
+ }
}
}
@@ -2628,7 +2844,13 @@ class PowerManagerService extends IPowerManager.Stub
}
if (mProximitySensorActive) {
mProximitySensorActive = false;
- forceUserActivityLocked();
+ if (mDebugProximitySensor) {
+ Slog.d(TAG, "disableProximityLockLocked mProxIgnoredBecauseScreenTurnedOff="
+ + mProxIgnoredBecauseScreenTurnedOff);
+ }
+ if (!mProxIgnoredBecauseScreenTurnedOff) {
+ forceUserActivityLocked();
+ }
}
}
}
@@ -2642,15 +2864,27 @@ class PowerManagerService extends IPowerManager.Stub
return;
}
if (active) {
- goToSleepLocked(SystemClock.uptimeMillis(),
- WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR);
+ if (mDebugProximitySensor) {
+ Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff="
+ + mProxIgnoredBecauseScreenTurnedOff);
+ }
+ if (!mProxIgnoredBecauseScreenTurnedOff) {
+ goToSleepLocked(SystemClock.uptimeMillis(),
+ WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR);
+ }
mProximitySensorActive = true;
} else {
// proximity sensor negative events trigger as user activity.
// temporarily set mUserActivityAllowed to true so this will work
// even when the keyguard is on.
mProximitySensorActive = false;
- forceUserActivityLocked();
+ if (mDebugProximitySensor) {
+ Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff="
+ + mProxIgnoredBecauseScreenTurnedOff);
+ }
+ if (!mProxIgnoredBecauseScreenTurnedOff) {
+ forceUserActivityLocked();
+ }
if (mProximityWakeLockCount == 0) {
// disable sensor if we have no listeners left after proximity negative
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 4fa89d9..43dbcc0 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -39,7 +39,7 @@ public class ProcessStats {
private static final int[] PROCESS_STATS_FORMAT = new int[] {
PROC_SPACE_TERM,
- PROC_SPACE_TERM,
+ PROC_SPACE_TERM|PROC_PARENS,
PROC_SPACE_TERM,
PROC_SPACE_TERM,
PROC_SPACE_TERM,
@@ -75,16 +75,29 @@ public class ProcessStats {
PROC_SPACE_TERM,
PROC_SPACE_TERM,
PROC_SPACE_TERM,
+ PROC_SPACE_TERM|PROC_OUT_LONG, // 9: minor faults
PROC_SPACE_TERM,
+ PROC_SPACE_TERM|PROC_OUT_LONG, // 11: major faults
PROC_SPACE_TERM,
+ PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime
+ PROC_SPACE_TERM|PROC_OUT_LONG, // 14: stime
PROC_SPACE_TERM,
PROC_SPACE_TERM,
- PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime
- PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM|PROC_OUT_LONG, // 21: vsize
};
- private final String[] mProcessFullStatsStringData = new String[3];
- private final long[] mProcessFullStatsData = new long[3];
+ static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1;
+ static final int PROCESS_FULL_STAT_MAJOR_FAULTS = 2;
+ static final int PROCESS_FULL_STAT_UTIME = 3;
+ static final int PROCESS_FULL_STAT_STIME = 4;
+ static final int PROCESS_FULL_STAT_VSIZE = 5;
+
+ private final String[] mProcessFullStatsStringData = new String[6];
+ private final long[] mProcessFullStatsData = new long[6];
private static final int[] SYSTEM_CPU_FORMAT = new int[] {
PROC_SPACE_TERM|PROC_COMBINE,
@@ -116,6 +129,9 @@ public class ProcessStats {
private long mCurrentSampleTime;
private long mLastSampleTime;
+ private long mCurrentSampleRealTime;
+ private long mLastSampleRealTime;
+
private long mBaseUserTime;
private long mBaseSystemTime;
private long mBaseIoWaitTime;
@@ -163,10 +179,15 @@ public class ProcessStats {
final ArrayList<Stats> threadStats;
final ArrayList<Stats> workingThreads;
+ public boolean interesting;
+
public String baseName;
public String name;
int nameWidth;
+ public long base_uptime;
+ public long rel_uptime;
+
public long base_utime;
public long base_stime;
public int rel_utime;
@@ -178,6 +199,7 @@ public class ProcessStats {
public int rel_majfaults;
public boolean active;
+ public boolean working;
public boolean added;
public boolean removed;
@@ -211,8 +233,7 @@ public class ProcessStats {
private final static Comparator<Stats> sLoadComparator = new Comparator<Stats>() {
public final int
- compare(Stats sta, Stats stb)
- {
+ compare(Stats sta, Stats stb) {
int ta = sta.rel_utime + sta.rel_stime;
int tb = stb.rel_utime + stb.rel_stime;
if (ta != tb) {
@@ -241,31 +262,17 @@ public class ProcessStats {
}
public void init() {
+ if (DEBUG) Slog.v(TAG, "Init: " + this);
mFirst = true;
update();
}
public void update() {
+ if (DEBUG) Slog.v(TAG, "Update: " + this);
mLastSampleTime = mCurrentSampleTime;
mCurrentSampleTime = SystemClock.uptimeMillis();
-
- final float[] loadAverages = mLoadAverageData;
- if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT,
- null, null, loadAverages)) {
- float load1 = loadAverages[0];
- float load5 = loadAverages[1];
- float load15 = loadAverages[2];
- if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) {
- mLoad1 = load1;
- mLoad5 = load5;
- mLoad15 = load15;
- onLoadChanged(load1, load5, load15);
- }
- }
-
- mCurPids = collectStats("/proc", -1, mFirst, mCurPids,
- mProcStats, mWorkingProcs);
- mFirst = false;
+ mLastSampleRealTime = mCurrentSampleRealTime;
+ mCurrentSampleRealTime = SystemClock.elapsedRealtime();
final long[] sysCpu = mSystemCpuData;
if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
@@ -288,7 +295,7 @@ public class ProcessStats {
mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
mRelIdleTime = (int)(idletime - mBaseIdleTime);
- if (false) {
+ if (DEBUG) {
Slog.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
+ " S:" + sysCpu[2] + " I:" + sysCpu[3]
+ " W:" + sysCpu[4] + " Q:" + sysCpu[5]
@@ -305,16 +312,32 @@ public class ProcessStats {
mBaseIdleTime = idletime;
}
+ mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats);
+
+ final float[] loadAverages = mLoadAverageData;
+ if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT,
+ null, null, loadAverages)) {
+ float load1 = loadAverages[0];
+ float load5 = loadAverages[1];
+ float load15 = loadAverages[2];
+ if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) {
+ mLoad1 = load1;
+ mLoad5 = load5;
+ mLoad15 = load15;
+ onLoadChanged(load1, load5, load15);
+ }
+ }
+
+ if (DEBUG) Slog.i(TAG, "*** TIME TO COLLECT STATS: "
+ + (SystemClock.uptimeMillis()-mCurrentSampleTime));
+
mWorkingProcsSorted = false;
mFirst = false;
}
private int[] collectStats(String statsFile, int parentPid, boolean first,
- int[] curPids, ArrayList<Stats> allProcs,
- ArrayList<Stats> workingProcs) {
+ int[] curPids, ArrayList<Stats> allProcs) {
- workingProcs.clear();
-
int[] pids = Process.getPids(statsFile, curPids);
int NP = (pids == null) ? 0 : pids.length;
int NS = allProcs.size();
@@ -330,56 +353,68 @@ public class ProcessStats {
if (st != null && st.pid == pid) {
// Update an existing process...
st.added = false;
+ st.working = false;
curStatsIndex++;
- if (localLOGV) Slog.v(TAG, "Existing pid " + pid + ": " + st);
+ if (DEBUG) Slog.v(TAG, "Existing "
+ + (parentPid < 0 ? "process" : "thread")
+ + " pid " + pid + ": " + st);
- final long[] procStats = mProcessStatsData;
- if (!Process.readProcFile(st.statFile.toString(),
- PROCESS_STATS_FORMAT, null, procStats, null)) {
- continue;
- }
-
- final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
- final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
- final long utime = procStats[PROCESS_STAT_UTIME];
- final long stime = procStats[PROCESS_STAT_STIME];
-
- if (utime == st.base_utime && stime == st.base_stime) {
- st.rel_utime = 0;
- st.rel_stime = 0;
- st.rel_minfaults = 0;
- st.rel_majfaults = 0;
- if (st.active) {
- st.active = false;
+ if (st.interesting) {
+ final long uptime = SystemClock.uptimeMillis();
+
+ final long[] procStats = mProcessStatsData;
+ if (!Process.readProcFile(st.statFile.toString(),
+ PROCESS_STATS_FORMAT, null, procStats, null)) {
+ continue;
}
- continue;
- }
- if (!st.active) {
- st.active = true;
- }
+ final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
+ final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
+ final long utime = procStats[PROCESS_STAT_UTIME];
+ final long stime = procStats[PROCESS_STAT_STIME];
+
+ if (utime == st.base_utime && stime == st.base_stime) {
+ st.rel_utime = 0;
+ st.rel_stime = 0;
+ st.rel_minfaults = 0;
+ st.rel_majfaults = 0;
+ if (st.active) {
+ st.active = false;
+ }
+ continue;
+ }
- if (parentPid < 0) {
- getName(st, st.cmdlineFile);
- if (st.threadStats != null) {
- mCurThreadPids = collectStats(st.threadsDir, pid, false,
- mCurThreadPids, st.threadStats,
- st.workingThreads);
+ if (!st.active) {
+ st.active = true;
+ }
+
+ if (parentPid < 0) {
+ getName(st, st.cmdlineFile);
+ if (st.threadStats != null) {
+ mCurThreadPids = collectStats(st.threadsDir, pid, false,
+ mCurThreadPids, st.threadStats);
+ }
}
+
+ if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
+ + " utime=" + utime + "-" + st.base_utime
+ + " stime=" + stime + "-" + st.base_stime
+ + " minfaults=" + minfaults + "-" + st.base_minfaults
+ + " majfaults=" + majfaults + "-" + st.base_majfaults);
+
+ st.rel_uptime = uptime - st.base_uptime;
+ st.base_uptime = uptime;
+ st.rel_utime = (int)(utime - st.base_utime);
+ st.rel_stime = (int)(stime - st.base_stime);
+ st.base_utime = utime;
+ st.base_stime = stime;
+ st.rel_minfaults = (int)(minfaults - st.base_minfaults);
+ st.rel_majfaults = (int)(majfaults - st.base_majfaults);
+ st.base_minfaults = minfaults;
+ st.base_majfaults = majfaults;
+ st.working = true;
}
- st.rel_utime = (int)(utime - st.base_utime);
- st.rel_stime = (int)(stime - st.base_stime);
- st.base_utime = utime;
- st.base_stime = stime;
- st.rel_minfaults = (int)(minfaults - st.base_minfaults);
- st.rel_majfaults = (int)(majfaults - st.base_majfaults);
- st.base_minfaults = minfaults;
- st.base_majfaults = majfaults;
- //Slog.i("Load", "Stats changed " + name + " pid=" + st.pid
- // + " name=" + st.name + " utime=" + utime
- // + " stime=" + stime);
- workingProcs.add(st);
continue;
}
@@ -389,19 +424,34 @@ public class ProcessStats {
allProcs.add(curStatsIndex, st);
curStatsIndex++;
NS++;
- if (localLOGV) Slog.v(TAG, "New pid " + pid + ": " + st);
+ if (DEBUG) Slog.v(TAG, "New "
+ + (parentPid < 0 ? "process" : "thread")
+ + " pid " + pid + ": " + st);
final String[] procStatsString = mProcessFullStatsStringData;
final long[] procStats = mProcessFullStatsData;
+ st.base_uptime = SystemClock.uptimeMillis();
if (Process.readProcFile(st.statFile.toString(),
PROCESS_FULL_STATS_FORMAT, procStatsString,
procStats, null)) {
- st.baseName = parentPid < 0
- ? procStatsString[0] : Integer.toString(pid);
- st.base_utime = 0; //procStats[1];
- st.base_stime = 0; //procStats[2];
- st.base_minfaults = st.base_majfaults = 0;
+ // This is a possible way to filter out processes that
+ // are actually kernel threads... do we want to? Some
+ // of them do use CPU, but there can be a *lot* that are
+ // not doing anything.
+ if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) {
+ st.interesting = true;
+ st.baseName = procStatsString[0];
+ st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
+ st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
+ st.base_utime = procStats[PROCESS_FULL_STAT_UTIME];
+ st.base_stime = procStats[PROCESS_FULL_STAT_STIME];
+ } else {
+ Slog.i(TAG, "Skipping kernel process pid " + pid
+ + " name " + procStatsString[0]);
+ st.baseName = procStatsString[0];
+ }
} else {
+ Slog.w(TAG, "Skipping unknown process pid " + pid);
st.baseName = "<unknown>";
st.base_utime = st.base_stime = 0;
st.base_minfaults = st.base_majfaults = 0;
@@ -409,24 +459,26 @@ public class ProcessStats {
if (parentPid < 0) {
getName(st, st.cmdlineFile);
- } else {
- st.name = st.baseName;
- st.nameWidth = onMeasureProcessName(st.name);
if (st.threadStats != null) {
mCurThreadPids = collectStats(st.threadsDir, pid, true,
- mCurThreadPids, st.threadStats,
- st.workingThreads);
+ mCurThreadPids, st.threadStats);
}
+ } else if (st.interesting) {
+ st.name = st.baseName;
+ st.nameWidth = onMeasureProcessName(st.name);
}
+
+ if (DEBUG) Slog.v("Load", "Stats added " + st.name + " pid=" + st.pid
+ + " utime=" + st.base_utime + " stime=" + st.base_stime
+ + " minfaults=" + st.base_minfaults + " majfaults=" + st.base_majfaults);
- //Slog.i("Load", "New process: " + st.pid + " " + st.name);
st.rel_utime = 0;
st.rel_stime = 0;
st.rel_minfaults = 0;
st.rel_majfaults = 0;
st.added = true;
- if (!first) {
- workingProcs.add(st);
+ if (!first && st.interesting) {
+ st.working = true;
}
continue;
}
@@ -437,10 +489,12 @@ public class ProcessStats {
st.rel_minfaults = 0;
st.rel_majfaults = 0;
st.removed = true;
- workingProcs.add(st);
+ st.working = true;
allProcs.remove(curStatsIndex);
NS--;
- if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st);
+ if (DEBUG) Slog.v(TAG, "Removed "
+ + (parentPid < 0 ? "process" : "thread")
+ + " pid " + pid + ": " + st);
// Decrement the loop counter so that we process the current pid
// again the next time through the loop.
i--;
@@ -455,7 +509,7 @@ public class ProcessStats {
st.rel_minfaults = 0;
st.rel_majfaults = 0;
st.removed = true;
- workingProcs.add(st);
+ st.working = true;
allProcs.remove(curStatsIndex);
NS--;
if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st);
@@ -569,11 +623,42 @@ public class ProcessStats {
/ (mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime);
}
- final public int countWorkingStats() {
+ final void buildWorkingProcs() {
if (!mWorkingProcsSorted) {
+ mWorkingProcs.clear();
+ final int N = mProcStats.size();
+ for (int i=0; i<N; i++) {
+ Stats stats = mProcStats.get(i);
+ if (stats.working) {
+ mWorkingProcs.add(stats);
+ if (stats.threadStats != null && stats.threadStats.size() > 1) {
+ stats.workingThreads.clear();
+ final int M = stats.threadStats.size();
+ for (int j=0; j<M; j++) {
+ Stats tstats = stats.threadStats.get(j);
+ if (tstats.working) {
+ stats.workingThreads.add(tstats);
+ }
+ }
+ Collections.sort(stats.workingThreads, sLoadComparator);
+ }
+ }
+ }
Collections.sort(mWorkingProcs, sLoadComparator);
mWorkingProcsSorted = true;
}
+ }
+
+ final public int countStats() {
+ return mProcStats.size();
+ }
+
+ final public Stats getStats(int index) {
+ return mProcStats.get(index);
+ }
+
+ final public int countWorkingStats() {
+ buildWorkingProcs();
return mWorkingProcs.size();
}
@@ -581,12 +666,7 @@ public class ProcessStats {
return mWorkingProcs.get(index);
}
- final public String printCurrentState() {
- if (!mWorkingProcsSorted) {
- Collections.sort(mWorkingProcs, sLoadComparator);
- mWorkingProcsSorted = true;
- }
-
+ final public String printCurrentLoad() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.print("Load: ");
@@ -595,67 +675,111 @@ public class ProcessStats {
pw.print(mLoad5);
pw.print(" / ");
pw.println(mLoad15);
+ return sw.toString();
+ }
+
+ final public String printCurrentState(long now) {
+ buildWorkingProcs();
- long now = SystemClock.uptimeMillis();
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
pw.print("CPU usage from ");
- pw.print(now-mLastSampleTime);
- pw.print("ms to ");
- pw.print(now-mCurrentSampleTime);
- pw.println("ms ago:");
+ if (now > mLastSampleTime) {
+ pw.print(now-mLastSampleTime);
+ pw.print("ms to ");
+ pw.print(now-mCurrentSampleTime);
+ pw.print("ms ago");
+ } else {
+ pw.print(mLastSampleTime-now);
+ pw.print("ms to ");
+ pw.print(mCurrentSampleTime-now);
+ pw.print("ms later");
+ }
+
+ long sampleTime = mCurrentSampleTime - mLastSampleTime;
+ long sampleRealTime = mCurrentSampleRealTime - mLastSampleRealTime;
+ long percAwake = (sampleTime*100) / sampleRealTime;
+ if (percAwake != 100) {
+ pw.print(" with ");
+ pw.print(percAwake);
+ pw.print("% awake");
+ }
+ pw.println(":");
final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
+ mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
+ if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time "
+ + (mCurrentSampleTime-mLastSampleTime));
+
int N = mWorkingProcs.size();
for (int i=0; i<N; i++) {
Stats st = mWorkingProcs.get(i);
printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": " "),
- st.name, totalTime, st.rel_utime, st.rel_stime, 0, 0, 0,
- st.rel_minfaults, st.rel_majfaults);
+ st.pid, st.name, (int)(st.rel_uptime+5)/10,
+ st.rel_utime, st.rel_stime, 0, 0, 0, st.rel_minfaults, st.rel_majfaults);
if (!st.removed && st.workingThreads != null) {
int M = st.workingThreads.size();
for (int j=0; j<M; j++) {
Stats tst = st.workingThreads.get(j);
printProcessCPU(pw,
tst.added ? " +" : (tst.removed ? " -": " "),
- tst.name, totalTime, tst.rel_utime, tst.rel_stime,
- 0, 0, 0, 0, 0);
+ tst.pid, tst.name, (int)(st.rel_uptime+5)/10,
+ tst.rel_utime, tst.rel_stime, 0, 0, 0, 0, 0);
}
}
}
- printProcessCPU(pw, "", "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
+ printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
return sw.toString();
}
- private void printProcessCPU(PrintWriter pw, String prefix, String label, int totalTime,
- int user, int system, int iowait, int irq, int softIrq, int minFaults, int majFaults) {
+ private void printRatio(PrintWriter pw, long numerator, long denominator) {
+ long thousands = (numerator*1000)/denominator;
+ long hundreds = thousands/10;
+ pw.print(hundreds);
+ if (hundreds < 10) {
+ long remainder = thousands - (hundreds*10);
+ if (remainder != 0) {
+ pw.print('.');
+ pw.print(remainder);
+ }
+ }
+ }
+
+ private void printProcessCPU(PrintWriter pw, String prefix, int pid, String label,
+ int totalTime, int user, int system, int iowait, int irq, int softIrq,
+ int minFaults, int majFaults) {
pw.print(prefix);
+ if (totalTime == 0) totalTime = 1;
+ printRatio(pw, user+system+iowait+irq+softIrq, totalTime);
+ pw.print("% ");
+ if (pid >= 0) {
+ pw.print(pid);
+ pw.print("/");
+ }
pw.print(label);
pw.print(": ");
- if (totalTime == 0) totalTime = 1;
- pw.print(((user+system+iowait+irq+softIrq)*100)/totalTime);
- pw.print("% = ");
- pw.print((user*100)/totalTime);
+ printRatio(pw, user, totalTime);
pw.print("% user + ");
- pw.print((system*100)/totalTime);
+ printRatio(pw, system, totalTime);
pw.print("% kernel");
if (iowait > 0) {
pw.print(" + ");
- pw.print((iowait*100)/totalTime);
+ printRatio(pw, iowait, totalTime);
pw.print("% iowait");
}
if (irq > 0) {
pw.print(" + ");
- pw.print((irq*100)/totalTime);
+ printRatio(pw, irq, totalTime);
pw.print("% irq");
}
if (softIrq > 0) {
pw.print(" + ");
- pw.print((softIrq*100)/totalTime);
+ printRatio(pw, softIrq, totalTime);
pw.print("% softirq");
}
if (minFaults > 0 || majFaults > 0) {
@@ -696,8 +820,9 @@ public class ProcessStats {
}
private void getName(Stats st, String cmdlineFile) {
- String newName = st.baseName;
- if (st.baseName == null || st.baseName.equals("app_process")) {
+ String newName = st.name;
+ if (st.name == null || st.name.equals("app_process")
+ || st.name.equals("<pre-initialized>")) {
String cmdName = readFile(cmdlineFile, '\0');
if (cmdName != null && cmdName.length() > 1) {
newName = cmdName;
@@ -706,6 +831,9 @@ public class ProcessStats {
newName = newName.substring(i+1);
}
}
+ if (newName == null) {
+ newName = st.baseName;
+ }
}
if (st.name == null || !newName.equals(st.name)) {
st.name = newName;
diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java
deleted file mode 100644
index 9f5718f..0000000
--- a/services/java/com/android/server/SensorService.java
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2008 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.hardware.ISensorService;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.util.Config;
-import android.util.Slog;
-import android.util.PrintWriterPrinter;
-import android.util.Printer;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-import com.android.internal.app.IBatteryStats;
-import com.android.server.am.BatteryStatsService;
-
-
-/**
- * Class that manages the device's sensors. It register clients and activate
- * the needed sensors. The sensor events themselves are not broadcasted from
- * this service, instead, a file descriptor is provided to each client they
- * can read events from.
- */
-
-class SensorService extends ISensorService.Stub {
- static final String TAG = SensorService.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
- private static final int SENSOR_DISABLE = -1;
- private int mCurrentDelay = 0;
-
- /**
- * Battery statistics to be updated when sensors are enabled and disabled.
- */
- final IBatteryStats mBatteryStats = BatteryStatsService.getService();
-
- private final class Listener implements IBinder.DeathRecipient {
- final IBinder mToken;
- final int mUid;
-
- int mSensors = 0;
- int mDelay = 0x7FFFFFFF;
-
- Listener(IBinder token, int uid) {
- mToken = token;
- mUid = uid;
- }
-
- void addSensor(int sensor, int delay) {
- mSensors |= (1<<sensor);
- if (delay < mDelay)
- mDelay = delay;
- }
-
- void removeSensor(int sensor) {
- mSensors &= ~(1<<sensor);
- }
-
- boolean hasSensor(int sensor) {
- return ((mSensors & (1<<sensor)) != 0);
- }
-
- public void binderDied() {
- if (localLOGV) Slog.d(TAG, "sensor listener died");
- synchronized(mListeners) {
- mListeners.remove(this);
- mToken.unlinkToDeath(this, 0);
- // go through the lists of sensors used by the listener that
- // died and deactivate them.
- for (int sensor=0 ; sensor<32 && mSensors!=0 ; sensor++) {
- if (hasSensor(sensor)) {
- removeSensor(sensor);
- deactivateIfUnusedLocked(sensor);
- try {
- mBatteryStats.noteStopSensor(mUid, sensor);
- } catch (RemoteException e) {
- // oops. not a big deal.
- }
- }
- }
- if (mListeners.size() == 0) {
- _sensors_control_wake();
- _sensors_control_close();
- } else {
- // TODO: we should recalculate the delay, since removing
- // a listener may increase the overall rate.
- }
- mListeners.notify();
- }
- }
- }
-
- @SuppressWarnings("unused")
- public SensorService(Context context) {
- if (localLOGV) Slog.d(TAG, "SensorService startup");
- _sensors_control_init();
- }
-
- public Bundle getDataChannel() throws RemoteException {
- // synchronize so we do not require sensor HAL to be thread-safe.
- synchronized(mListeners) {
- return _sensors_control_open();
- }
- }
-
- public boolean enableSensor(IBinder binder, String name, int sensor, int enable)
- throws RemoteException {
-
- if (localLOGV) Slog.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable);
-
- if (binder == null) {
- Slog.e(TAG, "listener is null (sensor=" + name + ", id=" + sensor + ")");
- return false;
- }
-
- if (enable < 0 && (enable != SENSOR_DISABLE)) {
- Slog.e(TAG, "invalid enable parameter (enable=" + enable +
- ", sensor=" + name + ", id=" + sensor + ")");
- return false;
- }
-
- boolean res;
- int uid = Binder.getCallingUid();
- synchronized(mListeners) {
- res = enableSensorInternalLocked(binder, uid, name, sensor, enable);
- if (res == true) {
- // Inform battery statistics service of status change
- long identity = Binder.clearCallingIdentity();
- if (enable == SENSOR_DISABLE) {
- mBatteryStats.noteStopSensor(uid, sensor);
- } else {
- mBatteryStats.noteStartSensor(uid, sensor);
- }
- Binder.restoreCallingIdentity(identity);
- }
- }
- return res;
- }
-
- private boolean enableSensorInternalLocked(IBinder binder, int uid,
- String name, int sensor, int enable) throws RemoteException {
-
- // check if we have this listener
- Listener l = null;
- for (Listener listener : mListeners) {
- if (binder == listener.mToken) {
- l = listener;
- break;
- }
- }
-
- if (enable != SENSOR_DISABLE) {
- // Activate the requested sensor
- if (_sensors_control_activate(sensor, true) == false) {
- Slog.w(TAG, "could not enable sensor " + sensor);
- return false;
- }
-
- if (l == null) {
- /*
- * we don't have a listener for this binder yet, so
- * create a new one and add it to the list.
- */
- l = new Listener(binder, uid);
- binder.linkToDeath(l, 0);
- mListeners.add(l);
- mListeners.notify();
- }
-
- // take note that this sensor is now used by this client
- l.addSensor(sensor, enable);
-
- } else {
-
- if (l == null) {
- /*
- * This client isn't in the list, this usually happens
- * when enabling the sensor failed, but the client
- * didn't handle the error and later tries to shut that
- * sensor off.
- */
- Slog.w(TAG, "listener with binder " + binder +
- ", doesn't exist (sensor=" + name +
- ", id=" + sensor + ")");
- return false;
- }
-
- // remove this sensor from this client
- l.removeSensor(sensor);
-
- // see if we need to deactivate this sensors=
- deactivateIfUnusedLocked(sensor);
-
- // if the listener doesn't have any more sensors active
- // we can get rid of it
- if (l.mSensors == 0) {
- // we won't need this death notification anymore
- binder.unlinkToDeath(l, 0);
- // remove the listener from the list
- mListeners.remove(l);
- // and if the list is empty, turn off the whole sensor h/w
- if (mListeners.size() == 0) {
- _sensors_control_wake();
- _sensors_control_close();
- }
- mListeners.notify();
- }
- }
-
- // calculate and set the new delay
- int minDelay = 0x7FFFFFFF;
- for (Listener listener : mListeners) {
- if (listener.mDelay < minDelay)
- minDelay = listener.mDelay;
- }
- if (minDelay != 0x7FFFFFFF) {
- mCurrentDelay = minDelay;
- _sensors_control_set_delay(minDelay);
- }
-
- return true;
- }
-
- private void deactivateIfUnusedLocked(int sensor) {
- int size = mListeners.size();
- for (int i=0 ; i<size ; i++) {
- if (mListeners.get(i).hasSensor(sensor)) {
- // this sensor is still in use, don't turn it off
- return;
- }
- }
- if (_sensors_control_activate(sensor, false) == false) {
- Slog.w(TAG, "could not disable sensor " + sensor);
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- synchronized (mListeners) {
- Printer pr = new PrintWriterPrinter(pw);
- int c = 0;
- pr.println(mListeners.size() + " listener(s), delay=" + mCurrentDelay + " ms");
- for (Listener l : mListeners) {
- pr.println("listener[" + c + "] " +
- "sensors=0x" + Integer.toString(l.mSensors, 16) +
- ", uid=" + l.mUid +
- ", delay=" +
- l.mDelay + " ms");
- c++;
- }
- }
- }
-
- private ArrayList<Listener> mListeners = new ArrayList<Listener>();
-
- private static native int _sensors_control_init();
- private static native Bundle _sensors_control_open();
- private static native int _sensors_control_close();
- private static native boolean _sensors_control_activate(int sensor, boolean activate);
- private static native int _sensors_control_set_delay(int ms);
- private static native int _sensors_control_wake();
-}
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
new file mode 100644
index 0000000..e5dfb18
--- /dev/null
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+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
+{
+ static final String TAG = "StatusBarManagerService";
+ static final boolean SPEW = false;
+
+ final Context mContext;
+ Handler mHandler = new Handler();
+ NotificationCallbacks mNotificationCallbacks;
+ volatile IStatusBar mBar;
+ StatusBarIconList mIcons = new StatusBarIconList();
+ HashMap<IBinder,StatusBarNotification> mNotifications
+ = new HashMap<IBinder,StatusBarNotification>();
+
+ // for disabling the status bar
+ ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
+ int mDisabled = 0;
+
+ private class DisableRecord implements IBinder.DeathRecipient {
+ String pkg;
+ int what;
+ IBinder token;
+
+ public void binderDied() {
+ Slog.i(TAG, "binder died for pkg=" + pkg);
+ disable(0, token, pkg);
+ token.unlinkToDeath(this, 0);
+ }
+ }
+
+ public interface NotificationCallbacks {
+ void onSetDisabled(int status);
+ void onClearAll();
+ void onNotificationClick(String pkg, String tag, int id);
+ void onPanelRevealed();
+ void onNotificationError(String pkg, String tag, int id,
+ int uid, int initialPid, String message);
+ }
+
+ /**
+ * Construct the service, add the status bar view to the window manager
+ */
+ public StatusBarManagerService(Context context) {
+ mContext = context;
+
+ final Resources res = context.getResources();
+ mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));
+ }
+
+ public void setNotificationCallbacks(NotificationCallbacks listener) {
+ mNotificationCallbacks = listener;
+ }
+
+ // ================================================================================
+ // Constructing the view
+ // ================================================================================
+
+ public void systemReady() {
+ }
+
+ public void systemReady2() {
+ ComponentName cn = ComponentName.unflattenFromString(
+ mContext.getString(com.android.internal.R.string.config_statusBarComponent));
+ Intent intent = new Intent();
+ intent.setComponent(cn);
+ Slog.i(TAG, "Starting service: " + cn);
+ mContext.startService(intent);
+ }
+
+ // ================================================================================
+ // From IStatusBarService
+ // ================================================================================
+ public void expand() {
+ enforceExpandStatusBar();
+
+ if (mBar != null) {
+ try {
+ mBar.animateExpand();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ public void collapse() {
+ enforceExpandStatusBar();
+
+ if (mBar != null) {
+ try {
+ mBar.animateCollapse();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ public void disable(int what, IBinder token, String pkg) {
+ enforceStatusBar();
+
+ // It's important that the the callback and the call to mBar get done
+ // in the same order when multiple threads are calling this function
+ // so they are paired correctly. The messages on the handler will be
+ // handled in the order they were enqueued, but will be outside the lock.
+ synchronized (mDisableRecords) {
+ manageDisableListLocked(what, token, pkg);
+ final int net = gatherDisableActionsLocked();
+ if (net != mDisabled) {
+ mDisabled = net;
+ mHandler.post(new Runnable() {
+ public void run() {
+ mNotificationCallbacks.onSetDisabled(net);
+ }
+ });
+ if (mBar != null) {
+ try {
+ mBar.disable(net);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+ }
+
+ public void setIcon(String slot, String iconPackage, int iconId, int iconLevel) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ StatusBarIcon icon = new StatusBarIcon(iconPackage, iconId, iconLevel);
+ //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
+ mIcons.setIcon(index, icon);
+
+ if (mBar != null) {
+ try {
+ mBar.setIcon(index, icon);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ public void setIconVisibility(String slot, boolean visible) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ StatusBarIcon icon = mIcons.getIcon(index);
+ if (icon == null) {
+ return;
+ }
+
+ if (icon.visible != visible) {
+ icon.visible = visible;
+
+ if (mBar != null) {
+ try {
+ mBar.setIcon(index, icon);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+ }
+
+ public void removeIcon(String slot) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ mIcons.removeIcon(index);
+
+ if (mBar != null) {
+ try {
+ mBar.removeIcon(index);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ private void enforceStatusBar() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
+ "StatusBarManagerService");
+ }
+
+ private void enforceExpandStatusBar() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
+ "StatusBarManagerService");
+ }
+
+ private void enforceStatusBarService() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ "StatusBarManagerService");
+ }
+
+
+ // ================================================================================
+ // Callbacks from the status bar service.
+ // ================================================================================
+ public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
+ List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {
+ enforceStatusBarService();
+
+ Slog.i(TAG, "registerStatusBar bar=" + bar);
+ mBar = bar;
+ synchronized (mIcons) {
+ iconList.copyFrom(mIcons);
+ }
+ synchronized (mNotifications) {
+ for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
+ notificationKeys.add(e.getKey());
+ notifications.add(e.getValue());
+ }
+ }
+ }
+
+ /**
+ * The status bar service should call this each time the user brings the panel from
+ * invisible to visible in order to clear the notification light.
+ */
+ public void onPanelRevealed() {
+ enforceStatusBarService();
+
+ // tell the notification manager to turn off the lights.
+ mNotificationCallbacks.onPanelRevealed();
+ }
+
+ public void onNotificationClick(String pkg, String tag, int id) {
+ enforceStatusBarService();
+
+ mNotificationCallbacks.onNotificationClick(pkg, tag, id);
+ }
+
+ public void onNotificationError(String pkg, String tag, int id,
+ int uid, int initialPid, String message) {
+ enforceStatusBarService();
+
+ // WARNING: this will call back into us to do the remove. Don't hold any locks.
+ mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message);
+ }
+
+ public void onClearAllNotifications() {
+ enforceStatusBarService();
+
+ mNotificationCallbacks.onClearAll();
+ }
+
+ // ================================================================================
+ // Callbacks for NotificationManagerService.
+ // ================================================================================
+ public IBinder addNotification(StatusBarNotification notification) {
+ synchronized (mNotifications) {
+ IBinder key = new Binder();
+ mNotifications.put(key, notification);
+ if (mBar != null) {
+ try {
+ mBar.addNotification(key, notification);
+ } catch (RemoteException ex) {
+ }
+ }
+ return key;
+ }
+ }
+
+ public void updateNotification(IBinder key, StatusBarNotification notification) {
+ synchronized (mNotifications) {
+ if (!mNotifications.containsKey(key)) {
+ throw new IllegalArgumentException("updateNotification key not found: " + key);
+ }
+ mNotifications.put(key, notification);
+ if (mBar != null) {
+ try {
+ mBar.updateNotification(key, notification);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ public void removeNotification(IBinder key) {
+ synchronized (mNotifications) {
+ final StatusBarNotification n = mNotifications.remove(key);
+ if (n == null) {
+ throw new IllegalArgumentException("removeNotification key not found: " + key);
+ }
+ if (mBar != null) {
+ try {
+ mBar.removeNotification(key);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ // ================================================================================
+ // Can be called from any thread
+ // ================================================================================
+
+ // lock on mDisableRecords
+ void manageDisableListLocked(int what, IBinder token, String pkg) {
+ if (SPEW) {
+ Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
+ }
+ // update the list
+ synchronized (mDisableRecords) {
+ final int N = mDisableRecords.size();
+ DisableRecord tok = null;
+ int i;
+ for (i=0; i<N; i++) {
+ DisableRecord t = mDisableRecords.get(i);
+ if (t.token == token) {
+ tok = t;
+ break;
+ }
+ }
+ if (what == 0 || !token.isBinderAlive()) {
+ if (tok != null) {
+ mDisableRecords.remove(i);
+ tok.token.unlinkToDeath(tok, 0);
+ }
+ } else {
+ if (tok == null) {
+ tok = new DisableRecord();
+ try {
+ token.linkToDeath(tok, 0);
+ }
+ catch (RemoteException ex) {
+ return; // give up
+ }
+ mDisableRecords.add(tok);
+ }
+ tok.what = what;
+ tok.token = token;
+ tok.pkg = pkg;
+ }
+ }
+ }
+
+ // lock on mDisableRecords
+ int gatherDisableActionsLocked() {
+ final int N = mDisableRecords.size();
+ // gather the new net flags
+ int net = 0;
+ for (int i=0; i<N; i++) {
+ net |= mDisableRecords.get(i).what;
+ }
+ return net;
+ }
+
+ // ================================================================================
+ // Always called from UI thread
+ // ================================================================================
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump StatusBar from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mIcons) {
+ mIcons.dump(pw);
+ }
+
+ synchronized (mNotifications) {
+ int i=0;
+ pw.println("Notification list:");
+ for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
+ pw.printf(" %2d: %s\n", i, e.getValue().toString());
+ i++;
+ }
+ }
+
+ synchronized (mDisableRecords) {
+ final int N = mDisableRecords.size();
+ pw.println(" mDisableRecords.size=" + N
+ + " mDisabled=0x" + Integer.toHexString(mDisabled));
+ for (int i=0; i<N; i++) {
+ DisableRecord tok = mDisableRecords.get(i);
+ pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what)
+ + " pkg=" + tok.pkg + " token=" + tok.token);
+ }
+ }
+ }
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_SCREEN_OFF.equals(action)) {
+ collapse();
+ }
+ /*
+ else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
+ updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
+ intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
+ }
+ else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ updateResources();
+ }
+ */
+ }
+ };
+
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9d5d035..df69b76 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,7 +17,7 @@
package com.android.server;
import com.android.server.am.ActivityManagerService;
-import com.android.server.status.StatusBarService;
+import com.android.internal.app.ShutdownThread;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
@@ -35,13 +35,19 @@ import android.content.pm.IPackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.media.AudioService;
-import android.os.*;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
import android.provider.Contacts.People;
import android.provider.Settings;
import android.server.BluetoothA2dpService;
import android.server.BluetoothService;
import android.server.search.SearchManagerService;
import android.util.EventLog;
+import android.util.Log;
import android.util.Slog;
import android.accounts.AccountManagerService;
@@ -81,7 +87,26 @@ class ServerThread extends Thread {
android.os.Process.THREAD_PRIORITY_FOREGROUND);
BinderInternal.disableBackgroundScheduling(true);
-
+ android.os.Process.setCanSelfBackground(false);
+
+ // Check whether we failed to shut down last time we tried.
+ {
+ final String shutdownAction = SystemProperties.get(
+ ShutdownThread.SHUTDOWN_ACTION_PROPERTY, "");
+ if (shutdownAction != null && shutdownAction.length() > 0) {
+ boolean reboot = (shutdownAction.charAt(0) == '1');
+
+ final String reason;
+ if (shutdownAction.length() > 1) {
+ reason = shutdownAction.substring(1, shutdownAction.length());
+ } else {
+ reason = null;
+ }
+
+ ShutdownThread.rebootOrShutdown(reboot, reason);
+ }
+ }
+
String factoryTestStr = SystemProperties.get("ro.factorytest");
int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF
: Integer.parseInt(factoryTestStr);
@@ -97,6 +122,7 @@ class ServerThread extends Thread {
BluetoothA2dpService bluetoothA2dp = null;
HeadsetObserver headset = null;
DockObserver dock = null;
+ UsbObserver usb = null;
UiModeManagerService uiMode = null;
RecognitionManagerService recognition = null;
ThrottleService throttle = null;
@@ -164,10 +190,6 @@ class ServerThread extends Thread {
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
- // Sensor Service is needed by Window Manager, so this goes first
- Slog.i(TAG, "Sensor Service");
- ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));
-
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
@@ -206,7 +228,7 @@ class ServerThread extends Thread {
}
DevicePolicyManagerService devicePolicy = null;
- StatusBarService statusBar = null;
+ StatusBarManagerService statusBar = null;
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
NotificationManagerService notification = null;
@@ -224,10 +246,10 @@ class ServerThread extends Thread {
try {
Slog.i(TAG, "Status Bar");
- statusBar = new StatusBarService(context);
+ statusBar = new StatusBarManagerService(context);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
- Slog.e(TAG, "Failure starting StatusBarService", e);
+ Slog.e(TAG, "Failure starting StatusBarManagerService", e);
}
try {
@@ -256,7 +278,8 @@ class ServerThread extends Thread {
try {
Slog.i(TAG, "NetworkManagement Service");
ServiceManager.addService(
- Context.NETWORKMANAGEMENT_SERVICE, new NetworkManagementService(context));
+ Context.NETWORKMANAGEMENT_SERVICE,
+ NetworkManagementService.create(context));
} catch (Throwable e) {
Slog.e(TAG, "Failure starting NetworkManagement Service", e);
}
@@ -374,8 +397,16 @@ class ServerThread extends Thread {
}
try {
+ Slog.i(TAG, "USB Observer");
+ // Listen for USB changes
+ usb = new UsbObserver(context);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting UsbObserver", e);
+ }
+
+ try {
Slog.i(TAG, "UI Mode Manager Service");
- // Listen for dock station changes
+ // Listen for UI mode changes
uiMode = new UiModeManagerService(context);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting UiModeManagerService", e);
@@ -403,13 +434,7 @@ class ServerThread extends Thread {
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Recognition Service", e);
}
-
- try {
- com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure installing status bar icons", e);
- }
-
+
try {
Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
@@ -464,9 +489,11 @@ class ServerThread extends Thread {
}
// These are needed to propagate to the runnable below.
+ final StatusBarManagerService statusBarF = statusBar;
final BatteryService batteryF = battery;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
+ final UsbObserver usbF = usb;
final ThrottleService throttleF = throttle;
final UiModeManagerService uiModeF = uiMode;
final AppWidgetService appWidgetF = appWidget;
@@ -485,9 +512,11 @@ class ServerThread extends Thread {
public void run() {
Slog.i(TAG, "Making services ready");
+ if (statusBarF != null) statusBarF.systemReady2();
if (batteryF != null) batteryF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
+ if (usbF != null) usbF.systemReady();
if (uiModeF != null) uiModeF.systemReady();
if (recognitionF != null) recognitionF.systemReady();
Watchdog.getInstance().start();
@@ -503,6 +532,11 @@ class ServerThread extends Thread {
}
});
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode for system server main thread.");
+ }
+
Looper.loop();
Slog.d(TAG, "System ServerThread is exiting!");
}
@@ -548,6 +582,10 @@ public class SystemServer
static Timer timer;
static final long SNAPSHOT_INTERVAL = 60 * 60 * 1000; // 1hr
+ // The earliest supported time. We pick one day into 1970, to
+ // give any timezone code room without going into negative time.
+ private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000;
+
/**
* This method is called from Zygote to initialize the system. This will cause the native
* services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
@@ -556,6 +594,16 @@ public class SystemServer
native public static void init1(String[] args);
public static void main(String[] args) {
+ if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
+ // If a device's clock is before 1970 (before 0), a lot of
+ // APIs crash dealing with negative numbers, notably
+ // java.io.File#setLastModified, so instead we fake it and
+ // hope that time from cell towers or NTP fixes it
+ // shortly.
+ Slog.w(TAG, "System clock is before 1970; setting to 1970.");
+ SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
+ }
+
if (SamplingProfilerIntegration.isEnabled()) {
SamplingProfilerIntegration.start();
timer = new Timer();
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 664dfa5..3370279 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -19,6 +19,7 @@ package com.android.server;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -253,6 +254,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyServiceState()")){
return;
}
+ Slog.i(TAG, "notifyServiceState: " + state);
synchronized (mRecords) {
mServiceState = state;
for (int i = mRecords.size() - 1; i >= 0; i--) {
@@ -294,6 +296,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
return;
}
+ Slog.i(TAG, "notifyMessageWaitingChanged: " + mwi);
synchronized (mRecords) {
mMessageWaiting = mwi;
for (int i = mRecords.size() - 1; i >= 0; i--) {
@@ -313,6 +316,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
return;
}
+ Slog.i(TAG, "notifyCallForwardingChanged: " + cfi);
synchronized (mRecords) {
mCallForwarding = cfi;
for (int i = mRecords.size() - 1; i >= 0; i--) {
@@ -348,10 +352,14 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String[] apnTypes, String interfaceName, int networkType) {
+ String reason, String apn, String[] apnTypes, String interfaceName, int networkType,
+ String gateway) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
+ Slog.i(TAG, "notifyDataConnection: state=" + state + " isDataConnectivityPossible="
+ + isDataConnectivityPossible + " reason=" + reason
+ + " interfaceName=" + interfaceName + " networkType=" + networkType);
synchronized (mRecords) {
mDataConnectionState = state;
mDataConnectionPossible = isDataConnectivityPossible;
@@ -372,7 +380,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- apnTypes, interfaceName);
+ apnTypes, interfaceName, gateway);
}
public void notifyDataConnectionFailed(String reason) {
@@ -535,7 +543,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private void broadcastDataConnectionStateChanged(int state,
boolean isDataConnectivityPossible,
- String reason, String apn, String[] apnTypes, String interfaceName) {
+ String reason, String apn, String[] apnTypes, String interfaceName, String gateway) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
@@ -558,6 +566,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
+ int gatewayAddr = 0;
+ if (gateway != null) {
+ gatewayAddr = NetworkUtils.v4StringToInt(gateway);
+ }
+ intent.putExtra(Phone.DATA_GATEWAY_KEY, gatewayAddr);
+
mContext.sendStickyBroadcast(intent);
}
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 019245f..431cc39 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -566,7 +566,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
}
- // Fear not: StatusBarService manages a list of requests to disable
+ // Fear not: StatusBarManagerService manages a list of requests to disable
// features of the status bar; these are ORed together to form the
// active disabled list. So if (for example) the device is locked and
// the status bar should be totally disabled, the calls below will
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
new file mode 100644
index 0000000..d08fe9b
--- /dev/null
+++ b/services/java/com/android/server/UsbObserver.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2010 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.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.Usb;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UEventObserver;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.ArrayList;
+
+/**
+ * <p>UsbObserver monitors for changes to USB state.
+ */
+class UsbObserver extends UEventObserver {
+ private static final String TAG = UsbObserver.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ private static final String USB_CONFIGURATION_MATCH = "DEVPATH=/devices/virtual/switch/usb_configuration";
+ private static final String USB_FUNCTIONS_MATCH = "DEVPATH=/devices/virtual/usb_composite/";
+ private static final String USB_CONFIGURATION_PATH = "/sys/class/switch/usb_configuration/state";
+ private static final String USB_COMPOSITE_CLASS_PATH = "/sys/class/usb_composite";
+
+ private static final int MSG_UPDATE = 0;
+
+ private int mUsbConfig = 0;
+ private int mPreviousUsbConfig = 0;
+
+ // lists of enabled and disabled USB functions
+ private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
+ private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
+
+ private boolean mSystemReady;
+
+ private final Context mContext;
+
+ private PowerManagerService mPowerManager;
+
+ public UsbObserver(Context context) {
+ mContext = context;
+ init(); // set initial status
+
+ startObserving(USB_CONFIGURATION_MATCH);
+ startObserving(USB_FUNCTIONS_MATCH);
+ }
+
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "USB UEVENT: " + event.toString());
+ }
+
+ synchronized (this) {
+ String switchState = event.get("SWITCH_STATE");
+ if (switchState != null) {
+ try {
+ int newConfig = Integer.parseInt(switchState);
+ if (newConfig != mUsbConfig) {
+ mPreviousUsbConfig = mUsbConfig;
+ mUsbConfig = newConfig;
+ // trigger an Intent broadcast
+ if (mSystemReady) {
+ update();
+ }
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Could not parse switch state from event " + event);
+ }
+ } else {
+ String function = event.get("FUNCTION");
+ String enabledStr = event.get("ENABLED");
+ if (function != null && enabledStr != null) {
+ // Note: we do not broadcast a change when a function is enabled or disabled.
+ // We just record the state change for the next broadcast.
+ boolean enabled = "1".equals(enabledStr);
+ if (enabled) {
+ if (!mEnabledFunctions.contains(function)) {
+ mEnabledFunctions.add(function);
+ }
+ mDisabledFunctions.remove(function);
+ } else {
+ if (!mDisabledFunctions.contains(function)) {
+ mDisabledFunctions.add(function);
+ }
+ mEnabledFunctions.remove(function);
+ }
+ }
+ }
+ }
+ }
+ private final void init() {
+ char[] buffer = new char[1024];
+
+ try {
+ FileReader file = new FileReader(USB_CONFIGURATION_PATH);
+ int len = file.read(buffer, 0, 1024);
+ mPreviousUsbConfig = mUsbConfig = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have USB configuration switch support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+
+ try {
+ File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
+ for (int i = 0; i < files.length; i++) {
+ File file = new File(files[i], "enable");
+ FileReader reader = new FileReader(file);
+ int len = reader.read(buffer, 0, 1024);
+ int value = Integer.valueOf((new String(buffer, 0, len)).trim());
+ String functionName = files[i].getName();
+ if (value == 1) {
+ mEnabledFunctions.add(functionName);
+ } else {
+ mDisabledFunctions.add(functionName);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have USB composite class support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+ }
+
+ void systemReady() {
+ synchronized (this) {
+ update();
+ mSystemReady = true;
+ }
+ }
+
+ private final void update() {
+ mHandler.sendEmptyMessage(MSG_UPDATE);
+ }
+
+ private final Handler mHandler = new Handler() {
+ private void addEnabledFunctions(Intent intent) {
+ // include state of all USB functions in our extras
+ for (int i = 0; i < mEnabledFunctions.size(); i++) {
+ intent.putExtra(mEnabledFunctions.get(i), Usb.USB_FUNCTION_ENABLED);
+ }
+ for (int i = 0; i < mDisabledFunctions.size(); i++) {
+ intent.putExtra(mDisabledFunctions.get(i), Usb.USB_FUNCTION_DISABLED);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE:
+ synchronized (this) {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ if (Settings.Secure.getInt(cr,
+ Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+ Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
+ return;
+ }
+ // Send an Intent containing connected/disconnected state
+ // and the enabled/disabled state of all USB functions
+ Intent intent;
+ boolean usbConnected = (mUsbConfig != 0);
+ if (usbConnected) {
+ intent = new Intent(Usb.ACTION_USB_CONNECTED);
+ addEnabledFunctions(intent);
+ } else {
+ intent = new Intent(Usb.ACTION_USB_DISCONNECTED);
+ }
+ mContext.sendBroadcast(intent);
+
+ // send a sticky broadcast for clients interested in both connect and disconnect
+ intent = new Intent(Usb.ACTION_USB_STATE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(Usb.USB_CONNECTED, usbConnected);
+ addEnabledFunctions(intent);
+ mContext.sendStickyBroadcast(intent);
+ }
+ break;
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 2e7e3e1..f0b5955 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -29,6 +29,7 @@ import android.os.RemoteException;
import android.os.IBinder;
import android.os.Binder;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.util.Slog;
import java.util.LinkedList;
@@ -39,6 +40,7 @@ public class VibratorService extends IVibratorService.Stub {
private final LinkedList<Vibration> mVibrations;
private Vibration mCurrentVibration;
+ private final WorkSource mTmpWorkSource = new WorkSource();
private class Vibration implements IBinder.DeathRecipient {
private final IBinder mToken;
@@ -46,22 +48,24 @@ public class VibratorService extends IVibratorService.Stub {
private final long mStartTime;
private final long[] mPattern;
private final int mRepeat;
+ private final int mUid;
- Vibration(IBinder token, long millis) {
- this(token, millis, null, 0);
+ Vibration(IBinder token, long millis, int uid) {
+ this(token, millis, null, 0, uid);
}
- Vibration(IBinder token, long[] pattern, int repeat) {
- this(token, 0, pattern, repeat);
+ Vibration(IBinder token, long[] pattern, int repeat, int uid) {
+ this(token, 0, pattern, repeat, uid);
}
private Vibration(IBinder token, long millis, long[] pattern,
- int repeat) {
+ int repeat, int uid) {
mToken = token;
mTimeout = millis;
mStartTime = SystemClock.uptimeMillis();
mPattern = pattern;
mRepeat = repeat;
+ mUid = uid;
}
public void binderDied() {
@@ -98,7 +102,7 @@ public class VibratorService extends IVibratorService.Stub {
mContext = context;
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
mVibrations = new LinkedList<Vibration>();
@@ -113,6 +117,7 @@ public class VibratorService extends IVibratorService.Stub {
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
+ int uid = Binder.getCallingUid();
// We're running in the system server so we cannot crash. Check for a
// timeout of 0 or negative. This will ensure that a vibration has
// either a timeout of > 0 or a non-null pattern.
@@ -122,7 +127,7 @@ public class VibratorService extends IVibratorService.Stub {
// longer than milliseconds.
return;
}
- Vibration vib = new Vibration(token, milliseconds);
+ Vibration vib = new Vibration(token, milliseconds, uid);
synchronized (mVibrations) {
removeVibrationLocked(token);
doCancelVibrateLocked();
@@ -146,6 +151,7 @@ public class VibratorService extends IVibratorService.Stub {
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
+ int uid = Binder.getCallingUid();
// so wakelock calls will succeed
long identity = Binder.clearCallingIdentity();
try {
@@ -165,7 +171,7 @@ public class VibratorService extends IVibratorService.Stub {
return;
}
- Vibration vib = new Vibration(token, pattern, repeat);
+ Vibration vib = new Vibration(token, pattern, repeat, uid);
try {
token.linkToDeath(vib, 0);
} catch (RemoteException e) {
@@ -280,6 +286,8 @@ public class VibratorService extends IVibratorService.Stub {
VibrateThread(Vibration vib) {
mVibration = vib;
+ mTmpWorkSource.set(vib.mUid);
+ mWakeLock.setWorkSource(mTmpWorkSource);
mWakeLock.acquire();
}
diff --git a/services/java/com/android/server/ViewServer.java b/services/java/com/android/server/ViewServer.java
index ae00438..7b5d18a 100644
--- a/services/java/com/android/server/ViewServer.java
+++ b/services/java/com/android/server/ViewServer.java
@@ -21,6 +21,8 @@ import android.util.Slog;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@@ -41,11 +43,13 @@ class ViewServer implements Runnable {
*/
public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
+ private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
+
// Debug facility
private static final String LOG_TAG = "ViewServer";
- private static final String VALUE_PROTOCOL_VERSION = "2";
- private static final String VALUE_SERVER_VERSION = "3";
+ private static final String VALUE_PROTOCOL_VERSION = "3";
+ private static final String VALUE_SERVER_VERSION = "4";
// Protocol commands
// Returns the protocol version
@@ -54,6 +58,10 @@ class ViewServer implements Runnable {
private static final String COMMAND_SERVER_VERSION = "SERVER";
// Lists all of the available windows in the system
private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
+ // Keeps a connection open and notifies when the list of windows changes
+ private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
+ // Returns the focused window
+ private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
private ServerSocket mServer;
private Thread mThread;
@@ -61,6 +69,8 @@ class ViewServer implements Runnable {
private final WindowManagerService mWindowManager;
private final int mPort;
+ private ExecutorService mThreadPool;
+
/**
* Creates a new ViewServer associated with the specified window manager.
* The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
@@ -103,8 +113,9 @@ class ViewServer implements Runnable {
return false;
}
- mServer = new ServerSocket(mPort, 1, InetAddress.getLocalHost());
+ mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
+ mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
mThread.start();
return true;
@@ -122,7 +133,16 @@ class ViewServer implements Runnable {
*/
boolean stop() {
if (mThread != null) {
+
mThread.interrupt();
+ if (mThreadPool != null) {
+ try {
+ mThreadPool.shutdownNow();
+ } catch (SecurityException e) {
+ Slog.w(LOG_TAG, "Could not stop all view server threads");
+ }
+ }
+ mThreadPool = null;
mThread = null;
try {
mServer.close();
@@ -152,62 +172,21 @@ class ViewServer implements Runnable {
* Main server loop.
*/
public void run() {
- final ServerSocket server = mServer;
-
while (Thread.currentThread() == mThread) {
- Socket client = null;
// Any uncaught exception will crash the system process
try {
- client = server.accept();
-
- BufferedReader in = null;
- try {
- in = new BufferedReader(new InputStreamReader(client.getInputStream()), 1024);
-
- final String request = in.readLine();
-
- String command;
- String parameters;
-
- int index = request.indexOf(' ');
- if (index == -1) {
- command = request;
- parameters = "";
- } else {
- command = request.substring(0, index);
- parameters = request.substring(index + 1);
- }
-
- boolean result;
- if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
- result = writeValue(client, VALUE_PROTOCOL_VERSION);
- } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
- result = writeValue(client, VALUE_SERVER_VERSION);
- } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
- result = mWindowManager.viewServerListWindows(client);
- } else {
- result = mWindowManager.viewServerWindowCommand(client,
- command, parameters);
- }
-
- if (!result) {
- Slog.w(LOG_TAG, "An error occured with the command: " + command);
- }
- } finally {
- if (in != null) {
- in.close();
- }
- }
- } catch (Exception e) {
- Slog.w(LOG_TAG, "Connection error: ", e);
- } finally {
- if (client != null) {
+ Socket client = mServer.accept();
+ if(mThreadPool != null) {
+ mThreadPool.submit(new ViewServerWorker(client));
+ } else {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
+ } catch (Exception e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
}
}
}
@@ -235,4 +214,133 @@ class ViewServer implements Runnable {
}
return result;
}
+
+ class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
+ private Socket mClient;
+ private boolean mNeedWindowListUpdate;
+ private boolean mNeedFocusedWindowUpdate;
+ public ViewServerWorker(Socket client) {
+ mClient = client;
+ mNeedWindowListUpdate = false;
+ mNeedFocusedWindowUpdate = false;
+ }
+
+ public void run() {
+
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
+
+ final String request = in.readLine();
+
+ String command;
+ String parameters;
+
+ int index = request.indexOf(' ');
+ if (index == -1) {
+ command = request;
+ parameters = "";
+ } else {
+ command = request.substring(0, index);
+ parameters = request.substring(index + 1);
+ }
+
+ boolean result;
+ if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
+ result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
+ } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
+ result = writeValue(mClient, VALUE_SERVER_VERSION);
+ } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
+ result = mWindowManager.viewServerListWindows(mClient);
+ } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
+ result = mWindowManager.viewServerGetFocusedWindow(mClient);
+ } else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
+ result = windowManagerAutolistLoop();
+ } else {
+ result = mWindowManager.viewServerWindowCommand(mClient,
+ command, parameters);
+ }
+
+ if (!result) {
+ Slog.w(LOG_TAG, "An error occured with the command: " + command);
+ }
+ } catch(IOException e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (mClient != null) {
+ try {
+ mClient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public void windowsChanged() {
+ synchronized(this) {
+ mNeedWindowListUpdate = true;
+ notifyAll();
+ }
+ }
+
+ public void focusChanged() {
+ synchronized(this) {
+ mNeedFocusedWindowUpdate = true;
+ notifyAll();
+ }
+ }
+
+ private boolean windowManagerAutolistLoop() {
+ mWindowManager.addWindowChangeListener(this);
+ BufferedWriter out = null;
+ try {
+ out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
+ while (!Thread.interrupted()) {
+ boolean needWindowListUpdate = false;
+ boolean needFocusedWindowUpdate = false;
+ synchronized (this) {
+ while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
+ wait();
+ }
+ if (mNeedWindowListUpdate) {
+ mNeedWindowListUpdate = false;
+ needWindowListUpdate = true;
+ }
+ if (mNeedFocusedWindowUpdate) {
+ mNeedFocusedWindowUpdate = false;
+ needFocusedWindowUpdate = true;
+ }
+ }
+ if(needWindowListUpdate) {
+ out.write("LIST UPDATE\n");
+ out.flush();
+ }
+ if(needFocusedWindowUpdate) {
+ out.write("FOCUS UPDATE\n");
+ out.flush();
+ }
+ }
+ } catch (Exception e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ }
+ }
+ mWindowManager.removeWindowChangeListener(this);
+ }
+ return true;
+ }
+ }
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 124da4e..859c85c 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -35,6 +35,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.FileObserver;
@@ -727,9 +728,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ // Called by SystemBackupAgent after files are restored to disk.
void settingsRestored() {
if (DEBUG) Slog.v(TAG, "settingsRestored");
-
+
boolean success = false;
synchronized (mLock) {
loadSettingsLocked();
@@ -766,7 +768,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
mName = "";
WALLPAPER_FILE.delete();
}
- saveSettingsLocked();
+
+ synchronized (mLock) {
+ saveSettingsLocked();
+ }
}
boolean restoreNamedResourceLocked() {
@@ -832,6 +837,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
} catch (IOException ex) {}
}
if (fos != null) {
+ FileUtils.sync(fos);
try {
fos.close();
} catch (IOException ex) {}
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index d4133f3..1b885f5 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -39,9 +39,6 @@ import android.util.Log;
import android.util.Slog;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -57,20 +54,10 @@ public class Watchdog extends Thread {
static final boolean RECORD_KERNEL_THREADS = true;
static final int MONITOR = 2718;
- static final int GLOBAL_PSS = 2719;
static final int TIME_TO_RESTART = DB ? 15*1000 : 60*1000;
static final int TIME_TO_WAIT = TIME_TO_RESTART / 2;
- static final int MEMCHECK_DEFAULT_INTERVAL = DB ? 30 : 30*60; // 30 minutes
- static final int MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL = DB ? 60 : 2*60*60; // 2 hours
- static final int MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD = (DB ? 10:16)*1024*1024; // 16MB
- static final int MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD = (DB ? 14:20)*1024*1024; // 20MB
- static final int MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD = (DB ? 4:8)*1024*1024; // 8MB
- static final int MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD = (DB ? 8:12)*1024*1024; // 12MB
-
- static final int MEMCHECK_DEFAULT_EXEC_START_TIME = 1*60*60; // 1:00am
- static final int MEMCHECK_DEFAULT_EXEC_END_TIME = 5*60*60; // 5:00am
static final int MEMCHECK_DEFAULT_MIN_SCREEN_OFF = DB ? 1*60 : 5*60; // 5 minutes
static final int MEMCHECK_DEFAULT_MIN_ALARM = DB ? 1*60 : 3*60; // 3 minutes
static final int MEMCHECK_DEFAULT_RECHECK_INTERVAL = DB ? 1*60 : 5*60; // 5 minutes
@@ -79,14 +66,12 @@ public class Watchdog extends Thread {
static final int REBOOT_DEFAULT_START_TIME = 3*60*60; // 3:00am
static final int REBOOT_DEFAULT_WINDOW = 60*60; // within 1 hour
- static final String CHECKUP_ACTION = "com.android.service.Watchdog.CHECKUP";
static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT";
static Watchdog sWatchdog;
/* This handler will be used to post message back onto the main thread */
final Handler mHandler;
- final Runnable mGlobalPssCollected;
final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
ContentResolver mResolver;
BatteryService mBattery;
@@ -97,31 +82,9 @@ public class Watchdog extends Thread {
boolean mForceKillSystem;
Monitor mCurrentMonitor;
- PssRequestor mPhoneReq;
int mPhonePid;
- int mPhonePss;
-
- long mLastMemCheckTime = -(MEMCHECK_DEFAULT_INTERVAL*1000);
- boolean mHavePss;
- long mLastMemCheckRealtime = -(MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL*1000);
- boolean mHaveGlobalPss;
- final MemMonitor mSystemMemMonitor = new MemMonitor("system",
- Settings.Secure.MEMCHECK_SYSTEM_ENABLED,
- Settings.Secure.MEMCHECK_SYSTEM_SOFT_THRESHOLD,
- MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD,
- Settings.Secure.MEMCHECK_SYSTEM_HARD_THRESHOLD,
- MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD);
- final MemMonitor mPhoneMemMonitor = new MemMonitor("com.android.phone",
- Settings.Secure.MEMCHECK_PHONE_ENABLED,
- Settings.Secure.MEMCHECK_PHONE_SOFT_THRESHOLD,
- MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD,
- Settings.Secure.MEMCHECK_PHONE_HARD_THRESHOLD,
- MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD);
final Calendar mCalendar = Calendar.getInstance();
- long mMemcheckLastTime;
- long mMemcheckExecStartTime;
- long mMemcheckExecEndTime;
int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF;
int mMinAlarm = MEMCHECK_DEFAULT_MIN_ALARM;
boolean mNeedScheduledCheck;
@@ -140,128 +103,13 @@ public class Watchdog extends Thread {
int mReqRecheckInterval= -1; // >= 0 if a specific recheck interval has been requested
/**
- * This class monitors the memory in a particular process.
- */
- final class MemMonitor {
- final String mProcessName;
- final String mEnabledSetting;
- final String mSoftSetting;
- final String mHardSetting;
-
- int mSoftThreshold;
- int mHardThreshold;
- boolean mEnabled;
- long mLastPss;
-
- static final int STATE_OK = 0;
- static final int STATE_SOFT = 1;
- static final int STATE_HARD = 2;
- int mState;
-
- MemMonitor(String processName, String enabledSetting,
- String softSetting, int defSoftThreshold,
- String hardSetting, int defHardThreshold) {
- mProcessName = processName;
- mEnabledSetting = enabledSetting;
- mSoftSetting = softSetting;
- mHardSetting = hardSetting;
- mSoftThreshold = defSoftThreshold;
- mHardThreshold = defHardThreshold;
- }
-
- void retrieveSettings(ContentResolver resolver) {
- mSoftThreshold = Settings.Secure.getInt(
- resolver, mSoftSetting, mSoftThreshold);
- mHardThreshold = Settings.Secure.getInt(
- resolver, mHardSetting, mHardThreshold);
- mEnabled = Settings.Secure.getInt(
- resolver, mEnabledSetting, 0) != 0;
- }
-
- boolean checkLocked(long curTime, int pid, int pss) {
- mLastPss = pss;
- if (mLastPss < mSoftThreshold) {
- mState = STATE_OK;
- } else if (mLastPss < mHardThreshold) {
- mState = STATE_SOFT;
- } else {
- mState = STATE_HARD;
- }
- EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_PSS, mProcessName, pid, mLastPss);
-
- if (mState == STATE_OK) {
- // Memory is good, don't recover.
- return false;
- }
-
- if (mState == STATE_HARD) {
- // Memory is really bad, kill right now.
- EventLog.writeEvent(EventLogTags.WATCHDOG_HARD_RESET, mProcessName, pid,
- mHardThreshold, mLastPss);
- return mEnabled;
- }
-
- // It is time to schedule a reset...
- // Check if we are currently within the time to kill processes due
- // to memory use.
- computeMemcheckTimesLocked(curTime);
- String skipReason = null;
- if (curTime < mMemcheckExecStartTime || curTime > mMemcheckExecEndTime) {
- skipReason = "time";
- } else {
- skipReason = shouldWeBeBrutalLocked(curTime);
- }
- EventLog.writeEvent(EventLogTags.WATCHDOG_SOFT_RESET, mProcessName, pid,
- mSoftThreshold, mLastPss, skipReason != null ? skipReason : "");
- if (skipReason != null) {
- mNeedScheduledCheck = true;
- return false;
- }
- return mEnabled;
- }
-
- void clear() {
- mLastPss = 0;
- mState = STATE_OK;
- }
- }
-
- /**
* Used for scheduling monitor callbacks and checking memory usage.
*/
final class HeartbeatHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case GLOBAL_PSS: {
- if (mHaveGlobalPss) {
- // During the last pass we collected pss information, so
- // now it is time to report it.
- mHaveGlobalPss = false;
- if (localLOGV) Slog.v(TAG, "Received global pss, logging.");
- logGlobalMemory();
- }
- } break;
-
case MONITOR: {
- if (mHavePss) {
- // During the last pass we collected pss information, so
- // now it is time to report it.
- mHavePss = false;
- if (localLOGV) Slog.v(TAG, "Have pss, checking memory.");
- checkMemory();
- }
-
- if (mHaveGlobalPss) {
- // During the last pass we collected pss information, so
- // now it is time to report it.
- mHaveGlobalPss = false;
- if (localLOGV) Slog.v(TAG, "Have global pss, logging.");
- logGlobalMemory();
- }
-
- long now = SystemClock.uptimeMillis();
-
// See if we should force a reboot.
int rebootInterval = mReqRebootInterval >= 0
? mReqRebootInterval : Settings.Secure.getInt(
@@ -274,32 +122,6 @@ public class Watchdog extends Thread {
checkReboot(false);
}
- // See if we should check memory conditions.
- long memCheckInterval = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_INTERVAL,
- MEMCHECK_DEFAULT_INTERVAL) * 1000;
- if ((mLastMemCheckTime+memCheckInterval) < now) {
- // It is now time to collect pss information. This
- // is async so we won't report it now. And to keep
- // things simple, we will assume that everyone has
- // reported back by the next MONITOR message.
- mLastMemCheckTime = now;
- if (localLOGV) Slog.v(TAG, "Collecting memory usage.");
- collectMemory();
- mHavePss = true;
-
- long memCheckRealtimeInterval = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_LOG_REALTIME_INTERVAL,
- MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL) * 1000;
- long realtimeNow = SystemClock.elapsedRealtime();
- if ((mLastMemCheckRealtime+memCheckRealtimeInterval) < realtimeNow) {
- mLastMemCheckRealtime = realtimeNow;
- if (localLOGV) Slog.v(TAG, "Collecting global memory usage.");
- collectGlobalMemory();
- mHaveGlobalPss = true;
- }
- }
-
final int size = mMonitors.size();
for (int i = 0 ; i < size ; i++) {
mCurrentMonitor = mMonitors.get(i);
@@ -315,20 +137,6 @@ public class Watchdog extends Thread {
}
}
- final class GlobalPssCollected implements Runnable {
- public void run() {
- mHandler.sendEmptyMessage(GLOBAL_PSS);
- }
- }
-
- final class CheckupReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context c, Intent intent) {
- if (localLOGV) Slog.v(TAG, "Alarm went off, checking memory.");
- checkMemory();
- }
- }
-
final class RebootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context c, Intent intent) {
@@ -359,27 +167,6 @@ public class Watchdog extends Thread {
void monitor();
}
- public interface PssRequestor {
- void requestPss();
- }
-
- public class PssStats {
- public int mEmptyPss;
- public int mEmptyCount;
- public int mBackgroundPss;
- public int mBackgroundCount;
- public int mServicePss;
- public int mServiceCount;
- public int mVisiblePss;
- public int mVisibleCount;
- public int mForegroundPss;
- public int mForegroundCount;
-
- public int mNoPssCount;
-
- public int mProcDeaths[] = new int[10];
- }
-
public static Watchdog getInstance() {
if (sWatchdog == null) {
sWatchdog = new Watchdog();
@@ -391,7 +178,6 @@ public class Watchdog extends Thread {
private Watchdog() {
super("watchdog");
mHandler = new HeartbeatHandler();
- mGlobalPssCollected = new GlobalPssCollected();
}
public void init(Context context, BatteryService battery,
@@ -403,11 +189,6 @@ public class Watchdog extends Thread {
mAlarm = alarm;
mActivity = activity;
- context.registerReceiver(new CheckupReceiver(),
- new IntentFilter(CHECKUP_ACTION));
- mCheckupIntent = PendingIntent.getBroadcast(context,
- 0, new Intent(CHECKUP_ACTION), 0);
-
context.registerReceiver(new RebootReceiver(),
new IntentFilter(REBOOT_ACTION));
mRebootIntent = PendingIntent.getBroadcast(context,
@@ -420,20 +201,10 @@ public class Watchdog extends Thread {
mBootTime = System.currentTimeMillis();
}
- public void processStarted(PssRequestor req, String name, int pid) {
+ public void processStarted(String name, int pid) {
synchronized (this) {
if ("com.android.phone".equals(name)) {
- mPhoneReq = req;
mPhonePid = pid;
- mPhonePss = 0;
- }
- }
- }
-
- public void reportPss(PssRequestor req, String name, int pss) {
- synchronized (this) {
- if (mPhoneReq == req) {
- mPhonePss = pss;
}
}
}
@@ -447,152 +218,6 @@ public class Watchdog extends Thread {
}
}
- /**
- * Retrieve memory usage information from specific processes being
- * monitored. This is an async operation, so must be done before doing
- * memory checks.
- */
- void collectMemory() {
- synchronized (this) {
- if (mPhoneReq != null) {
- mPhoneReq.requestPss();
- }
- }
- }
-
- /**
- * Retrieve memory usage over all application processes. This is an
- * async operation, so must be done before doing memory checks.
- */
- void collectGlobalMemory() {
- mActivity.requestPss(mGlobalPssCollected);
- }
-
- /**
- * Check memory usage in the system, scheduling kills/reboots as needed.
- * This always runs on the mHandler thread.
- */
- void checkMemory() {
- boolean needScheduledCheck;
- long curTime;
- long nextTime = 0;
-
- long recheckInterval = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_RECHECK_INTERVAL,
- MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000;
-
- mSystemMemMonitor.retrieveSettings(mResolver);
- mPhoneMemMonitor.retrieveSettings(mResolver);
- retrieveBrutalityAmount();
-
- synchronized (this) {
- curTime = System.currentTimeMillis();
- mNeedScheduledCheck = false;
-
- // How is the system doing?
- if (mSystemMemMonitor.checkLocked(curTime, Process.myPid(),
- (int)Process.getPss(Process.myPid()))) {
- // Not good! Time to suicide.
- mForceKillSystem = true;
- notifyAll();
- return;
- }
-
- // How is the phone process doing?
- if (mPhoneReq != null) {
- if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
- mPhonePss)) {
- // Just kill the phone process and let it restart.
- Slog.i(TAG, "Watchdog is killing the phone process");
- Process.killProcess(mPhonePid);
- }
- } else {
- mPhoneMemMonitor.clear();
- }
-
- needScheduledCheck = mNeedScheduledCheck;
- if (needScheduledCheck) {
- // Something is going bad, but now is not a good time to
- // tear things down... schedule an alarm to check again soon.
- nextTime = curTime + recheckInterval;
- if (nextTime < mMemcheckExecStartTime) {
- nextTime = mMemcheckExecStartTime;
- } else if (nextTime >= mMemcheckExecEndTime){
- // Need to check during next exec time... so that needs
- // to be computed.
- if (localLOGV) Slog.v(TAG, "Computing next time range");
- computeMemcheckTimesLocked(nextTime);
- nextTime = mMemcheckExecStartTime;
- }
-
- if (localLOGV) {
- mCalendar.setTimeInMillis(nextTime);
- Slog.v(TAG, "Next Alarm Time: " + mCalendar);
- }
- }
- }
-
- if (needScheduledCheck) {
- if (localLOGV) Slog.v(TAG, "Scheduling next memcheck alarm for "
- + ((nextTime-curTime)/1000/60) + "m from now");
- mAlarm.remove(mCheckupIntent);
- mAlarm.set(AlarmManager.RTC_WAKEUP, nextTime, mCheckupIntent);
- } else {
- if (localLOGV) Slog.v(TAG, "No need to schedule a memcheck alarm!");
- mAlarm.remove(mCheckupIntent);
- }
- }
-
- final PssStats mPssStats = new PssStats();
- final String[] mMemInfoFields = new String[] {
- "MemFree:", "Buffers:", "Cached:",
- "Active:", "Inactive:",
- "AnonPages:", "Mapped:", "Slab:",
- "SReclaimable:", "SUnreclaim:", "PageTables:" };
- final long[] mMemInfoSizes = new long[mMemInfoFields.length];
- final String[] mVMStatFields = new String[] {
- "pgfree ", "pgactivate ", "pgdeactivate ",
- "pgfault ", "pgmajfault " };
- final long[] mVMStatSizes = new long[mVMStatFields.length];
- final long[] mPrevVMStatSizes = new long[mVMStatFields.length];
- long mLastLogGlobalMemoryTime;
-
- void logGlobalMemory() {
- PssStats stats = mPssStats;
- mActivity.collectPss(stats);
- EventLog.writeEvent(EventLogTags.WATCHDOG_PSS_STATS,
- stats.mEmptyPss, stats.mEmptyCount,
- stats.mBackgroundPss, stats.mBackgroundCount,
- stats.mServicePss, stats.mServiceCount,
- stats.mVisiblePss, stats.mVisibleCount,
- stats.mForegroundPss, stats.mForegroundCount,
- stats.mNoPssCount);
- EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_STATS,
- stats.mProcDeaths[0], stats.mProcDeaths[1], stats.mProcDeaths[2],
- stats.mProcDeaths[3], stats.mProcDeaths[4]);
- Process.readProcLines("/proc/meminfo", mMemInfoFields, mMemInfoSizes);
- for (int i=0; i<mMemInfoSizes.length; i++) {
- mMemInfoSizes[i] *= 1024;
- }
- EventLog.writeEvent(EventLogTags.WATCHDOG_MEMINFO,
- (int)mMemInfoSizes[0], (int)mMemInfoSizes[1], (int)mMemInfoSizes[2],
- (int)mMemInfoSizes[3], (int)mMemInfoSizes[4],
- (int)mMemInfoSizes[5], (int)mMemInfoSizes[6], (int)mMemInfoSizes[7],
- (int)mMemInfoSizes[8], (int)mMemInfoSizes[9], (int)mMemInfoSizes[10]);
- long now = SystemClock.uptimeMillis();
- long dur = now - mLastLogGlobalMemoryTime;
- mLastLogGlobalMemoryTime = now;
- Process.readProcLines("/proc/vmstat", mVMStatFields, mVMStatSizes);
- for (int i=0; i<mVMStatSizes.length; i++) {
- long v = mVMStatSizes[i];
- mVMStatSizes[i] -= mPrevVMStatSizes[i];
- mPrevVMStatSizes[i] = v;
- }
- EventLog.writeEvent(EventLogTags.WATCHDOG_VMSTAT, dur,
- (int)mVMStatSizes[0], (int)mVMStatSizes[1], (int)mVMStatSizes[2],
- (int)mVMStatSizes[3], (int)mVMStatSizes[4]);
- }
-
void checkReboot(boolean fromAlarm) {
int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval
: Settings.Secure.getInt(
@@ -730,47 +355,6 @@ public class Watchdog extends Thread {
return null;
}
- /**
- * Compute the times during which we next would like to perform process
- * restarts.
- *
- * @param curTime The current system time.
- */
- void computeMemcheckTimesLocked(long curTime) {
- if (mMemcheckLastTime == curTime) {
- return;
- }
-
- mMemcheckLastTime = curTime;
-
- long memcheckExecStartTime = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_EXEC_START_TIME,
- MEMCHECK_DEFAULT_EXEC_START_TIME);
- long memcheckExecEndTime = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_EXEC_END_TIME,
- MEMCHECK_DEFAULT_EXEC_END_TIME);
-
- mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
- memcheckExecEndTime);
- if (mMemcheckExecEndTime < curTime) {
- memcheckExecStartTime += 24*60*60;
- memcheckExecEndTime += 24*60*60;
- mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
- memcheckExecEndTime);
- }
- mMemcheckExecStartTime = computeCalendarTime(mCalendar, curTime,
- memcheckExecStartTime);
-
- if (localLOGV) {
- mCalendar.setTimeInMillis(curTime);
- Slog.v(TAG, "Current Time: " + mCalendar);
- mCalendar.setTimeInMillis(mMemcheckExecStartTime);
- Slog.v(TAG, "Start Check Time: " + mCalendar);
- mCalendar.setTimeInMillis(mMemcheckExecEndTime);
- Slog.v(TAG, "End Check Time: " + mCalendar);
- }
- }
-
static long computeCalendarTime(Calendar c, long curTime,
long secondsSinceMidnight) {
@@ -829,9 +413,9 @@ public class Watchdog extends Thread {
if (!waitedHalf) {
// We've waited half the deadlock-detection interval. Pull a stack
// trace and wait another half.
- ArrayList pids = new ArrayList();
+ ArrayList<Integer> pids = new ArrayList<Integer>();
pids.add(Process.myPid());
- File stack = ActivityManagerService.dumpStackTraces(true, pids);
+ ActivityManagerService.dumpStackTraces(true, pids, null, null);
waitedHalf = true;
continue;
}
@@ -841,15 +425,16 @@ public class Watchdog extends Thread {
// First collect stack traces from all threads of the system process.
// Then kill this process so that the system will restart.
- String name = (mCurrentMonitor != null) ? mCurrentMonitor.getClass().getName() : "null";
+ String name = (mCurrentMonitor != null) ?
+ mCurrentMonitor.getClass().getName() : "null";
EventLog.writeEvent(EventLogTags.WATCHDOG, name);
- ArrayList pids = new ArrayList();
+ ArrayList<Integer> pids = new ArrayList<Integer>();
pids.add(Process.myPid());
if (mPhonePid > 0) pids.add(mPhonePid);
// Pass !waitedHalf so that just in case we somehow wind up here without having
// dumped the halfway stacks, we properly re-initialize the trace file.
- File stack = ActivityManagerService.dumpStackTraces(!waitedHalf, pids);
+ File stack = ActivityManagerService.dumpStackTraces(!waitedHalf, pids, null, null);
// Give some extra time to make sure the stack traces get written.
// The system's been hanging for a minute, another second or two won't hurt much.
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 8d6ad93..c1e9965 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -63,6 +63,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.Slog;
import android.text.TextUtils;
@@ -116,6 +117,8 @@ public class WifiService extends IWifiManager.Stub {
private final LockList mLocks = new LockList();
// some wifi lock statistics
+ private int mFullHighPerfLocksAcquired;
+ private int mFullHighPerfLocksReleased;
private int mFullLocksAcquired;
private int mFullLocksReleased;
private int mScanLocksAcquired;
@@ -142,7 +145,7 @@ public class WifiService extends IWifiManager.Stub {
*/
private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
- private static final String WAKELOCK_TAG = "WifiService";
+ private static final String WAKELOCK_TAG = "*wifi*";
/**
* The maximum amount of time to hold the wake lock after a disconnect
@@ -169,6 +172,8 @@ public class WifiService extends IWifiManager.Stub {
private static final int MESSAGE_START_ACCESS_POINT = 6;
private static final int MESSAGE_STOP_ACCESS_POINT = 7;
private static final int MESSAGE_SET_CHANNELS = 8;
+ private static final int MESSAGE_ENABLE_NETWORKS = 9;
+ private static final int MESSAGE_START_SCAN = 10;
private final WifiHandler mWifiHandler;
@@ -185,6 +190,12 @@ public class WifiService extends IWifiManager.Stub {
private static final int SCAN_RESULT_BUFFER_SIZE = 512;
private boolean mNeedReconfig;
+ /**
+ * Temporary for computing UIDS that are responsible for starting WIFI.
+ * Protected by mWifiStateTracker lock.
+ */
+ private final WorkSource mTmpWorkSource = new WorkSource();
+
/*
* Last UID that asked to enable WIFI.
*/
@@ -375,23 +386,12 @@ public class WifiService extends IWifiManager.Stub {
/**
* see {@link android.net.wifi.WifiManager#startScan()}
- * @return {@code true} if the operation succeeds
*/
- public boolean startScan(boolean forceActive) {
+ public void startScan(boolean forceActive) {
enforceChangePermission();
+ if (mWifiHandler == null) return;
- switch (mWifiStateTracker.getSupplicantState()) {
- case DISCONNECTED:
- case INACTIVE:
- case SCANNING:
- case DORMANT:
- break;
- default:
- mWifiStateTracker.setScanResultHandling(
- WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
- break;
- }
- return mWifiStateTracker.scan(forceActive);
+ Message.obtain(mWifiHandler, MESSAGE_START_SCAN, forceActive ? 1 : 0, 0).sendToTarget();
}
/**
@@ -525,9 +525,9 @@ public class WifiService extends IWifiManager.Stub {
long ident = Binder.clearCallingIdentity();
try {
if (wifiState == WIFI_STATE_ENABLED) {
- mBatteryStats.noteWifiOn(uid);
+ mBatteryStats.noteWifiOn();
} else if (wifiState == WIFI_STATE_DISABLED) {
- mBatteryStats.noteWifiOff(uid);
+ mBatteryStats.noteWifiOff();
}
} catch (RemoteException e) {
} finally {
@@ -631,6 +631,7 @@ public class WifiService extends IWifiManager.Stub {
}
public WifiConfiguration getWifiApConfiguration() {
+ enforceAccessPermission();
final ContentResolver cr = mContext.getContentResolver();
WifiConfiguration wifiConfig = new WifiConfiguration();
int authType;
@@ -648,7 +649,8 @@ public class WifiService extends IWifiManager.Stub {
}
}
- private void persistApConfiguration(WifiConfiguration wifiConfig) {
+ public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
+ enforceChangePermission();
final ContentResolver cr = mContext.getContentResolver();
boolean isWpa;
if (wifiConfig == null)
@@ -679,9 +681,9 @@ public class WifiService extends IWifiManager.Stub {
/* Configuration changed on a running access point */
if(enable && (wifiConfig != null)) {
try {
- persistApConfiguration(wifiConfig);
nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
SOFTAP_IFACE);
+ setWifiApConfiguration(wifiConfig);
return true;
} catch(Exception e) {
Slog.e(TAG, "Exception in nwService during AP restart");
@@ -717,7 +719,6 @@ public class WifiService extends IWifiManager.Stub {
wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
}
- persistApConfiguration(wifiConfig);
if (!mWifiStateTracker.loadDriver()) {
Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
@@ -734,6 +735,8 @@ public class WifiService extends IWifiManager.Stub {
return false;
}
+ setWifiApConfiguration(wifiConfig);
+
} else {
try {
@@ -781,9 +784,9 @@ public class WifiService extends IWifiManager.Stub {
long ident = Binder.clearCallingIdentity();
try {
if (wifiAPState == WIFI_AP_STATE_ENABLED) {
- mBatteryStats.noteWifiOn(uid);
+ mBatteryStats.noteWifiOn();
} else if (wifiAPState == WIFI_AP_STATE_DISABLED) {
- mBatteryStats.noteWifiOff(uid);
+ mBatteryStats.noteWifiOff();
}
} catch (RemoteException e) {
} finally {
@@ -1653,13 +1656,26 @@ public class WifiService extends IWifiManager.Stub {
Settings.System.getInt(mContext.getContentResolver(),
Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
if (action.equals(Intent.ACTION_SCREEN_ON)) {
- Slog.d(TAG, "ACTION_SCREEN_ON");
+ if (DBG) {
+ Slog.d(TAG, "ACTION_SCREEN_ON");
+ }
mAlarmManager.cancel(mIdleIntent);
mDeviceIdle = false;
mScreenOff = false;
+ // Once the screen is on, we are not keeping WIFI running
+ // because of any locks so clear that tracking immediately.
+ reportStartWorkSource();
mWifiStateTracker.enableRssiPolling(true);
+ /* DHCP or other temporary failures in the past can prevent
+ * a disabled network from being connected to, enable on screen on
+ */
+ if (mWifiStateTracker.isAnyNetworkDisabled()) {
+ sendEnableNetworksMessage();
+ }
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- Slog.d(TAG, "ACTION_SCREEN_OFF");
+ if (DBG) {
+ Slog.d(TAG, "ACTION_SCREEN_OFF");
+ }
mScreenOff = true;
mWifiStateTracker.enableRssiPolling(false);
/*
@@ -1676,22 +1692,30 @@ public class WifiService extends IWifiManager.Stub {
// as long as we would if connected (below)
// TODO - fix the race conditions and switch back to the immediate turn-off
long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
- Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
+ if (DBG) {
+ Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
+ }
mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
// // do not keep Wifi awake when screen is off if Wifi is not associated
// mDeviceIdle = true;
// updateWifiState();
} else {
long triggerTime = System.currentTimeMillis() + idleMillis;
- Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+ if (DBG) {
+ Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis
+ + "ms");
+ }
mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
}
}
/* we can return now -- there's nothing to do until we get the idle intent back */
return;
} else if (action.equals(ACTION_DEVICE_IDLE)) {
- Slog.d(TAG, "got ACTION_DEVICE_IDLE");
+ if (DBG) {
+ Slog.d(TAG, "got ACTION_DEVICE_IDLE");
+ }
mDeviceIdle = true;
+ reportStartWorkSource();
} else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
@@ -1701,11 +1725,15 @@ public class WifiService extends IWifiManager.Stub {
* the already-set timer.
*/
int pluggedType = intent.getIntExtra("plugged", 0);
- Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
+ if (DBG) {
+ Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
+ }
if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
!shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
long triggerTime = System.currentTimeMillis() + idleMillis;
- Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+ if (DBG) {
+ Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+ }
mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
mPluggedType = pluggedType;
return;
@@ -1779,8 +1807,8 @@ public class WifiService extends IWifiManager.Stub {
msg.sendToTarget();
}
- private void sendStartMessage(boolean scanOnlyMode) {
- Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
+ private void sendStartMessage(int lockMode) {
+ Message.obtain(mWifiHandler, MESSAGE_START_WIFI, lockMode, 0).sendToTarget();
}
private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
@@ -1789,6 +1817,23 @@ public class WifiService extends IWifiManager.Stub {
uid, 0, wifiConfig).sendToTarget();
}
+ private void sendEnableNetworksMessage() {
+ Message.obtain(mWifiHandler, MESSAGE_ENABLE_NETWORKS).sendToTarget();
+ }
+
+ private void reportStartWorkSource() {
+ synchronized (mWifiStateTracker) {
+ mTmpWorkSource.clear();
+ if (mDeviceIdle) {
+ for (int i=0; i<mLocks.mList.size(); i++) {
+ mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
+ }
+ }
+ mWifiStateTracker.updateBatteryWorkSourceLocked(mTmpWorkSource);
+ sWakeLock.setWorkSource(mTmpWorkSource);
+ }
+ }
+
private void updateWifiState() {
// send a message so it's all serialized
Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
@@ -1797,13 +1842,21 @@ public class WifiService extends IWifiManager.Stub {
private void doUpdateWifiState() {
boolean wifiEnabled = getPersistedWifiEnabled();
boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
- boolean lockHeld = mLocks.hasLocks();
- int strongestLockMode;
+
+ boolean lockHeld;
+ synchronized (mLocks) {
+ lockHeld = mLocks.hasLocks();
+ }
+
+ int strongestLockMode = WifiManager.WIFI_MODE_FULL;
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
- if (mDeviceIdle && lockHeld) {
+
+ if (lockHeld) {
strongestLockMode = mLocks.getStrongestLockMode();
- } else {
+ }
+ /* If device is not idle, lockmode cannot be scan only */
+ if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
strongestLockMode = WifiManager.WIFI_MODE_FULL;
}
@@ -1824,7 +1877,7 @@ public class WifiService extends IWifiManager.Stub {
sWakeLock.acquire();
sendEnableMessage(true, false, mLastEnableUid);
sWakeLock.acquire();
- sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
+ sendStartMessage(strongestLockMode);
} else if (!mWifiStateTracker.isDriverStopped()) {
int wakeLockTimeout =
Settings.Secure.getInt(
@@ -1902,8 +1955,11 @@ public class WifiService extends IWifiManager.Stub {
break;
case MESSAGE_START_WIFI:
- mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
+ reportStartWorkSource();
+ mWifiStateTracker.setScanOnlyMode(msg.arg1 == WifiManager.WIFI_MODE_SCAN_ONLY);
mWifiStateTracker.restart();
+ mWifiStateTracker.setHighPerfMode(msg.arg1 ==
+ WifiManager.WIFI_MODE_FULL_HIGH_PERF);
sWakeLock.release();
break;
@@ -1946,6 +2002,25 @@ public class WifiService extends IWifiManager.Stub {
setNumAllowedChannelsBlocking(msg.arg1, msg.arg2 == 1);
break;
+ case MESSAGE_ENABLE_NETWORKS:
+ mWifiStateTracker.enableAllNetworks(getConfiguredNetworks());
+ break;
+
+ case MESSAGE_START_SCAN:
+ boolean forceActive = (msg.arg1 == 1);
+ switch (mWifiStateTracker.getSupplicantState()) {
+ case DISCONNECTED:
+ case INACTIVE:
+ case SCANNING:
+ case DORMANT:
+ break;
+ default:
+ mWifiStateTracker.setScanResultHandling(
+ WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
+ break;
+ }
+ mWifiStateTracker.scan(forceActive);
+ break;
}
}
}
@@ -1983,8 +2058,10 @@ public class WifiService extends IWifiManager.Stub {
}
pw.println();
pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
+ mFullHighPerfLocksAcquired + " full high perf, " +
mScanLocksAcquired + " scan");
pw.println("Locks released: " + mFullLocksReleased + " full, " +
+ mFullHighPerfLocksReleased + " full high perf, " +
mScanLocksReleased + " scan");
pw.println();
pw.println("Locks held:");
@@ -2009,8 +2086,8 @@ public class WifiService extends IWifiManager.Stub {
}
private class WifiLock extends DeathRecipient {
- WifiLock(int lockMode, String tag, IBinder binder) {
- super(lockMode, tag, binder);
+ WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
+ super(lockMode, tag, binder, ws);
}
public void binderDied() {
@@ -2039,11 +2116,15 @@ public class WifiService extends IWifiManager.Stub {
if (mList.isEmpty()) {
return WifiManager.WIFI_MODE_FULL;
}
- for (WifiLock l : mList) {
- if (l.mMode == WifiManager.WIFI_MODE_FULL) {
- return WifiManager.WIFI_MODE_FULL;
- }
+
+ if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
+ return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
}
+
+ if (mFullLocksAcquired > mFullLocksReleased) {
+ return WifiManager.WIFI_MODE_FULL;
+ }
+
return WifiManager.WIFI_MODE_SCAN_ONLY;
}
@@ -2080,42 +2161,126 @@ public class WifiService extends IWifiManager.Stub {
}
}
- public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
+ void enforceWakeSourcePermission(int uid, int pid) {
+ if (uid == Process.myUid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+ pid, uid, null);
+ }
+
+ public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
+ if (lockMode != WifiManager.WIFI_MODE_FULL &&
+ lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
+ lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
+ Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
+ if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
return false;
}
- WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws != null) {
+ enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
+ }
+ if (ws == null) {
+ ws = new WorkSource(Binder.getCallingUid());
+ }
+ WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
synchronized (mLocks) {
return acquireWifiLockLocked(wifiLock);
}
}
+ private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
+ switch(wifiLock.mMode) {
+ case WifiManager.WIFI_MODE_FULL:
+ mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+ break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ /* Treat high power as a full lock for battery stats */
+ mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+ break;
+ case WifiManager.WIFI_MODE_SCAN_ONLY:
+ mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+ break;
+ }
+ }
+
+ private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
+ switch(wifiLock.mMode) {
+ case WifiManager.WIFI_MODE_FULL:
+ mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
+ break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ /* Treat high power as a full lock for battery stats */
+ mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
+ break;
+ case WifiManager.WIFI_MODE_SCAN_ONLY:
+ mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource);
+ break;
+ }
+ }
+
private boolean acquireWifiLockLocked(WifiLock wifiLock) {
- Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
+ if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
mLocks.addLock(wifiLock);
- int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
+ noteAcquireWifiLock(wifiLock);
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
++mFullLocksAcquired;
- mBatteryStats.noteFullWifiLockAcquired(uid);
+ break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ ++mFullHighPerfLocksAcquired;
break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksAcquired;
- mBatteryStats.noteScanWifiLockAcquired(uid);
break;
}
+
+ // Be aggressive about adding new locks into the accounted state...
+ // we want to over-report rather than under-report.
+ reportStartWorkSource();
+
+ updateWifiState();
+ return true;
} catch (RemoteException e) {
+ return false;
} finally {
Binder.restoreCallingIdentity(ident);
}
+ }
- updateWifiState();
- return true;
+ public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLocks) {
+ int index = mLocks.findLockByBinder(lock);
+ if (index < 0) {
+ throw new IllegalArgumentException("Wifi lock not active");
+ }
+ WifiLock wl = mLocks.mList.get(index);
+ noteReleaseWifiLock(wl);
+ wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
+ noteAcquireWifiLock(wl);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
public boolean releaseWifiLock(IBinder lock) {
@@ -2130,31 +2295,35 @@ public class WifiService extends IWifiManager.Stub {
WifiLock wifiLock = mLocks.removeLock(lock);
- Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
+ if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
hadLock = (wifiLock != null);
- if (hadLock) {
- int uid = Binder.getCallingUid();
- long ident = Binder.clearCallingIdentity();
- try {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (hadLock) {
+ noteAcquireWifiLock(wifiLock);
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
++mFullLocksReleased;
- mBatteryStats.noteFullWifiLockReleased(uid);
+ break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ ++mFullHighPerfLocksReleased;
break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksReleased;
- mBatteryStats.noteScanWifiLockReleased(uid);
break;
}
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
}
+
+ // TODO - should this only happen if you hadLock?
+ updateWifiState();
+
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- // TODO - should this only happen if you hadLock?
- updateWifiState();
+
return hadLock;
}
@@ -2163,12 +2332,14 @@ public class WifiService extends IWifiManager.Stub {
String mTag;
int mMode;
IBinder mBinder;
+ WorkSource mWorkSource;
- DeathRecipient(int mode, String tag, IBinder binder) {
+ DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
super();
mTag = tag;
mMode = mode;
mBinder = binder;
+ mWorkSource = ws;
try {
mBinder.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -2183,7 +2354,7 @@ public class WifiService extends IWifiManager.Stub {
private class Multicaster extends DeathRecipient {
Multicaster(String tag, IBinder binder) {
- super(Binder.getCallingUid(), tag, binder);
+ super(Binder.getCallingUid(), tag, binder, null);
}
public void binderDied() {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index dd4aeab..9ca53c6 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -16,11 +16,6 @@
package com.android.server;
-import static android.os.LocalPowerManager.CHEEK_EVENT;
-import static android.os.LocalPowerManager.OTHER_EVENT;
-import static android.os.LocalPowerManager.TOUCH_EVENT;
-import static android.os.LocalPowerManager.LONG_TOUCH_EVENT;
-import static android.os.LocalPowerManager.TOUCH_UP_EVENT;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
@@ -48,7 +43,6 @@ import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.WindowManagerPolicyThread;
-import com.android.server.KeyInputQueue.QueuedEvent;
import com.android.server.am.BatteryStatsService;
import android.Manifest;
@@ -63,17 +57,20 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
-import android.os.LatencyTimer;
import android.os.LocalPowerManager;
import android.os.Looper;
import android.os.Message;
@@ -93,34 +90,42 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.TypedValue;
import android.view.Display;
import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
import android.view.IApplicationToken;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.RawInputEvent;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
+import android.view.Surface.OutOfResourcesException;
import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
+import java.io.BufferedReader;
import java.io.BufferedWriter;
+import java.io.DataInputStream;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
@@ -135,7 +140,7 @@ import java.util.List;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback {
+ implements Watchdog.Monitor {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
@@ -156,16 +161,11 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean DEBUG_FREEZE = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
- static final boolean MEASURE_LATENCY = false;
- static private LatencyTimer lt;
static final boolean PROFILE_ORIENTATION = false;
static final boolean BLUR = true;
static final boolean localLOGV = DEBUG;
- /** How long to wait for subsequent key repeats, in milliseconds */
- static final int KEY_REPEAT_DELAY = 50;
-
/** How much to multiply the policy's type layer, to reserve room
* for multiple windows of the same type and Z-ordering adjustment
* with TYPE_LAYER_OFFSET. */
@@ -198,35 +198,19 @@ public class WindowManagerService extends IWindowManager.Stub
/** Adjustment to time to perform a dim, to make it more dramatic.
*/
static final int DIM_DURATION_MULTIPLIER = 6;
-
- static final int INJECT_FAILED = 0;
- static final int INJECT_SUCCEEDED = 1;
- static final int INJECT_NO_PERMISSION = -1;
+
+ // Maximum number of milliseconds to wait for input event injection.
+ // FIXME is this value reasonable?
+ private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
+
+ // Default input dispatching timeout in nanoseconds.
+ private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
static final int UPDATE_FOCUS_NORMAL = 0;
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
- /** The minimum time between dispatching touch events. */
- int mMinWaitTimeBetweenTouchEvents = 1000 / 35;
-
- // Last touch event time
- long mLastTouchEventTime = 0;
-
- // Last touch event type
- int mLastTouchEventType = OTHER_EVENT;
-
- // Time to wait before calling useractivity again. This saves CPU usage
- // when we get a flood of touch events.
- static final int MIN_TIME_BETWEEN_USERACTIVITIES = 1000;
-
- // Last time we call user activity
- long mLastUserActivityCallTime = 0;
-
- // Last time we updated battery stats
- long mLastBatteryStatsCallTime = 0;
-
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
@@ -349,7 +333,7 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Z-ordered (bottom-most first) list of all Window objects.
*/
- final ArrayList mWindows = new ArrayList();
+ final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
/**
* Windows that are being resized. Used so we can tell the client about
@@ -386,6 +370,7 @@ public class WindowManagerService extends IWindowManager.Stub
private DimAnimator mDimAnimator = null;
Surface mBlurSurface;
boolean mBlurShown;
+ Watermark mWatermark;
int mTransactionSequence = 0;
@@ -442,8 +427,6 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>();
final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>();
- //flag to detect fat touch events
- boolean mFatTouch = false;
Display mDisplay;
H mH = new H();
@@ -477,7 +460,6 @@ public class WindowManagerService extends IWindowManager.Stub
float mLastWallpaperY = -1;
float mLastWallpaperXStep = -1;
float mLastWallpaperYStep = -1;
- boolean mSendingPointersToWallpaper = false;
// This is set when we are waiting for a wallpaper to tell us it is done
// changing its scroll position.
WindowState mWaitingOnWallpaper;
@@ -495,12 +477,11 @@ public class WindowManagerService extends IWindowManager.Stub
float mWindowAnimationScale = 1.0f;
float mTransitionAnimationScale = 1.0f;
- final KeyWaiter mKeyWaiter = new KeyWaiter();
- final KeyQ mQueue;
- final InputDispatcherThread mInputThread;
+ final InputManager mInputManager;
// Who is holding the screen on.
Session mHoldingScreenOn;
+ PowerManager.WakeLock mHoldingScreenWakeLock;
boolean mTurnOnScreen;
@@ -511,8 +492,14 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mInTouchMode = false;
private ViewServer mViewServer;
+ private ArrayList<WindowChangeListener> mWindowChangeListeners =
+ new ArrayList<WindowChangeListener>();
+ private boolean mWindowsChanged = false;
- final Rect mTempRect = new Rect();
+ public interface WindowChangeListener {
+ public void windowsChanged();
+ public void focusChanged();
+ }
final Configuration mTempConfiguration = new Configuration();
int mScreenLayout = Configuration.SCREENLAYOUT_SIZE_UNDEFINED;
@@ -561,6 +548,7 @@ public class WindowManagerService extends IWindowManager.Stub
mHaveInputMethods);
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_DISPLAY);
+ android.os.Process.setCanSelfBackground(false);
synchronized (this) {
mService = s;
@@ -596,6 +584,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ android.os.Process.setCanSelfBackground(false);
mPolicy.init(mContext, mService, mPM);
synchronized (this) {
@@ -609,10 +598,6 @@ public class WindowManagerService extends IWindowManager.Stub
private WindowManagerService(Context context, PowerManagerService pm,
boolean haveInputMethods) {
- if (MEASURE_LATENCY) {
- lt = new LatencyTimer(100, 1000);
- }
-
mContext = context;
mHaveInputMethods = haveInputMethods;
mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -639,20 +624,11 @@ public class WindowManagerService extends IWindowManager.Stub
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter);
- int max_events_per_sec = 35;
- try {
- max_events_per_sec = Integer.parseInt(SystemProperties
- .get("windowsmgr.max_events_per_sec"));
- if (max_events_per_sec < 1) {
- max_events_per_sec = 35;
- }
- } catch (NumberFormatException e) {
- }
- mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
-
- mQueue = new KeyQ();
+ mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+ "KEEP_SCREEN_ON_FLAG");
+ mHoldingScreenWakeLock.setReferenceCounted(false);
- mInputThread = new InputDispatcherThread();
+ mInputManager = new InputManager(context, this);
PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
thr.start();
@@ -666,8 +642,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- mInputThread.start();
- mQueue.start();
+ mInputManager.start();
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
@@ -688,33 +663,35 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- private void placeWindowAfter(Object pos, WindowState window) {
+ private void placeWindowAfter(WindowState pos, WindowState window) {
final int i = mWindows.indexOf(pos);
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
TAG, "Adding window " + window + " at "
+ (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
mWindows.add(i+1, window);
+ mWindowsChanged = true;
}
- private void placeWindowBefore(Object pos, WindowState window) {
+ private void placeWindowBefore(WindowState pos, WindowState window) {
final int i = mWindows.indexOf(pos);
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
TAG, "Adding window " + window + " at "
+ i + " of " + mWindows.size() + " (before " + pos + ")");
mWindows.add(i, window);
+ mWindowsChanged = true;
}
//This method finds out the index of a window that has the same app token as
//win. used for z ordering the windows in mWindows
private int findIdxBasedOnAppTokens(WindowState win) {
//use a local variable to cache mWindows
- ArrayList localmWindows = mWindows;
+ ArrayList<WindowState> localmWindows = mWindows;
int jmax = localmWindows.size();
if(jmax == 0) {
return -1;
}
for(int j = (jmax-1); j >= 0; j--) {
- WindowState wentry = (WindowState)localmWindows.get(j);
+ WindowState wentry = localmWindows.get(j);
if(wentry.mAppToken == win.mAppToken) {
return j;
}
@@ -725,7 +702,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
- final ArrayList localmWindows = mWindows;
+ final ArrayList<WindowState> localmWindows = mWindows;
final int N = localmWindows.size();
final WindowState attached = win.mAttachedWindow;
@@ -759,6 +736,7 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Adding window " + win + " at "
+ (newIdx+1) + " of " + N);
localmWindows.add(newIdx+1, win);
+ mWindowsChanged = true;
}
}
}
@@ -769,7 +747,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Figure out where the window should go, based on the
// order of applications.
final int NA = mAppTokens.size();
- Object pos = null;
+ WindowState pos = null;
for (i=NA-1; i>=0; i--) {
AppWindowToken t = mAppTokens.get(i);
if (t == token) {
@@ -789,8 +767,7 @@ public class WindowManagerService extends IWindowManager.Stub
// we need to look some more.
if (pos != null) {
// Move behind any windows attached to this one.
- WindowToken atoken =
- mTokenMap.get(((WindowState)pos).mClient.asBinder());
+ WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
final int NC = atoken.windows.size();
if (NC > 0) {
@@ -816,8 +793,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (pos != null) {
// Move in front of any windows attached to this
// one.
- WindowToken atoken =
- mTokenMap.get(((WindowState)pos).mClient.asBinder());
+ WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
final int NC = atoken.windows.size();
if (NC > 0) {
@@ -832,7 +808,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Just search for the start of this layer.
final int myLayer = win.mBaseLayer;
for (i=0; i<N; i++) {
- WindowState w = (WindowState)localmWindows.get(i);
+ WindowState w = localmWindows.get(i);
if (w.mBaseLayer > myLayer) {
break;
}
@@ -841,6 +817,7 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Adding window " + win + " at "
+ i + " of " + N);
localmWindows.add(i, win);
+ mWindowsChanged = true;
}
}
}
@@ -848,7 +825,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Figure out where window should go, based on layer.
final int myLayer = win.mBaseLayer;
for (i=N-1; i>=0; i--) {
- if (((WindowState)localmWindows.get(i)).mBaseLayer <= myLayer) {
+ if (localmWindows.get(i).mBaseLayer <= myLayer) {
i++;
break;
}
@@ -858,6 +835,7 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Adding window " + win + " at "
+ i + " of " + N);
localmWindows.add(i, win);
+ mWindowsChanged = true;
}
if (addToToken) {
token.windows.add(tokenWindowsPos, win);
@@ -930,13 +908,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
- final ArrayList localmWindows = mWindows;
+ final ArrayList<WindowState> localmWindows = mWindows;
final int N = localmWindows.size();
WindowState w = null;
int i = N;
while (i > 0) {
i--;
- w = (WindowState)localmWindows.get(i);
+ w = localmWindows.get(i);
//Slog.i(TAG, "Checking window @" + i + " " + w + " fl=0x"
// + Integer.toHexString(w.mAttrs.flags));
@@ -951,7 +929,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (!willMove
&& w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& i > 0) {
- WindowState wb = (WindowState)localmWindows.get(i-1);
+ WindowState wb = localmWindows.get(i-1);
if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
i--;
w = wb;
@@ -981,7 +959,7 @@ public class WindowManagerService extends IWindowManager.Stub
int pos = 0;
pos = localmWindows.indexOf(curTarget);
while (pos >= 0) {
- WindowState win = (WindowState)localmWindows.get(pos);
+ WindowState win = localmWindows.get(pos);
if (win.mAppToken != token) {
break;
}
@@ -1066,6 +1044,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(
TAG, "Adding input method window " + win + " at " + pos);
mWindows.add(pos, win);
+ mWindowsChanged = true;
moveInputMethodDialogsLocked(pos+1);
return;
}
@@ -1085,7 +1064,7 @@ public class WindowManagerService extends IWindowManager.Stub
int wi = imw.mChildWindows.size();
while (wi > 0) {
wi--;
- WindowState cw = (WindowState)imw.mChildWindows.get(wi);
+ WindowState cw = imw.mChildWindows.get(wi);
cw.mAnimLayer = cw.mLayer + adj;
if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + cw
+ " anim layer: " + cw.mAnimLayer);
@@ -1107,10 +1086,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (wpos < interestingPos) interestingPos--;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing at " + wpos + ": " + win);
mWindows.remove(wpos);
+ mWindowsChanged = true;
int NC = win.mChildWindows.size();
while (NC > 0) {
NC--;
- WindowState cw = (WindowState)win.mChildWindows.get(NC);
+ WindowState cw = win.mChildWindows.get(NC);
int cpos = mWindows.indexOf(cw);
if (cpos >= 0) {
if (cpos < interestingPos) interestingPos--;
@@ -1133,6 +1113,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos
+ ": " + win);
mWindows.remove(wpos);
+ mWindowsChanged = true;
reAddWindowLocked(wpos, win);
}
}
@@ -1161,7 +1142,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (pos >= 0) {
final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
if (pos < mWindows.size()) {
- WindowState wp = (WindowState)mWindows.get(pos);
+ WindowState wp = mWindows.get(pos);
if (wp == mInputMethodWindow) {
pos++;
}
@@ -1205,14 +1186,14 @@ public class WindowManagerService extends IWindowManager.Stub
// located here, and contiguous.
final int N = mWindows.size();
WindowState firstImWin = imPos < N
- ? (WindowState)mWindows.get(imPos) : null;
+ ? mWindows.get(imPos) : null;
// Figure out the actual input method window that should be
// at the bottom of their stack.
WindowState baseImWin = imWin != null
? imWin : mInputMethodDialogs.get(0);
if (baseImWin.mChildWindows.size() > 0) {
- WindowState cw = (WindowState)baseImWin.mChildWindows.get(0);
+ WindowState cw = baseImWin.mChildWindows.get(0);
if (cw.mSubLayer < 0) baseImWin = cw;
}
@@ -1221,7 +1202,7 @@ public class WindowManagerService extends IWindowManager.Stub
// First find the top IM window.
int pos = imPos+1;
while (pos < N) {
- if (!((WindowState)mWindows.get(pos)).mIsImWindow) {
+ if (!(mWindows.get(pos)).mIsImWindow) {
break;
}
pos++;
@@ -1229,7 +1210,7 @@ public class WindowManagerService extends IWindowManager.Stub
pos++;
// Now there should be no more input method windows above.
while (pos < N) {
- if (((WindowState)mWindows.get(pos)).mIsImWindow) {
+ if ((mWindows.get(pos)).mIsImWindow) {
break;
}
pos++;
@@ -1317,7 +1298,7 @@ public class WindowManagerService extends IWindowManager.Stub
// First find top-most window that has asked to be on top of the
// wallpaper; all wallpapers go behind it.
- final ArrayList localmWindows = mWindows;
+ final ArrayList<WindowState> localmWindows = mWindows;
int N = localmWindows.size();
WindowState w = null;
WindowState foundW = null;
@@ -1327,7 +1308,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = N;
while (i > 0) {
i--;
- w = (WindowState)localmWindows.get(i);
+ w = localmWindows.get(i);
if ((w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER)) {
if (topCurW == null) {
topCurW = w;
@@ -1498,7 +1479,7 @@ public class WindowManagerService extends IWindowManager.Stub
// AND any starting window associated with it, AND below the
// maximum layer the policy allows for wallpapers.
while (foundI > 0) {
- WindowState wb = (WindowState)localmWindows.get(foundI-1);
+ WindowState wb = localmWindows.get(foundI-1);
if (wb.mBaseLayer < maxLayer &&
wb.mAttachedWindow != foundW &&
wb.mAttachedWindow != foundW.mAttachedWindow &&
@@ -1523,7 +1504,7 @@ public class WindowManagerService extends IWindowManager.Stub
} else {
// Okay i is the position immediately above the wallpaper. Look at
// what is below it for later.
- foundW = foundI > 0 ? (WindowState)localmWindows.get(foundI-1) : null;
+ foundW = foundI > 0 ? localmWindows.get(foundI-1) : null;
}
if (visible) {
@@ -1582,7 +1563,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (wallpaper == foundW) {
foundI--;
foundW = foundI > 0
- ? (WindowState)localmWindows.get(foundI-1) : null;
+ ? localmWindows.get(foundI-1) : null;
continue;
}
@@ -1594,6 +1575,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at "
+ oldIndex + ": " + wallpaper);
localmWindows.remove(oldIndex);
+ mWindowsChanged = true;
if (oldIndex < foundI) {
foundI--;
}
@@ -1605,6 +1587,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " from " + oldIndex + " to " + foundI);
localmWindows.add(foundI, wallpaper);
+ mWindowsChanged = true;
changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
}
}
@@ -1794,74 +1777,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
-
- void sendPointerToWallpaperLocked(WindowState srcWin,
- MotionEvent pointer, long eventTime) {
- int curTokenIndex = mWallpaperTokens.size();
- while (curTokenIndex > 0) {
- curTokenIndex--;
- WindowToken token = mWallpaperTokens.get(curTokenIndex);
- int curWallpaperIndex = token.windows.size();
- while (curWallpaperIndex > 0) {
- curWallpaperIndex--;
- WindowState wallpaper = token.windows.get(curWallpaperIndex);
- if ((wallpaper.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- continue;
- }
- try {
- MotionEvent ev = MotionEvent.obtainNoHistory(pointer);
- if (srcWin != null) {
- ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left,
- srcWin.mFrame.top-wallpaper.mFrame.top);
- } else {
- ev.offsetLocation(-wallpaper.mFrame.left, -wallpaper.mFrame.top);
- }
- switch (pointer.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mSendingPointersToWallpaper = true;
- break;
- case MotionEvent.ACTION_UP:
- mSendingPointersToWallpaper = false;
- break;
- }
- wallpaper.mClient.dispatchPointer(ev, eventTime, false);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure sending pointer to wallpaper", e);
- }
- }
- }
- }
-
- void dispatchPointerElsewhereLocked(WindowState srcWin, WindowState relWin,
- MotionEvent pointer, long eventTime, boolean skipped) {
- if (relWin != null) {
- mPolicy.dispatchedPointerEventLw(pointer, relWin.mFrame.left, relWin.mFrame.top);
- } else {
- mPolicy.dispatchedPointerEventLw(pointer, 0, 0);
- }
-
- // If we sent an initial down to the wallpaper, then continue
- // sending events until the final up.
- if (mSendingPointersToWallpaper) {
- if (skipped) {
- Slog.i(TAG, "Sending skipped pointer to wallpaper!");
- }
- sendPointerToWallpaperLocked(relWin, pointer, eventTime);
-
- // If we are on top of the wallpaper, then the wallpaper also
- // gets to see this movement.
- } else if (srcWin != null
- && pointer.getAction() == MotionEvent.ACTION_DOWN
- && mWallpaperTarget == srcWin
- && srcWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
- sendPointerToWallpaperLocked(relWin, pointer, eventTime);
- }
- }
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
- Rect outContentInsets) {
+ Rect outContentInsets, InputChannel outInputChannel) {
int res = mPolicy.checkAddPermission(attrs);
if (res != WindowManagerImpl.ADD_OKAY) {
return res;
@@ -1880,7 +1799,7 @@ public class WindowManagerService extends IWindowManager.Stub
mDisplay = wm.getDefaultDisplay();
mInitialDisplayWidth = mDisplay.getWidth();
mInitialDisplayHeight = mDisplay.getHeight();
- mQueue.setDisplay(mDisplay);
+ mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight);
reportNewConfig = true;
}
@@ -1973,6 +1892,15 @@ public class WindowManagerService extends IWindowManager.Stub
if (res != WindowManagerImpl.ADD_OKAY) {
return res;
}
+
+ if (outInputChannel != null) {
+ String name = win.makeInputChannelName();
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
+ win.mInputChannel = inputChannels[0];
+ inputChannels[1].transferToBinderOutParameter(outInputChannel);
+
+ mInputManager.registerInputChannel(win.mInputChannel);
+ }
// From now on, no exceptions or errors allowed!
@@ -2026,8 +1954,8 @@ public class WindowManagerService extends IWindowManager.Stub
boolean focusChanged = false;
if (win.canReceiveKeys()) {
- if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS))
- == true) {
+ focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);
+ if (focusChanged) {
imMayMove = false;
}
}
@@ -2043,10 +1971,9 @@ public class WindowManagerService extends IWindowManager.Stub
//dump();
if (focusChanged) {
- if (mCurrentFocus != null) {
- mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
- }
+ finishUpdateFocusedWindowAfterAssignLayersLocked();
}
+
if (localLOGV) Slog.v(
TAG, "New client " + client.asBinder()
+ ": window=" + win);
@@ -2088,6 +2015,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ ", surface=" + win.mSurface);
final long origId = Binder.clearCallingIdentity();
+
+ win.disposeInputChannel();
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Remove " + win + ": mSurface=" + win.mSurface
@@ -2148,12 +2077,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void removeWindowInnerLocked(Session session, WindowState win) {
- mKeyWaiter.finishedKey(session, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releaseMotionTarget(win);
- mKeyWaiter.releasePendingPointerLocked(win.mSession);
- mKeyWaiter.releasePendingTrackballLocked(win.mSession);
-
win.mRemoved = true;
if (mInputMethodTarget == win) {
@@ -2171,6 +2094,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWindowMap.remove(win.mClient.asBinder());
mWindows.remove(win);
+ mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
if (mInputMethodWindow == win) {
@@ -2231,6 +2155,8 @@ public class WindowManagerService extends IWindowManager.Stub
win.mAppToken.updateReportedVisibilityLocked();
}
}
+
+ mInputMonitor.updateInputWindowsLw();
}
private static void logSurface(WindowState w, String msg, RuntimeException where) {
@@ -2474,6 +2400,8 @@ public class WindowManagerService extends IWindowManager.Stub
outSurface.release();
}
} catch (Exception e) {
+ mInputMonitor.updateInputWindowsLw();
+
Slog.w(TAG, "Exception thrown when creating surface for client "
+ client + " (" + win.mAttrs.getTitle() + ")",
e);
@@ -2520,8 +2448,6 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win, transit, false)) {
focusMayChange = true;
win.mExiting = true;
- mKeyWaiter.finishedKey(session, client, true,
- KeyWaiter.RETURN_NOTHING);
} else if (win.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
@@ -2616,6 +2542,8 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
inTouchMode = mInTouchMode;
+
+ mInputMonitor.updateInputWindowsLw();
}
if (configChanged) {
@@ -2982,8 +2910,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.isVisibleNow()) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
changed = true;
}
}
@@ -3001,6 +2927,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ mInputMonitor.updateInputWindowsLw();
} else {
Slog.w(TAG, "Attempted to remove non-existing token: " + token);
}
@@ -3014,6 +2941,20 @@ public class WindowManagerService extends IWindowManager.Stub
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
+
+ // Get the dispatching timeout here while we are not holding any locks so that it
+ // can be cached by the AppWindowToken. The timeout value is used later by the
+ // input dispatcher in code that does hold locks. If we did not cache the value
+ // here we would run the chance of introducing a deadlock between the window manager
+ // (which holds locks while updating the input dispatcher state) and the activity manager
+ // (which holds locks while querying the application token).
+ long inputDispatchingTimeoutNanos;
+ try {
+ inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L;
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Could not get dispatching timeout.", ex);
+ inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3022,6 +2963,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
wtoken = new AppWindowToken(token);
+ wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
wtoken.groupId = groupId;
wtoken.appFullscreen = fullscreen;
wtoken.requestedOrientation = requestedOrientation;
@@ -3057,7 +2999,7 @@ public class WindowManagerService extends IWindowManager.Stub
public int getOrientationFromWindowsLocked() {
int pos = mWindows.size() - 1;
while (pos >= 0) {
- WindowState wtoken = (WindowState) mWindows.get(pos);
+ WindowState wtoken = mWindows.get(pos);
pos--;
if (wtoken.mAppToken != null) {
// We hit an application window. so the orientation will be determined by the
@@ -3125,11 +3067,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
// If this application has requested an explicit orientation,
// then use it.
- if (or == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ||
- or == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ||
- or == ActivityInfo.SCREEN_ORIENTATION_SENSOR ||
- or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR ||
- or == ActivityInfo.SCREEN_ORIENTATION_USER) {
+ if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
return or;
}
findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
@@ -3161,8 +3100,11 @@ public class WindowManagerService extends IWindowManager.Stub
} else if (currentConfig != null) {
// No obvious action we need to take, but if our current
- // state mismatches the activity maanager's, update it
+ // state mismatches the activity manager's, update it,
+ // disregarding font scale, which should remain set to
+ // the value of the previous configuration.
mTempConfiguration.setToDefaults();
+ mTempConfiguration.fontScale = currentConfig.fontScale;
if (computeNewConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
@@ -3285,7 +3227,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp);
changed = mFocusedApp != null;
mFocusedApp = null;
- mKeyWaiter.tickle();
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(null);
+ }
} else {
AppWindowToken newFocus = findAppWindowToken(token);
if (newFocus == null) {
@@ -3295,7 +3239,9 @@ public class WindowManagerService extends IWindowManager.Stub
changed = mFocusedApp != newFocus;
mFocusedApp = newFocus;
if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp);
- mKeyWaiter.tickle();
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(newFocus);
+ }
}
if (moveFocusNow && changed) {
@@ -3436,6 +3382,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Removing starting window: " + startingWindow);
mWindows.remove(startingWindow);
+ mWindowsChanged = true;
ttoken.windows.remove(startingWindow);
ttoken.allAppWindows.remove(startingWindow);
addWindowToListInOrderLocked(startingWindow, true);
@@ -3606,8 +3553,6 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
}
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
changed = true;
}
}
@@ -3635,6 +3580,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (performLayout) {
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
performLayoutAndPlaceSurfacesLocked();
+ } else {
+ mInputMonitor.updateInputWindowsLw();
}
}
}
@@ -3892,7 +3839,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken);
mFocusedApp = null;
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
- mKeyWaiter.tickle();
+ mInputMonitor.setFocusedAppLw(null);
}
} else {
Slog.w(TAG, "Attempted to remove non-existing app token: " + token);
@@ -3918,10 +3865,11 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState win = token.windows.get(i);
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing app window " + win);
mWindows.remove(win);
+ mWindowsChanged = true;
int j = win.mChildWindows.size();
while (j > 0) {
j--;
- WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ WindowState cwin = win.mChildWindows.get(j);
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Tmp removing child window " + cwin);
mWindows.remove(cwin);
@@ -3949,7 +3897,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = NW;
while (i > 0) {
i--;
- WindowState win = (WindowState)mWindows.get(i);
+ WindowState win = mWindows.get(i);
if (win.getAppToken() != null) {
return i+1;
}
@@ -3975,7 +3923,7 @@ public class WindowManagerService extends IWindowManager.Stub
int j = win.mChildWindows.size();
while (j > 0) {
j--;
- WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ WindowState cwin = win.mChildWindows.get(j);
if (cwin.mSubLayer >= 0) {
for (int pos=NW-1; pos>=0; pos--) {
if (mWindows.get(pos) == cwin) {
@@ -4003,7 +3951,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int NCW = win.mChildWindows.size();
boolean added = false;
for (int j=0; j<NCW; j++) {
- WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ WindowState cwin = win.mChildWindows.get(j);
if (!added && cwin.mSubLayer >= 0) {
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding child window at "
+ index + ": " + cwin);
@@ -4022,6 +3970,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWindows.add(index, win);
index++;
}
+ mWindowsChanged = true;
return index;
}
@@ -4286,7 +4235,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void closeSystemDialogs(String reason) {
synchronized(mWindowMap) {
for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mSurface != null) {
try {
w.mClient.closeSystemDialogs(reason);
@@ -4357,7 +4306,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getSwitchState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return KeyInputQueue.getSwitchState(sw);
+ return mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getSwitchStateForDevice(int devid, int sw) {
@@ -4365,7 +4314,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getSwitchStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return KeyInputQueue.getSwitchState(devid, sw);
+ return mInputManager.getSwitchState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getScancodeState(int sw) {
@@ -4373,7 +4322,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getScancodeStateForDevice(int devid, int sw) {
@@ -4381,7 +4330,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getScancodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getScancodeState(devid, sw);
+ return mInputManager.getScanCodeState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getTrackballScancodeState(int sw) {
@@ -4389,7 +4338,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getTrackballScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getTrackballScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL, sw);
}
public int getDPadScancodeState(int sw) {
@@ -4397,7 +4346,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getDPadScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getDPadScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_DPAD, sw);
}
public int getKeycodeState(int sw) {
@@ -4405,7 +4354,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getKeycodeStateForDevice(int devid, int sw) {
@@ -4413,7 +4362,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getKeycodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getKeycodeState(devid, sw);
+ return mInputManager.getKeyCodeState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getTrackballKeycodeState(int sw) {
@@ -4421,7 +4370,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getTrackballKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getTrackballKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_TRACKBALL, sw);
}
public int getDPadKeycodeState(int sw) {
@@ -4429,11 +4378,27 @@ public class WindowManagerService extends IWindowManager.Stub
"getDPadKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getDPadKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, sw);
}
-
+
public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
- return KeyInputQueue.hasKeys(keycodes, keyExists);
+ return mInputManager.hasKeys(-1, InputDevice.SOURCE_ANY, keycodes, keyExists);
+ }
+
+ public InputChannel monitorInput(String inputChannelName) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "monitorInput()")) {
+ throw new SecurityException("Requires READ_INPUT_STATE permission");
+ }
+ return mInputManager.monitorInput(inputChannelName);
+ }
+
+ public InputDevice getInputDevice(int deviceId) {
+ return mInputManager.getInputDevice(deviceId);
+ }
+
+ public int[] getInputDeviceIds() {
+ return mInputManager.getInputDeviceIds();
}
public void enableScreenAfterBoot() {
@@ -4470,7 +4435,7 @@ public class WindowManagerService extends IWindowManager.Stub
// have been drawn.
final int N = mWindows.size();
for (int i=0; i<N; i++) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.isVisibleLw() && !w.mObscured
&& (w.mOrientationChanging || !w.isDrawnLw())) {
return;
@@ -4578,12 +4543,12 @@ public class WindowManagerService extends IWindowManager.Stub
mLayoutNeeded = true;
startFreezingDisplayLocked();
Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags);
- mQueue.setOrientation(rotation);
+ mInputManager.setDisplayOrientation(0, rotation);
if (mDisplayEnabled) {
Surface.setOrientation(0, rotation, animFlags);
}
for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mSurface != null) {
w.mOrientationChanging = true;
}
@@ -4739,11 +4704,10 @@ public class WindowManagerService extends IWindowManager.Stub
boolean result = true;
- Object[] windows;
+ WindowState[] windows;
synchronized (mWindowMap) {
- windows = new Object[mWindows.size()];
//noinspection unchecked
- windows = mWindows.toArray(windows);
+ windows = mWindows.toArray(new WindowState[mWindows.size()]);
}
BufferedWriter out = null;
@@ -4755,7 +4719,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int count = windows.length;
for (int i = 0; i < count; i++) {
- final WindowState w = (WindowState) windows[i];
+ final WindowState w = windows[i];
out.write(Integer.toHexString(System.identityHashCode(w)));
out.write(' ');
out.append(w.mAttrs.getTitle());
@@ -4780,6 +4744,51 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
+ * Returns the focused window in the following format:
+ * windowHashCodeInHexadecimal windowName
+ *
+ * @param client The remote client to send the listing to.
+ * @return False if an error occurred, true otherwise.
+ */
+ boolean viewServerGetFocusedWindow(Socket client) {
+ if (isSystemSecure()) {
+ return false;
+ }
+
+ boolean result = true;
+
+ WindowState focusedWindow = getFocusedWindow();
+
+ BufferedWriter out = null;
+
+ // Any uncaught exception will crash the system process
+ try {
+ OutputStream clientStream = client.getOutputStream();
+ out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
+
+ if(focusedWindow != null) {
+ out.write(Integer.toHexString(System.identityHashCode(focusedWindow)));
+ out.write(' ');
+ out.append(focusedWindow.mAttrs.getTitle());
+ }
+ out.write('\n');
+ out.flush();
+ } catch (Exception e) {
+ result = false;
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ result = false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Sends a command to a target window. The result of the command, if any, will be
* written in the output stream of the specified socket.
*
@@ -4808,6 +4817,8 @@ public class WindowManagerService extends IWindowManager.Stub
Parcel data = null;
Parcel reply = null;
+ BufferedWriter out = null;
+
// Any uncaught exception will crash the system process
try {
// Find the hashcode of the window
@@ -4845,6 +4856,12 @@ public class WindowManagerService extends IWindowManager.Stub
reply.readException();
+ if (!client.isOutputShutdown()) {
+ out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
+ out.write("DONE\n");
+ out.flush();
+ }
+
} catch (Exception e) {
Slog.w(TAG, "Could not send command " + command + " with parameters " + parameters, e);
success = false;
@@ -4855,22 +4872,71 @@ public class WindowManagerService extends IWindowManager.Stub
if (reply != null) {
reply.recycle();
}
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+
+ }
+ }
}
return success;
}
+ public void addWindowChangeListener(WindowChangeListener listener) {
+ synchronized(mWindowMap) {
+ mWindowChangeListeners.add(listener);
+ }
+ }
+
+ public void removeWindowChangeListener(WindowChangeListener listener) {
+ synchronized(mWindowMap) {
+ mWindowChangeListeners.remove(listener);
+ }
+ }
+
+ private void notifyWindowsChanged() {
+ WindowChangeListener[] windowChangeListeners;
+ synchronized(mWindowMap) {
+ if(mWindowChangeListeners.isEmpty()) {
+ return;
+ }
+ windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()];
+ windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners);
+ }
+ int N = windowChangeListeners.length;
+ for(int i = 0; i < N; i++) {
+ windowChangeListeners[i].windowsChanged();
+ }
+ }
+
+ private void notifyFocusChanged() {
+ WindowChangeListener[] windowChangeListeners;
+ synchronized(mWindowMap) {
+ if(mWindowChangeListeners.isEmpty()) {
+ return;
+ }
+ windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()];
+ windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners);
+ }
+ int N = windowChangeListeners.length;
+ for(int i = 0; i < N; i++) {
+ windowChangeListeners[i].focusChanged();
+ }
+ }
+
private WindowState findWindow(int hashCode) {
if (hashCode == -1) {
return getFocusedWindow();
}
synchronized (mWindowMap) {
- final ArrayList windows = mWindows;
+ final ArrayList<WindowState> windows = mWindows;
final int count = windows.size();
for (int i = 0; i < count; i++) {
- WindowState w = (WindowState) windows.get(i);
+ WindowState w = windows.get(i);
if (System.identityHashCode(w) == hashCode) {
return w;
}
@@ -4909,7 +4975,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (mDisplay == null) {
return false;
}
- mQueue.getInputConfiguration(config);
+
+ mInputManager.getInputConfiguration(config);
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -4952,9 +5019,13 @@ public class WindowManagerService extends IWindowManager.Stub
mScreenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL
| Configuration.SCREENLAYOUT_LONG_NO;
} else {
- // Is this a large screen?
- if (longSize > 640 && shortSize >= 480) {
- // VGA or larger screens at medium density are the point
+ // What size is this screen screen?
+ if (longSize >= 800 && shortSize >= 600) {
+ // SVGA or larger screens at medium density are the point
+ // at which we consider it to be an extra large screen.
+ mScreenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ } else if (longSize >= 530 && shortSize >= 400) {
+ // SVGA or larger screens at high density are the point
// at which we consider it to be a large screen.
mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
} else {
@@ -4984,451 +5055,311 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.adjustConfigurationLw(config);
return true;
}
-
+
// -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
+
+ InputMonitor mInputMonitor = new InputMonitor();
+
+ /* Tracks the progress of input dispatch and ensures that input dispatch state
+ * is kept in sync with changes in window focus, visibility, registration, and
+ * other relevant Window Manager state transitions. */
+ final class InputMonitor {
+ // Current window with input focus for keys and other non-touch events. May be null.
+ private WindowState mInputFocus;
+
+ // When true, prevents input dispatch from proceeding until set to false again.
+ private boolean mInputDispatchFrozen;
+
+ // When true, input dispatch proceeds normally. Otherwise all events are dropped.
+ private boolean mInputDispatchEnabled = true;
- private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
- long curTime = SystemClock.uptimeMillis();
-
- if (eventType == TOUCH_EVENT || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
- if (mLastTouchEventType == eventType &&
- (curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) {
- return;
- }
- mLastUserActivityCallTime = curTime;
- mLastTouchEventType = eventType;
- }
-
- if (targetWin == null
- || targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
- mPowerManager.userActivity(curTime, false, eventType, false);
- }
- }
-
- // tells if it's a cheek event or not -- this function is stateful
- private static final int EVENT_NONE = 0;
- private static final int EVENT_UNKNOWN = 0;
- private static final int EVENT_CHEEK = 0;
- private static final int EVENT_IGNORE_DURATION = 300; // ms
- private static final float CHEEK_THRESHOLD = 0.6f;
- private int mEventState = EVENT_NONE;
- private float mEventSize;
-
- private int eventType(MotionEvent ev) {
- float size = ev.getSize();
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mEventSize = size;
- return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_EVENT;
- case MotionEvent.ACTION_UP:
- if (size > mEventSize) mEventSize = size;
- return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_UP_EVENT;
- case MotionEvent.ACTION_MOVE:
- final int N = ev.getHistorySize();
- if (size > mEventSize) mEventSize = size;
- if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
- for (int i=0; i<N; i++) {
- size = ev.getHistoricalSize(i);
- if (size > mEventSize) mEventSize = size;
- if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
- }
- if (ev.getEventTime() < ev.getDownTime() + EVENT_IGNORE_DURATION) {
- return TOUCH_EVENT;
- } else {
- return LONG_TOUCH_EVENT;
+ // Temporary list of windows information to provide to the input dispatcher.
+ private InputWindowList mTempInputWindows = new InputWindowList();
+
+ // Temporary input application object to provide to the input dispatcher.
+ private InputApplication mTempInputApplication = new InputApplication();
+
+ /* Notifies the window manager about a broken input channel.
+ *
+ * Called by the InputManager.
+ */
+ public void notifyInputChannelBroken(InputChannel inputChannel) {
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState == null) {
+ return; // irrelevant
+ }
+
+ Slog.i(TAG, "WINDOW DIED " + windowState);
+ removeWindowLocked(windowState.mSession, windowState);
}
- default:
- // not good
- return OTHER_EVENT;
- }
- }
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
- if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Slog.v(TAG,
- "dispatchPointer " + ev);
-
- if (MEASURE_LATENCY) {
- lt.sample("3 Wait for last dispatch ", System.nanoTime() - qev.whenNano);
}
-
- Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, true, false, pid, uid);
-
- if (MEASURE_LATENCY) {
- lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano);
- }
-
- int action = ev.getAction();
-
- if (action == MotionEvent.ACTION_UP) {
- // let go of our target
- mKeyWaiter.mMotionTarget = null;
- mPowerManager.logPointerUpEvent();
- } else if (action == MotionEvent.ACTION_DOWN) {
- mPowerManager.logPointerDownEvent();
- }
-
- if (targetObj == null) {
- // In this case we are either dropping the event, or have received
- // a move or up without a down. It is common to receive move
- // events in such a way, since this means the user is moving the
- // pointer without actually pressing down. All other cases should
- // be atypical, so let's log them.
- if (action != MotionEvent.ACTION_MOVE) {
- Slog.w(TAG, "No window to dispatch pointer action " + ev.getAction());
+
+ /* Notifies the window manager about an application that is not responding.
+ * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
+ *
+ * Called by the InputManager.
+ */
+ public long notifyANR(Object token, InputChannel inputChannel) {
+ AppWindowToken appWindowToken = null;
+ if (inputChannel != null) {
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState != null) {
+ Slog.i(TAG, "Input event dispatching timed out sending to "
+ + windowState.mAttrs.getTitle());
+ appWindowToken = windowState.mAppToken;
+ }
+ }
}
- synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true);
+
+ if (appWindowToken == null && token != null) {
+ appWindowToken = (AppWindowToken) token;
+ Slog.i(TAG, "Input event dispatching timed out sending to application "
+ + appWindowToken.stringName);
}
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ if (appWindowToken != null && appWindowToken.appToken != null) {
+ try {
+ // Notify the activity manager about the timeout and let it decide whether
+ // to abort dispatching or keep waiting.
+ boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
+ if (! abort) {
+ // The activity manager declined to abort dispatching.
+ // Wait a bit longer and timeout again later.
+ return appWindowToken.inputDispatchingTimeoutNanos;
+ }
+ } catch (RemoteException ex) {
+ }
}
- ev.recycle();
- return INJECT_FAILED;
+ return 0; // abort dispatching
}
- if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+
+ private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true);
- }
- if (qev != null) {
- mQueue.recycleEvent(qev);
+ return getWindowStateForInputChannelLocked(inputChannel);
}
- ev.recycle();
- return INJECT_SUCCEEDED;
}
-
- WindowState target = (WindowState)targetObj;
-
- final long eventTime = ev.getEventTime();
- final long eventTimeNano = ev.getEventTimeNano();
-
- //Slog.i(TAG, "Sending " + ev + " to " + target);
-
- if (uid != 0 && uid != target.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting pointer event from pid "
- + pid + " uid " + uid + " to window " + target
- + " owned by uid " + target.mSession.mUid);
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
+ int windowCount = mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState windowState = mWindows.get(i);
+ if (windowState.mInputChannel == inputChannel) {
+ return windowState;
}
- ev.recycle();
- return INJECT_NO_PERMISSION;
}
+
+ return null;
}
-
- if (MEASURE_LATENCY) {
- lt.sample("4 in dispatchPointer ", System.nanoTime() - eventTimeNano);
- }
-
- if ((target.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
- //target wants to ignore fat touch events
- boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev);
- //explicit flag to return without processing event further
- boolean returnFlag = false;
- if((action == MotionEvent.ACTION_DOWN)) {
- mFatTouch = false;
- if(cheekPress) {
- mFatTouch = true;
- returnFlag = true;
+
+ /* Updates the cached window information provided to the input dispatcher. */
+ public void updateInputWindowsLw() {
+ // Populate the input window list with information about all of the windows that
+ // could potentially receive input.
+ // As an optimization, we could try to prune the list of windows but this turns
+ // out to be difficult because only the native code knows for sure which window
+ // currently has touch focus.
+ final ArrayList<WindowState> windows = mWindows;
+ final int N = windows.size();
+ for (int i = N - 1; i >= 0; i--) {
+ final WindowState child = windows.get(i);
+ if (child.mInputChannel == null || child.mRemoved) {
+ // Skip this window because it cannot possibly receive input.
+ continue;
}
- } else {
- if(action == MotionEvent.ACTION_UP) {
- if(mFatTouch) {
- //earlier even was invalid doesnt matter if current up is cheekpress or not
- mFatTouch = false;
- returnFlag = true;
- } else if(cheekPress) {
- //cancel the earlier event
- ev.setAction(MotionEvent.ACTION_CANCEL);
- action = MotionEvent.ACTION_CANCEL;
+
+ final int flags = child.mAttrs.flags;
+ final int type = child.mAttrs.type;
+
+ final boolean hasFocus = (child == mInputFocus);
+ final boolean isVisible = child.isVisibleLw();
+ final boolean hasWallpaper = (child == mWallpaperTarget)
+ && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
+
+ // Add a window to our list of input windows.
+ final InputWindow inputWindow = mTempInputWindows.add();
+ inputWindow.inputChannel = child.mInputChannel;
+ inputWindow.name = child.toString();
+ inputWindow.layoutParamsFlags = flags;
+ inputWindow.layoutParamsType = type;
+ inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
+ inputWindow.visible = isVisible;
+ inputWindow.canReceiveKeys = child.canReceiveKeys();
+ inputWindow.hasFocus = hasFocus;
+ inputWindow.hasWallpaper = hasWallpaper;
+ inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindow.layer = child.mLayer;
+ inputWindow.ownerPid = child.mSession.mPid;
+ inputWindow.ownerUid = child.mSession.mUid;
+
+ final Rect frame = child.mFrame;
+ inputWindow.frameLeft = frame.left;
+ inputWindow.frameTop = frame.top;
+ inputWindow.frameRight = frame.right;
+ inputWindow.frameBottom = frame.bottom;
+
+ final Rect visibleFrame = child.mVisibleFrame;
+ inputWindow.visibleFrameLeft = visibleFrame.left;
+ inputWindow.visibleFrameTop = visibleFrame.top;
+ inputWindow.visibleFrameRight = visibleFrame.right;
+ inputWindow.visibleFrameBottom = visibleFrame.bottom;
+
+ switch (child.mTouchableInsets) {
+ default:
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ inputWindow.touchableAreaLeft = frame.left;
+ inputWindow.touchableAreaTop = frame.top;
+ inputWindow.touchableAreaRight = frame.right;
+ inputWindow.touchableAreaBottom = frame.bottom;
+ break;
+
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
+ Rect inset = child.mGivenContentInsets;
+ inputWindow.touchableAreaLeft = frame.left + inset.left;
+ inputWindow.touchableAreaTop = frame.top + inset.top;
+ inputWindow.touchableAreaRight = frame.right - inset.right;
+ inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
+ break;
}
- } else if(action == MotionEvent.ACTION_MOVE) {
- if(mFatTouch) {
- //two cases here
- //an invalid down followed by 0 or moves(valid or invalid)
- //a valid down, invalid move, more moves. want to ignore till up
- returnFlag = true;
- } else if(cheekPress) {
- //valid down followed by invalid moves
- //an invalid move have to cancel earlier action
- ev.setAction(MotionEvent.ACTION_CANCEL);
- action = MotionEvent.ACTION_CANCEL;
- if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE");
- //note that the subsequent invalid moves will not get here
- mFatTouch = true;
+
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
+ Rect inset = child.mGivenVisibleInsets;
+ inputWindow.touchableAreaLeft = frame.left + inset.left;
+ inputWindow.touchableAreaTop = frame.top + inset.top;
+ inputWindow.touchableAreaRight = frame.right - inset.right;
+ inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
+ break;
}
}
- } //else if action
- if(returnFlag) {
- //recycle que, ev
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_FAILED;
}
- } //end if target
- // Enable this for testing the "right" value
- if (false && action == MotionEvent.ACTION_DOWN) {
- int max_events_per_sec = 35;
- try {
- max_events_per_sec = Integer.parseInt(SystemProperties
- .get("windowsmgr.max_events_per_sec"));
- if (max_events_per_sec < 1) {
- max_events_per_sec = 35;
- }
- } catch (NumberFormatException e) {
- }
- mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
+ // Send windows to native code.
+ mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
+
+ // Clear the list in preparation for the next round.
+ // Also avoids keeping InputChannel objects referenced unnecessarily.
+ mTempInputWindows.clear();
}
-
- /*
- * Throttle events to minimize CPU usage when there's a flood of events
- * e.g. constant contact with the screen
- */
- if (action == MotionEvent.ACTION_MOVE) {
- long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents;
- long now = SystemClock.uptimeMillis();
- if (now < nextEventTime) {
- try {
- Thread.sleep(nextEventTime - now);
- } catch (InterruptedException e) {
- }
- mLastTouchEventTime = nextEventTime;
- } else {
- mLastTouchEventTime = now;
- }
+
+ /* Notifies that the lid switch changed state. */
+ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+ mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
}
-
- if (MEASURE_LATENCY) {
- lt.sample("5 in dispatchPointer ", System.nanoTime() - eventTimeNano);
+
+ /* Provides an opportunity for the window manager policy to intercept early key
+ * processing as soon as the key has been read from the device. */
+ public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
+ int policyFlags, boolean isScreenOn) {
+ return mPolicy.interceptKeyBeforeQueueing(whenNanos,
+ keyCode, down, policyFlags, isScreenOn);
}
-
- synchronized(mWindowMap) {
- if (!target.isVisibleLw()) {
- // During this motion dispatch, the target window has become
- // invisible.
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), false);
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_SUCCEEDED;
- }
-
- if (qev != null && action == MotionEvent.ACTION_MOVE) {
- mKeyWaiter.bindTargetWindowLocked(target,
- KeyWaiter.RETURN_PENDING_POINTER, qev);
- ev = null;
- } else {
- if (action == MotionEvent.ACTION_DOWN) {
- WindowState out = mKeyWaiter.mOutsideTouchTargets;
- if (out != null) {
- MotionEvent oev = MotionEvent.obtain(ev);
- oev.setAction(MotionEvent.ACTION_OUTSIDE);
- do {
- final Rect frame = out.mFrame;
- oev.offsetLocation(-(float)frame.left, -(float)frame.top);
- try {
- out.mClient.dispatchPointer(oev, eventTime, false);
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during outside motion dispatch: " + out);
- }
- oev.offsetLocation((float)frame.left, (float)frame.top);
- out = out.mNextOutsideTouch;
- } while (out != null);
- mKeyWaiter.mOutsideTouchTargets = null;
- }
- }
-
- dispatchPointerElsewhereLocked(target, null, ev, ev.getEventTime(), false);
-
- final Rect frame = target.mFrame;
- ev.offsetLocation(-(float)frame.left, -(float)frame.top);
- mKeyWaiter.bindTargetWindowLocked(target);
- }
+
+ /* Provides an opportunity for the window manager policy to process a key before
+ * ordinary dispatch. */
+ public boolean interceptKeyBeforeDispatching(InputChannel focus,
+ int action, int flags, int keyCode, int metaState, int repeatCount,
+ int policyFlags) {
+ WindowState windowState = getWindowStateForInputChannel(focus);
+ return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags,
+ keyCode, metaState, repeatCount, policyFlags);
}
-
- // finally offset the event to the target's coordinate system and
- // dispatch the event.
- try {
- if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) {
- Slog.v(TAG, "Delivering pointer " + qev + " to " + target);
- }
-
- if (MEASURE_LATENCY) {
- lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano);
+
+ /* Called when the current input focus changes.
+ * Layer assignment is assumed to be complete by the time this is called.
+ */
+ public void setInputFocusLw(WindowState newWindow) {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "Input focus has changed to " + newWindow);
}
- target.mClient.dispatchPointer(ev, eventTime, true);
-
- if (MEASURE_LATENCY) {
- lt.sample("7 after svr->client ipc ", System.nanoTime() - eventTimeNano);
- }
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during motion dispatch: " + target);
- mKeyWaiter.mMotionTarget = null;
- try {
- removeWindow(target.mSession, target.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
+ if (newWindow != mInputFocus) {
+ if (newWindow != null && newWindow.canReceiveKeys()) {
+ // Displaying a window implicitly causes dispatching to be unpaused.
+ // This is to protect against bugs if someone pauses dispatching but
+ // forgets to resume.
+ newWindow.mToken.paused = false;
+ }
+
+ mInputFocus = newWindow;
+ updateInputWindowsLw();
}
}
- return INJECT_FAILED;
- }
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
-
- Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, false, false, pid, uid);
- if (focusObj == null) {
- Slog.w(TAG, "No focus window, dropping trackball: " + ev);
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ public void setFocusedAppLw(AppWindowToken newApp) {
+ // Focused app has changed.
+ if (newApp == null) {
+ mInputManager.setFocusedApplication(null);
+ } else {
+ mTempInputApplication.name = newApp.toString();
+ mTempInputApplication.dispatchingTimeoutNanos =
+ newApp.inputDispatchingTimeoutNanos;
+ mTempInputApplication.token = newApp;
+
+ mInputManager.setFocusedApplication(mTempInputApplication);
}
- ev.recycle();
- return INJECT_FAILED;
}
- if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ public void pauseDispatchingLw(WindowToken window) {
+ if (! window.paused) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Pausing WindowToken " + window);
+ }
+
+ window.paused = true;
+ updateInputWindowsLw();
}
- ev.recycle();
- return INJECT_SUCCEEDED;
}
-
- WindowState focus = (WindowState)focusObj;
-
- if (uid != 0 && uid != focus.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting key event from pid "
- + pid + " uid " + uid + " to window " + focus
- + " owned by uid " + focus.mSession.mUid);
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ public void resumeDispatchingLw(WindowToken window) {
+ if (window.paused) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Resuming WindowToken " + window);
}
- ev.recycle();
- return INJECT_NO_PERMISSION;
+
+ window.paused = false;
+ updateInputWindowsLw();
}
}
-
- final long eventTime = ev.getEventTime();
-
- synchronized(mWindowMap) {
- if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) {
- mKeyWaiter.bindTargetWindowLocked(focus,
- KeyWaiter.RETURN_PENDING_TRACKBALL, qev);
- // We don't deliver movement events to the client, we hold
- // them and wait for them to call back.
- ev = null;
- } else {
- mKeyWaiter.bindTargetWindowLocked(focus);
+
+ public void freezeInputDispatchingLw() {
+ if (! mInputDispatchFrozen) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Freezing input dispatching");
+ }
+
+ mInputDispatchFrozen = true;
+ updateInputDispatchModeLw();
}
}
-
- try {
- focus.mClient.dispatchTrackball(ev, eventTime, true);
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus);
- try {
- removeWindow(focus.mSession, focus.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
+
+ public void thawInputDispatchingLw() {
+ if (mInputDispatchFrozen) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Thawing input dispatching");
+ }
+
+ mInputDispatchFrozen = false;
+ updateInputDispatchModeLw();
}
}
-
- return INJECT_FAILED;
- }
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchKey(KeyEvent event, int pid, int uid) {
- if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
-
- Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
- null, false, false, pid, uid);
- if (focusObj == null) {
- Slog.w(TAG, "No focus window, dropping: " + event);
- return INJECT_FAILED;
- }
- if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
- return INJECT_SUCCEEDED;
- }
-
- // Okay we have finished waiting for the last event to be processed.
- // First off, if this is a repeat event, check to see if there is
- // a corresponding up event in the queue. If there is, we will
- // just drop the repeat, because it makes no sense to repeat after
- // the user has released a key. (This is especially important for
- // long presses.)
- if (event.getRepeatCount() > 0 && mQueue.hasKeyUpEvent(event)) {
- return INJECT_SUCCEEDED;
- }
-
- WindowState focus = (WindowState)focusObj;
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Dispatching to " + focus + ": " + event);
-
- if (uid != 0 && uid != focus.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting key event from pid "
- + pid + " uid " + uid + " to window " + focus
- + " owned by uid " + focus.mSession.mUid);
- return INJECT_NO_PERMISSION;
+
+ public void setEventDispatchingLw(boolean enabled) {
+ if (mInputDispatchEnabled != enabled) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Setting event dispatching to " + enabled);
+ }
+
+ mInputDispatchEnabled = enabled;
+ updateInputDispatchModeLw();
}
}
-
- synchronized(mWindowMap) {
- mKeyWaiter.bindTargetWindowLocked(focus);
- }
-
- // NOSHIP extra state logging
- mKeyWaiter.recordDispatchState(event, focus);
- // END NOSHIP
-
- try {
- if (DEBUG_INPUT || DEBUG_FOCUS) {
- Slog.v(TAG, "Delivering key " + event.getKeyCode()
- + " to " + focus);
- }
- focus.mClient.dispatchKey(event);
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus);
- try {
- removeWindow(focus.mSession, focus.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
- }
+
+ private void updateInputDispatchModeLw() {
+ mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
-
- return INJECT_FAILED;
}
public void pauseKeyDispatching(IBinder _token) {
@@ -5440,7 +5371,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- mKeyWaiter.pauseDispatchingLocked(token);
+ mInputMonitor.pauseDispatchingLw(token);
}
}
}
@@ -5454,7 +5385,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- mKeyWaiter.resumeDispatchingLocked(token);
+ mInputMonitor.resumeDispatchingLw(token);
}
}
}
@@ -5466,12 +5397,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized (mWindowMap) {
- mKeyWaiter.setEventDispatchingLocked(enabled);
+ mInputMonitor.setEventDispatchingLw(enabled);
}
}
/**
* Injects a keystroke event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the keystroke action. (Be sure to use
* {@link SystemClock#uptimeMillis()} as the timebase.)
@@ -5488,33 +5421,35 @@ public class WindowManagerService extends IWindowManager.Stub
int metaState = ev.getMetaState();
int deviceId = ev.getDeviceId();
int scancode = ev.getScanCode();
+ int source = ev.getSource();
+
+ if (source == InputDevice.SOURCE_UNKNOWN) {
+ source = InputDevice.SOURCE_KEYBOARD;
+ }
if (eventTime == 0) eventTime = SystemClock.uptimeMillis();
if (downTime == 0) downTime = eventTime;
KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
- deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM);
+ deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM, source);
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result = dispatchKey(newEvent, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
- }
+
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
+
Binder.restoreCallingIdentity(ident);
- switch (result) {
- case INJECT_NO_PERMISSION:
- throw new SecurityException(
- "Injecting to another application requires INJECT_EVENTS permission");
- case INJECT_SUCCEEDED:
- return true;
- }
- return false;
+ return reportInjectionResult(result);
}
/**
* Inject a pointer (touch) event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the pointer (touch) action. (As noted in
* {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
@@ -5526,23 +5461,25 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result = dispatchPointer(null, ev, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+
+ MotionEvent newEvent = MotionEvent.obtain(ev);
+ if ((newEvent.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
+ newEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
}
+
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
+
Binder.restoreCallingIdentity(ident);
- switch (result) {
- case INJECT_NO_PERMISSION:
- throw new SecurityException(
- "Injecting to another application requires INJECT_EVENTS permission");
- case INJECT_SUCCEEDED:
- return true;
- }
- return false;
+ return reportInjectionResult(result);
}
/**
* Inject a trackball (navigation device) event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the trackball action. (As noted in
* {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
@@ -5554,19 +5491,59 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result = dispatchTrackball(null, ev, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+
+ MotionEvent newEvent = MotionEvent.obtain(ev);
+ if ((newEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) == 0) {
+ newEvent.setSource(InputDevice.SOURCE_TRACKBALL);
}
+
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
+
+ Binder.restoreCallingIdentity(ident);
+ return reportInjectionResult(result);
+ }
+
+ /**
+ * Inject an input event into the UI without waiting for dispatch to commence.
+ * This variant is useful for fire-and-forget input event injection. It does not
+ * block any longer than it takes to enqueue the input event.
+ *
+ * @param ev An input event. (Be sure to set the input source correctly.)
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ public boolean injectInputEventNoWait(InputEvent ev) {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+
+ final int result = mInputManager.injectInputEvent(ev, pid, uid,
+ InputManager.INPUT_EVENT_INJECTION_SYNC_NONE,
+ INJECTION_TIMEOUT_MILLIS);
+
Binder.restoreCallingIdentity(ident);
+ return reportInjectionResult(result);
+ }
+
+ private boolean reportInjectionResult(int result) {
switch (result) {
- case INJECT_NO_PERMISSION:
+ case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ Slog.w(TAG, "Input event injection permission denied.");
throw new SecurityException(
"Injecting to another application requires INJECT_EVENTS permission");
- case INJECT_SUCCEEDED:
+ case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
+ //Slog.v(TAG, "Input event injection succeeded.");
return true;
+ case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
+ Slog.w(TAG, "Input event injection timed out.");
+ return false;
+ case InputManager.INPUT_EVENT_INJECTION_FAILED:
+ default:
+ Slog.w(TAG, "Input event injection failed.");
+ return false;
}
- return false;
}
private WindowState getFocusedWindow() {
@@ -5579,894 +5556,6 @@ public class WindowManagerService extends IWindowManager.Stub
return mCurrentFocus;
}
- /**
- * This class holds the state for dispatching key events. This state
- * is protected by the KeyWaiter instance, NOT by the window lock. You
- * can be holding the main window lock while acquire the KeyWaiter lock,
- * but not the other way around.
- */
- final class KeyWaiter {
- // NOSHIP debugging
- public class DispatchState {
- private KeyEvent event;
- private WindowState focus;
- private long time;
- private WindowState lastWin;
- private IBinder lastBinder;
- private boolean finished;
- private boolean gotFirstWindow;
- private boolean eventDispatching;
- private long timeToSwitch;
- private boolean wasFrozen;
- private boolean focusPaused;
- private WindowState curFocus;
-
- DispatchState(KeyEvent theEvent, WindowState theFocus) {
- focus = theFocus;
- event = theEvent;
- time = System.currentTimeMillis();
- // snapshot KeyWaiter state
- lastWin = mLastWin;
- lastBinder = mLastBinder;
- finished = mFinished;
- gotFirstWindow = mGotFirstWindow;
- eventDispatching = mEventDispatching;
- timeToSwitch = mTimeToSwitch;
- wasFrozen = mWasFrozen;
- curFocus = mCurrentFocus;
- // cache the paused state at ctor time as well
- if (theFocus == null || theFocus.mToken == null) {
- focusPaused = false;
- } else {
- focusPaused = theFocus.mToken.paused;
- }
- }
-
- public String toString() {
- return "{{" + event + " to " + focus + " @ " + time
- + " lw=" + lastWin + " lb=" + lastBinder
- + " fin=" + finished + " gfw=" + gotFirstWindow
- + " ed=" + eventDispatching + " tts=" + timeToSwitch
- + " wf=" + wasFrozen + " fp=" + focusPaused
- + " mcf=" + curFocus + "}}";
- }
- };
- private DispatchState mDispatchState = null;
- public void recordDispatchState(KeyEvent theEvent, WindowState theFocus) {
- mDispatchState = new DispatchState(theEvent, theFocus);
- }
- // END NOSHIP
-
- public static final int RETURN_NOTHING = 0;
- public static final int RETURN_PENDING_POINTER = 1;
- public static final int RETURN_PENDING_TRACKBALL = 2;
-
- final Object SKIP_TARGET_TOKEN = new Object();
- final Object CONSUMED_EVENT_TOKEN = new Object();
-
- private WindowState mLastWin = null;
- private IBinder mLastBinder = null;
- private boolean mFinished = true;
- private boolean mGotFirstWindow = false;
- private boolean mEventDispatching = true;
- private long mTimeToSwitch = 0;
- /* package */ boolean mWasFrozen = false;
-
- // Target of Motion events
- WindowState mMotionTarget;
-
- // Windows above the target who would like to receive an "outside"
- // touch event for any down events outside of them.
- WindowState mOutsideTouchTargets;
-
- /**
- * Wait for the last event dispatch to complete, then find the next
- * target that should receive the given event and wait for that one
- * to be ready to receive it.
- */
- Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
- MotionEvent nextMotion, boolean isPointerEvent,
- boolean failIfTimeout, int callingPid, int callingUid) {
- long startTime = SystemClock.uptimeMillis();
- long keyDispatchingTimeout = 5 * 1000;
- long waitedFor = 0;
-
- while (true) {
- // Figure out which window we care about. It is either the
- // last window we are waiting to have process the event or,
- // if none, then the next window we think the event should go
- // to. Note: we retrieve mLastWin outside of the lock, so
- // it may change before we lock. Thus we must check it again.
- WindowState targetWin = mLastWin;
- boolean targetIsNew = targetWin == null;
- if (DEBUG_INPUT) Slog.v(
- TAG, "waitForLastKey: mFinished=" + mFinished +
- ", mLastWin=" + mLastWin);
- if (targetIsNew) {
- Object target = findTargetWindow(nextKey, qev, nextMotion,
- isPointerEvent, callingPid, callingUid);
- if (target == SKIP_TARGET_TOKEN) {
- // The user has pressed a special key, and we are
- // dropping all pending events before it.
- if (DEBUG_INPUT) Slog.v(TAG, "Skipping: " + nextKey
- + " " + nextMotion);
- return null;
- }
- if (target == CONSUMED_EVENT_TOKEN) {
- if (DEBUG_INPUT) Slog.v(TAG, "Consumed: " + nextKey
- + " " + nextMotion);
- return target;
- }
- targetWin = (WindowState)target;
- }
-
- AppWindowToken targetApp = null;
-
- // Now: is it okay to send the next event to this window?
- synchronized (this) {
- // First: did we come here based on the last window not
- // being null, but it changed by the time we got here?
- // If so, try again.
- if (!targetIsNew && mLastWin == null) {
- continue;
- }
-
- // We never dispatch events if not finished with the
- // last one, or the display is frozen.
- if (mFinished && !mDisplayFrozen) {
- // If event dispatching is disabled, then we
- // just consume the events.
- if (!mEventDispatching) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Skipping event; dispatching disabled: "
- + nextKey + " " + nextMotion);
- return null;
- }
- if (targetWin != null) {
- // If this is a new target, and that target is not
- // paused or unresponsive, then all looks good to
- // handle the event.
- if (targetIsNew && !targetWin.mToken.paused) {
- return targetWin;
- }
-
- // If we didn't find a target window, and there is no
- // focused app window, then just eat the events.
- } else if (mFocusedApp == null) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Skipping event; no focused app: "
- + nextKey + " " + nextMotion);
- return null;
- }
- }
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for last key in " + mLastBinder
- + " target=" + targetWin
- + " mFinished=" + mFinished
- + " mDisplayFrozen=" + mDisplayFrozen
- + " targetIsNew=" + targetIsNew
- + " paused="
- + (targetWin != null ? targetWin.mToken.paused : false)
- + " mFocusedApp=" + mFocusedApp
- + " mCurrentFocus=" + mCurrentFocus);
-
- targetApp = targetWin != null
- ? targetWin.mAppToken : mFocusedApp;
-
- long curTimeout = keyDispatchingTimeout;
- if (mTimeToSwitch != 0) {
- long now = SystemClock.uptimeMillis();
- if (mTimeToSwitch <= now) {
- // If an app switch key has been pressed, and we have
- // waited too long for the current app to finish
- // processing keys, then wait no more!
- doFinishedKeyLocked(false);
- continue;
- }
- long switchTimeout = mTimeToSwitch - now;
- if (curTimeout > switchTimeout) {
- curTimeout = switchTimeout;
- }
- }
-
- try {
- // after that continue
- // processing keys, so we don't get stuck.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for key dispatch: " + curTimeout);
- wait(curTimeout);
- if (DEBUG_INPUT) Slog.v(TAG, "Finished waiting @"
- + SystemClock.uptimeMillis() + " startTime="
- + startTime + " switchTime=" + mTimeToSwitch
- + " target=" + targetWin + " mLW=" + mLastWin
- + " mLB=" + mLastBinder + " fin=" + mFinished
- + " mCurrentFocus=" + mCurrentFocus);
- } catch (InterruptedException e) {
- }
- }
-
- // If we were frozen during configuration change, restart the
- // timeout checks from now; otherwise look at whether we timed
- // out before awakening.
- if (mWasFrozen) {
- waitedFor = 0;
- mWasFrozen = false;
- } else {
- waitedFor = SystemClock.uptimeMillis() - startTime;
- }
-
- if (waitedFor >= keyDispatchingTimeout && mTimeToSwitch == 0) {
- IApplicationToken at = null;
- synchronized (this) {
- Slog.w(TAG, "Key dispatching timed out sending to " +
- (targetWin != null ? targetWin.mAttrs.getTitle()
- : "<null>: no window ready for key dispatch"));
- // NOSHIP debugging
- Slog.w(TAG, "Previous dispatch state: " + mDispatchState);
- Slog.w(TAG, "Current dispatch state: " +
- new DispatchState(nextKey, targetWin));
- // END NOSHIP
- //dump();
- if (targetWin != null) {
- at = targetWin.getAppToken();
- } else if (targetApp != null) {
- at = targetApp.appToken;
- }
- }
-
- boolean abort = true;
- if (at != null) {
- try {
- long timeout = at.getKeyDispatchingTimeout();
- if (timeout > waitedFor) {
- // we did not wait the proper amount of time for this application.
- // set the timeout to be the real timeout and wait again.
- keyDispatchingTimeout = timeout - waitedFor;
- continue;
- } else {
- abort = at.keyDispatchingTimedOut();
- }
- } catch (RemoteException ex) {
- }
- }
-
- synchronized (this) {
- if (abort && (mLastWin == targetWin || targetWin == null)) {
- mFinished = true;
- if (mLastWin != null) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Window " + mLastWin +
- " timed out on key input");
- if (mLastWin.mToken.paused) {
- Slog.w(TAG, "Un-pausing dispatching to this window");
- mLastWin.mToken.paused = false;
- }
- }
- if (mMotionTarget == targetWin) {
- mMotionTarget = null;
- }
- mLastWin = null;
- mLastBinder = null;
- if (failIfTimeout || targetWin == null) {
- return null;
- }
- } else {
- Slog.w(TAG, "Continuing to wait for key to be dispatched");
- startTime = SystemClock.uptimeMillis();
- }
- }
- }
- }
- }
-
- Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
- MotionEvent nextMotion, boolean isPointerEvent,
- int callingPid, int callingUid) {
- mOutsideTouchTargets = null;
-
- if (nextKey != null) {
- // Find the target window for a normal key event.
- final int keycode = nextKey.getKeyCode();
- final int repeatCount = nextKey.getRepeatCount();
- final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
-
- if (!dispatch) {
- if (callingUid == 0 ||
- mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS,
- callingPid, callingUid)
- == PackageManager.PERMISSION_GRANTED) {
- mPolicy.interceptKeyTi(null, keycode,
- nextKey.getMetaState(), down, repeatCount,
- nextKey.getFlags());
- }
- Slog.w(TAG, "Event timeout during app switch: dropping "
- + nextKey);
- return SKIP_TARGET_TOKEN;
- }
-
- // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")");
-
- WindowState focus = null;
- synchronized(mWindowMap) {
- focus = getFocusedWindowLocked();
- }
-
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-
- if (callingUid == 0 ||
- (focus != null && callingUid == focus.mSession.mUid) ||
- mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS,
- callingPid, callingUid)
- == PackageManager.PERMISSION_GRANTED) {
- if (mPolicy.interceptKeyTi(focus,
- keycode, nextKey.getMetaState(), down, repeatCount,
- nextKey.getFlags())) {
- return CONSUMED_EVENT_TOKEN;
- }
- }
-
- return focus;
-
- } else if (!isPointerEvent) {
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1);
- if (!dispatch) {
- Slog.w(TAG, "Event timeout during app switch: dropping trackball "
- + nextMotion);
- return SKIP_TARGET_TOKEN;
- }
-
- WindowState focus = null;
- synchronized(mWindowMap) {
- focus = getFocusedWindowLocked();
- }
-
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
- return focus;
- }
-
- if (nextMotion == null) {
- return SKIP_TARGET_TOKEN;
- }
-
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(
- KeyEvent.KEYCODE_UNKNOWN);
- if (!dispatch) {
- Slog.w(TAG, "Event timeout during app switch: dropping pointer "
- + nextMotion);
- return SKIP_TARGET_TOKEN;
- }
-
- // Find the target window for a pointer event.
- int action = nextMotion.getAction();
- final float xf = nextMotion.getX();
- final float yf = nextMotion.getY();
- final long eventTime = nextMotion.getEventTime();
-
- final boolean screenWasOff = qev != null
- && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
-
- WindowState target = null;
-
- synchronized(mWindowMap) {
- synchronized (this) {
- if (action == MotionEvent.ACTION_DOWN) {
- if (mMotionTarget != null) {
- // this is weird, we got a pen down, but we thought it was
- // already down!
- // XXX: We should probably send an ACTION_UP to the current
- // target.
- Slog.w(TAG, "Pointer down received while already down in: "
- + mMotionTarget);
- mMotionTarget = null;
- }
-
- // ACTION_DOWN is special, because we need to lock next events to
- // the window we'll land onto.
- final int x = (int)xf;
- final int y = (int)yf;
-
- final ArrayList windows = mWindows;
- final int N = windows.size();
- WindowState topErrWindow = null;
- final Rect tmpRect = mTempRect;
- for (int i=N-1; i>=0; i--) {
- WindowState child = (WindowState)windows.get(i);
- //Slog.i(TAG, "Checking dispatch to: " + child);
- final int flags = child.mAttrs.flags;
- if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
- if (topErrWindow == null) {
- topErrWindow = child;
- }
- }
- if (!child.isVisibleLw()) {
- //Slog.i(TAG, "Not visible!");
- continue;
- }
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- //Slog.i(TAG, "Not touchable!");
- if ((flags & WindowManager.LayoutParams
- .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
- child.mNextOutsideTouch = mOutsideTouchTargets;
- mOutsideTouchTargets = child;
- }
- continue;
- }
- tmpRect.set(child.mFrame);
- if (child.mTouchableInsets == ViewTreeObserver
- .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
- // The touch is inside of the window if it is
- // inside the frame, AND the content part of that
- // frame that was given by the application.
- tmpRect.left += child.mGivenContentInsets.left;
- tmpRect.top += child.mGivenContentInsets.top;
- tmpRect.right -= child.mGivenContentInsets.right;
- tmpRect.bottom -= child.mGivenContentInsets.bottom;
- } else if (child.mTouchableInsets == ViewTreeObserver
- .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
- // The touch is inside of the window if it is
- // inside the frame, AND the visible part of that
- // frame that was given by the application.
- tmpRect.left += child.mGivenVisibleInsets.left;
- tmpRect.top += child.mGivenVisibleInsets.top;
- tmpRect.right -= child.mGivenVisibleInsets.right;
- tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
- }
- final int touchFlags = flags &
- (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
- if (tmpRect.contains(x, y) || touchFlags == 0) {
- //Slog.i(TAG, "Using this target!");
- if (!screenWasOff || (flags &
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
- mMotionTarget = child;
- } else {
- //Slog.i(TAG, "Waking, skip!");
- mMotionTarget = null;
- }
- break;
- }
-
- if ((flags & WindowManager.LayoutParams
- .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
- child.mNextOutsideTouch = mOutsideTouchTargets;
- mOutsideTouchTargets = child;
- //Slog.i(TAG, "Adding to outside target list: " + child);
- }
- }
-
- // if there's an error window but it's not accepting
- // focus (typically because it is not yet visible) just
- // wait for it -- any other focused window may in fact
- // be in ANR state.
- if (topErrWindow != null && mMotionTarget != topErrWindow) {
- mMotionTarget = null;
- }
- }
-
- target = mMotionTarget;
- }
- }
-
- wakeupIfNeeded(target, eventType(nextMotion));
-
- // Pointer events are a little different -- if there isn't a
- // target found for any event, then just drop it.
- return target != null ? target : SKIP_TARGET_TOKEN;
- }
-
- boolean checkShouldDispatchKey(int keycode) {
- synchronized (this) {
- if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) {
- mTimeToSwitch = 0;
- return true;
- }
- if (mTimeToSwitch != 0
- && mTimeToSwitch < SystemClock.uptimeMillis()) {
- return false;
- }
- return true;
- }
- }
-
- void bindTargetWindowLocked(WindowState win,
- int pendingWhat, QueuedEvent pendingMotion) {
- synchronized (this) {
- bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion);
- }
- }
-
- void bindTargetWindowLocked(WindowState win) {
- synchronized (this) {
- bindTargetWindowLockedLocked(win, RETURN_NOTHING, null);
- }
- }
-
- void bindTargetWindowLockedLocked(WindowState win,
- int pendingWhat, QueuedEvent pendingMotion) {
- mLastWin = win;
- mLastBinder = win.mClient.asBinder();
- mFinished = false;
- if (pendingMotion != null) {
- final Session s = win.mSession;
- if (pendingWhat == RETURN_PENDING_POINTER) {
- releasePendingPointerLocked(s);
- s.mPendingPointerMove = pendingMotion;
- s.mPendingPointerWindow = win;
- if (DEBUG_INPUT) Slog.v(TAG,
- "bindTargetToWindow " + s.mPendingPointerMove);
- } else if (pendingWhat == RETURN_PENDING_TRACKBALL) {
- releasePendingTrackballLocked(s);
- s.mPendingTrackballMove = pendingMotion;
- s.mPendingTrackballWindow = win;
- }
- }
- }
-
- void releasePendingPointerLocked(Session s) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "releasePendingPointer " + s.mPendingPointerMove);
- if (s.mPendingPointerMove != null) {
- mQueue.recycleEvent(s.mPendingPointerMove);
- s.mPendingPointerMove = null;
- }
- }
-
- void releasePendingTrackballLocked(Session s) {
- if (s.mPendingTrackballMove != null) {
- mQueue.recycleEvent(s.mPendingTrackballMove);
- s.mPendingTrackballMove = null;
- }
- }
-
- void releaseMotionTarget(WindowState win) {
- if (mMotionTarget == win) {
- mMotionTarget = null;
- }
- }
-
- MotionEvent finishedKey(Session session, IWindow client, boolean force,
- int returnWhat) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: client=" + client + ", force=" + force);
-
- if (client == null) {
- return null;
- }
-
- MotionEvent res = null;
- QueuedEvent qev = null;
- WindowState win = null;
-
- synchronized (this) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: client=" + client.asBinder()
- + ", force=" + force + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")");
-
- if (returnWhat == RETURN_PENDING_POINTER) {
- qev = session.mPendingPointerMove;
- win = session.mPendingPointerWindow;
- session.mPendingPointerMove = null;
- session.mPendingPointerWindow = null;
- } else if (returnWhat == RETURN_PENDING_TRACKBALL) {
- qev = session.mPendingTrackballMove;
- win = session.mPendingTrackballWindow;
- session.mPendingTrackballMove = null;
- session.mPendingTrackballWindow = null;
- }
-
- if (mLastBinder == client.asBinder()) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: last paused="
- + ((mLastWin != null) ? mLastWin.mToken.paused : "null"));
- if (mLastWin != null && (!mLastWin.mToken.paused || force
- || !mEventDispatching)) {
- doFinishedKeyLocked(true);
- } else {
- // Make sure to wake up anyone currently waiting to
- // dispatch a key, so they can re-evaluate their
- // current situation.
- mFinished = true;
- notifyAll();
- }
- }
-
- if (qev != null) {
- res = (MotionEvent)qev.event;
- if (DEBUG_INPUT) Slog.v(TAG,
- "Returning pending motion: " + res);
- mQueue.recycleEvent(qev);
- if (win != null && returnWhat == RETURN_PENDING_POINTER) {
- res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
- }
- }
- }
-
- if (res != null && returnWhat == RETURN_PENDING_POINTER) {
- synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(win, win, res, res.getEventTime(), false);
- }
- }
-
- return res;
- }
-
- void tickle() {
- synchronized (this) {
- notifyAll();
- }
- }
-
- void handleNewWindowLocked(WindowState newWindow) {
- if (!newWindow.canReceiveKeys()) {
- return;
- }
- synchronized (this) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "New key dispatch window: win="
- + newWindow.mClient.asBinder()
- + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
- + "), finished=" + mFinished + ", paused="
- + newWindow.mToken.paused);
-
- // Displaying a window implicitly causes dispatching to
- // be unpaused. (This is to protect against bugs if someone
- // pauses dispatching but forgets to resume.)
- newWindow.mToken.paused = false;
-
- mGotFirstWindow = true;
-
- if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "New SYSTEM_ERROR window; resetting state");
- mLastWin = null;
- mLastBinder = null;
- mMotionTarget = null;
- mFinished = true;
- } else if (mLastWin != null) {
- // If the new window is above the window we are
- // waiting on, then stop waiting and let key dispatching
- // start on the new guy.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Last win layer=" + mLastWin.mLayer
- + ", new win layer=" + newWindow.mLayer);
- if (newWindow.mLayer >= mLastWin.mLayer) {
- // The new window is above the old; finish pending input to the last
- // window and start directing it to the new one.
- mLastWin.mToken.paused = false;
- doFinishedKeyLocked(false); // does a notifyAll()
- return;
- }
- }
-
- // Now that we've put a new window state in place, make the event waiter
- // take notice and retarget its attentions.
- notifyAll();
- }
- }
-
- void pauseDispatchingLocked(WindowToken token) {
- synchronized (this)
- {
- if (DEBUG_INPUT) Slog.v(TAG, "Pausing WindowToken " + token);
- token.paused = true;
-
- /*
- if (mLastWin != null && !mFinished && mLastWin.mBaseLayer <= layer) {
- mPaused = true;
- } else {
- if (mLastWin == null) {
- Slog.i(TAG, "Key dispatching not paused: no last window.");
- } else if (mFinished) {
- Slog.i(TAG, "Key dispatching not paused: finished last key.");
- } else {
- Slog.i(TAG, "Key dispatching not paused: window in higher layer.");
- }
- }
- */
- }
- }
-
- void resumeDispatchingLocked(WindowToken token) {
- synchronized (this) {
- if (token.paused) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "Resuming WindowToken " + token
- + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
- + "), finished=" + mFinished + ", paused="
- + token.paused);
- token.paused = false;
- if (mLastWin != null && mLastWin.mToken == token && mFinished) {
- doFinishedKeyLocked(false);
- } else {
- notifyAll();
- }
- }
- }
- }
-
- void setEventDispatchingLocked(boolean enabled) {
- synchronized (this) {
- mEventDispatching = enabled;
- notifyAll();
- }
- }
-
- void appSwitchComing() {
- synchronized (this) {
- // Don't wait for more than .5 seconds for app to finish
- // processing the pending events.
- long now = SystemClock.uptimeMillis() + 500;
- if (DEBUG_INPUT) Slog.v(TAG, "appSwitchComing: " + now);
- if (mTimeToSwitch == 0 || now < mTimeToSwitch) {
- mTimeToSwitch = now;
- }
- notifyAll();
- }
- }
-
- private final void doFinishedKeyLocked(boolean force) {
- if (mLastWin != null) {
- releasePendingPointerLocked(mLastWin.mSession);
- releasePendingTrackballLocked(mLastWin.mSession);
- }
-
- if (force || mLastWin == null || !mLastWin.mToken.paused
- || !mLastWin.isVisibleLw()) {
- // If the current window has been paused, we aren't -really-
- // finished... so let the waiters still wait.
- mLastWin = null;
- mLastBinder = null;
- }
- mFinished = true;
- notifyAll();
- }
- }
-
- private class KeyQ extends KeyInputQueue
- implements KeyInputQueue.FilterCallback {
- PowerManager.WakeLock mHoldingScreen;
-
- KeyQ() {
- super(mContext, WindowManagerService.this);
- PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
- "KEEP_SCREEN_ON_FLAG");
- mHoldingScreen.setReferenceCounted(false);
- }
-
- @Override
- boolean preprocessEvent(InputDevice device, RawInputEvent event) {
- if (mPolicy.preprocessInputEventTq(event)) {
- return true;
- }
-
- switch (event.type) {
- case RawInputEvent.EV_KEY: {
- // XXX begin hack
- if (DEBUG) {
- if (event.keycode == KeyEvent.KEYCODE_G) {
- if (event.value != 0) {
- // G down
- mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- }
- return false;
- }
- if (event.keycode == KeyEvent.KEYCODE_D) {
- if (event.value != 0) {
- //dump();
- }
- return false;
- }
- }
- // XXX end hack
-
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- int actions = mPolicy.interceptKeyTq(event, !screenIsOff);
-
- if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {
- mPowerManager.goToSleep(event.when);
- }
-
- if (screenIsOff) {
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {
- mPowerManager.userActivity(event.when, false,
- LocalPowerManager.BUTTON_EVENT, false);
- }
-
- if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
- if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) {
- filterQueue(this);
- mKeyWaiter.appSwitchComing();
- }
- return true;
- } else {
- return false;
- }
- }
-
- case RawInputEvent.EV_REL: {
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- if (screenIsOff) {
- if (!mPolicy.isWakeRelMovementTq(event.deviceId,
- device.classes, event)) {
- //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey");
- return false;
- }
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- return true;
- }
-
- case RawInputEvent.EV_ABS: {
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- if (screenIsOff) {
- if (!mPolicy.isWakeAbsMovementTq(event.deviceId,
- device.classes, event)) {
- //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey");
- return false;
- }
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- return true;
- }
-
- default:
- return true;
- }
- }
-
- public int filterEvent(QueuedEvent ev) {
- switch (ev.classType) {
- case RawInputEvent.CLASS_KEYBOARD:
- KeyEvent ke = (KeyEvent)ev.event;
- if (mPolicy.isMovementKeyTi(ke.getKeyCode())) {
- Slog.w(TAG, "Dropping movement key during app switch: "
- + ke.getKeyCode() + ", action=" + ke.getAction());
- return FILTER_REMOVE;
- }
- return FILTER_ABORT;
- default:
- return FILTER_KEEP;
- }
- }
-
- /**
- * Must be called with the main window manager lock held.
- */
- void setHoldScreenLocked(boolean holding) {
- boolean state = mHoldingScreen.isHeld();
- if (holding != state) {
- if (holding) {
- mHoldingScreen.acquire();
- } else {
- mPolicy.screenOnStoppedLw();
- mHoldingScreen.release();
- }
- }
- }
- }
-
public boolean detectSafeMode() {
mSafeMode = mPolicy.detectSafeMode();
return mSafeMode;
@@ -6476,219 +5565,6 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.systemReady();
}
- private final class InputDispatcherThread extends Thread {
- // Time to wait when there is nothing to do: 9999 seconds.
- static final int LONG_WAIT=9999*1000;
-
- public InputDispatcherThread() {
- super("InputDispatcher");
- }
-
- @Override
- public void run() {
- while (true) {
- try {
- process();
- } catch (Exception e) {
- Slog.e(TAG, "Exception in input dispatcher", e);
- }
- }
- }
-
- private void process() {
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
- // The last key event we saw
- KeyEvent lastKey = null;
-
- // Last keydown time for auto-repeating keys
- long lastKeyTime = SystemClock.uptimeMillis();
- long nextKeyTime = lastKeyTime+LONG_WAIT;
- long downTime = 0;
-
- // How many successive repeats we generated
- int keyRepeatCount = 0;
-
- // Need to report that configuration has changed?
- boolean configChanged = false;
-
- while (true) {
- long curTime = SystemClock.uptimeMillis();
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for next key: now=" + curTime
- + ", repeat @ " + nextKeyTime);
-
- // Retrieve next event, waiting only as long as the next
- // repeat timeout. If the configuration has changed, then
- // don't wait at all -- we'll report the change as soon as
- // we have processed all events.
- QueuedEvent ev = mQueue.getEvent(
- (int)((!configChanged && curTime < nextKeyTime)
- ? (nextKeyTime-curTime) : 0));
-
- if (DEBUG_INPUT && ev != null) Slog.v(
- TAG, "Event: type=" + ev.classType + " data=" + ev.event);
-
- if (MEASURE_LATENCY) {
- lt.sample("2 got event ", System.nanoTime() - ev.whenNano);
- }
-
- if (lastKey != null && !mPolicy.allowKeyRepeat()) {
- // cancel key repeat at the request of the policy.
- lastKey = null;
- downTime = 0;
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- }
- try {
- if (ev != null) {
- curTime = SystemClock.uptimeMillis();
- int eventType;
- if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
- eventType = eventType((MotionEvent)ev.event);
- } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
- ev.classType == RawInputEvent.CLASS_TRACKBALL) {
- eventType = LocalPowerManager.BUTTON_EVENT;
- } else {
- eventType = LocalPowerManager.OTHER_EVENT;
- }
- try {
- if ((curTime - mLastBatteryStatsCallTime)
- >= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastBatteryStatsCallTime = curTime;
- mBatteryStats.noteInputEvent();
- }
- } catch (RemoteException e) {
- // Ignore
- }
-
- if (ev.classType == RawInputEvent.CLASS_CONFIGURATION_CHANGED) {
- // do not wake screen in this case
- } else if (eventType != TOUCH_EVENT
- && eventType != LONG_TOUCH_EVENT
- && eventType != CHEEK_EVENT) {
- mPowerManager.userActivity(curTime, false,
- eventType, false);
- } else if (mLastTouchEventType != eventType
- || (curTime - mLastUserActivityCallTime)
- >= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastUserActivityCallTime = curTime;
- mLastTouchEventType = eventType;
- mPowerManager.userActivity(curTime, false,
- eventType, false);
- }
-
- switch (ev.classType) {
- case RawInputEvent.CLASS_KEYBOARD:
- KeyEvent ke = (KeyEvent)ev.event;
- if (ke.isDown()) {
- lastKeyTime = curTime;
- if (lastKey != null &&
- ke.getKeyCode() == lastKey.getKeyCode()) {
- keyRepeatCount++;
- // Arbitrary long timeout to block
- // repeating here since we know that
- // the device driver takes care of it.
- nextKeyTime = lastKeyTime + LONG_WAIT;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received repeated key down");
- } else {
- downTime = curTime;
- keyRepeatCount = 0;
- nextKeyTime = lastKeyTime
- + ViewConfiguration.getLongPressTimeout();
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received key down: first repeat @ "
- + nextKeyTime);
- }
- lastKey = ke;
- } else {
- lastKey = null;
- downTime = 0;
- keyRepeatCount = 0;
- // Arbitrary long timeout.
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received key up: ignore repeat @ "
- + nextKeyTime);
- }
- if (keyRepeatCount > 0) {
- dispatchKey(KeyEvent.changeTimeRepeat(ke,
- ke.getEventTime(), keyRepeatCount), 0, 0);
- } else {
- dispatchKey(ke, 0, 0);
- }
- mQueue.recycleEvent(ev);
- break;
- case RawInputEvent.CLASS_TOUCHSCREEN:
- //Slog.i(TAG, "Read next event " + ev);
- dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
- break;
- case RawInputEvent.CLASS_TRACKBALL:
- dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
- break;
- case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
- configChanged = true;
- break;
- default:
- mQueue.recycleEvent(ev);
- break;
- }
-
- } else if (configChanged) {
- configChanged = false;
- sendNewConfiguration();
-
- } else if (lastKey != null) {
- curTime = SystemClock.uptimeMillis();
-
- // Timeout occurred while key was down. If it is at or
- // past the key repeat time, dispatch the repeat.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Key timeout: repeat=" + nextKeyTime
- + ", now=" + curTime);
- if (curTime < nextKeyTime) {
- continue;
- }
-
- lastKeyTime = nextKeyTime;
- nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;
- keyRepeatCount++;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Key repeat: count=" + keyRepeatCount
- + ", next @ " + nextKeyTime);
- KeyEvent newEvent;
- if (downTime != 0 && (downTime
- + ViewConfiguration.getLongPressTimeout())
- <= curTime) {
- newEvent = KeyEvent.changeTimeRepeat(lastKey,
- curTime, keyRepeatCount,
- lastKey.getFlags() | KeyEvent.FLAG_LONG_PRESS);
- downTime = 0;
- } else {
- newEvent = KeyEvent.changeTimeRepeat(lastKey,
- curTime, keyRepeatCount);
- }
- dispatchKey(newEvent, 0, 0);
-
- } else {
- curTime = SystemClock.uptimeMillis();
-
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- }
-
- } catch (Exception e) {
- Slog.e(TAG,
- "Input thread received uncaught exception: " + e, e);
- }
- }
- }
- }
-
// -------------------------------------------------------------
// Client Session State
// -------------------------------------------------------------
@@ -6704,20 +5580,6 @@ public class WindowManagerService extends IWindowManager.Stub
int mNumWindow = 0;
boolean mClientDead = false;
- /**
- * Current pointer move event being dispatched to client window... must
- * hold key lock to access.
- */
- QueuedEvent mPendingPointerMove;
- WindowState mPendingPointerWindow;
-
- /**
- * Current trackball move event being dispatched to client window... must
- * hold key lock to access.
- */
- QueuedEvent mPendingTrackballMove;
- WindowState mPendingTrackballWindow;
-
public Session(IInputMethodClient client, IInputContext inputContext) {
mClient = client;
mInputContext = inputContext;
@@ -6793,8 +5655,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
public int add(IWindow window, WindowManager.LayoutParams attrs,
+ int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
+ return addWindow(this, window, attrs, viewVisibility, outContentInsets,
+ outInputChannel);
+ }
+
+ public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
- return addWindow(this, window, attrs, viewVisibility, outContentInsets);
+ return addWindow(this, window, attrs, viewVisibility, outContentInsets, null);
}
public void remove(IWindow window) {
@@ -6805,9 +5673,12 @@ public class WindowManagerService extends IWindowManager.Stub
int requestedWidth, int requestedHeight, int viewFlags,
boolean insetsPending, Rect outFrame, Rect outContentInsets,
Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
- return relayoutWindow(this, window, attrs,
+ //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid());
+ int res = relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, insetsPending,
outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
+ //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid());
+ return res;
}
public void setTransparentRegion(IWindow window, Region region) {
@@ -6830,27 +5701,6 @@ public class WindowManagerService extends IWindowManager.Stub
finishDrawingWindow(this, window);
}
- public void finishKey(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow finishKey called for " + window);
- mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_NOTHING);
- }
-
- public MotionEvent getPendingPointerMove(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow getPendingMotionEvent called for " + window);
- return mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_PENDING_POINTER);
- }
-
- public MotionEvent getPendingTrackballMove(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow getPendingMotionEvent called for " + window);
- return mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_PENDING_TRACKBALL);
- }
-
public void setInTouchMode(boolean mode) {
synchronized(mWindowMap) {
mInTouchMode = mode;
@@ -6954,16 +5804,6 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
pw.print(" mClientDead="); pw.print(mClientDead);
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
- if (mPendingPointerWindow != null || mPendingPointerMove != null) {
- pw.print(prefix);
- pw.print("mPendingPointerWindow="); pw.print(mPendingPointerWindow);
- pw.print(" mPendingPointerMove="); pw.println(mPendingPointerMove);
- }
- if (mPendingTrackballWindow != null || mPendingTrackballMove != null) {
- pw.print(prefix);
- pw.print("mPendingTrackballWindow="); pw.print(mPendingTrackballWindow);
- pw.print(" mPendingTrackballMove="); pw.println(mPendingTrackballMove);
- }
}
@Override
@@ -6986,7 +5826,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
final DeathRecipient mDeathRecipient;
final WindowState mAttachedWindow;
- final ArrayList mChildWindows = new ArrayList();
+ final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>();
final int mBaseLayer;
final int mSubLayer;
final boolean mLayoutAttached;
@@ -7014,8 +5854,6 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mObscured;
boolean mTurnOnScreen;
- WindowState mNextOutsideTouch;
-
int mLayoutSeq = -1;
Configuration mConfiguration = null;
@@ -7163,6 +6001,9 @@ public class WindowManagerService extends IWindowManager.Stub
int mSurfaceLayer;
float mSurfaceAlpha;
+ // Input channel
+ InputChannel mInputChannel;
+
WindowState(Session s, IWindow c, WindowToken token,
WindowState attachedWindow, WindowManager.LayoutParams a,
int viewVisibility) {
@@ -7390,6 +6231,12 @@ public class WindowManagerService extends IWindowManager.Stub
public IApplicationToken getAppToken() {
return mAppToken != null ? mAppToken.appToken : null;
}
+
+ public long getInputDispatchingTimeoutNanos() {
+ return mAppToken != null
+ ? mAppToken.inputDispatchingTimeoutNanos
+ : DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
public boolean hasAppShownWindows() {
return mAppToken != null ? mAppToken.firstWindowDrawn : false;
@@ -7523,14 +6370,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
void destroySurfaceLocked() {
- // Window is no longer on-screen, so can no longer receive
- // key events... if we were waiting for it to finish
- // handling a key event, the wait is over!
- mKeyWaiter.finishedKey(mSession, mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releasePendingPointerLocked(mSession);
- mKeyWaiter.releasePendingTrackballLocked(mSession);
-
if (mAppToken != null && this == mAppToken.startingWindow) {
mAppToken.startingDisplayed = false;
}
@@ -7543,7 +6382,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = mChildWindows.size();
while (i > 0) {
i--;
- WindowState c = (WindowState)mChildWindows.get(i);
+ WindowState c = mChildWindows.get(i);
c.mAttachedHidden = true;
}
@@ -7654,7 +6493,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = mChildWindows.size();
while (i > 0) {
i--;
- WindowState c = (WindowState)mChildWindows.get(i);
+ WindowState c = mChildWindows.get(i);
if (c.mAttachedHidden) {
c.mAttachedHidden = false;
if (c.mSurface != null) {
@@ -7827,7 +6666,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = mChildWindows.size();
for (int i=0; i<N; i++) {
- ((WindowState)mChildWindows.get(i)).finishExit();
+ mChildWindows.get(i).finishExit();
}
if (!mExiting) {
@@ -7852,7 +6691,6 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Error hiding surface in " + this, e);
}
mLastHidden = true;
- mKeyWaiter.releasePendingPointerLocked(mSession);
}
mExiting = false;
if (mRemoveOnExit) {
@@ -8118,31 +6956,6 @@ public class WindowManagerService extends IWindowManager.Stub
&& (mOrientationChanging || (!mDrawPending && !mCommitDrawPending));
}
- public boolean fillsScreenLw(int screenWidth, int screenHeight,
- boolean shownFrame, boolean onlyOpaque) {
- if (mSurface == null) {
- return false;
- }
- if (mAppToken != null && !mAppToken.appFullscreen) {
- return false;
- }
- if (onlyOpaque && mAttrs.format != PixelFormat.OPAQUE) {
- return false;
- }
- final Rect frame = shownFrame ? mShownFrame : mFrame;
-
- if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
- return frame.left <= mCompatibleScreenFrame.left &&
- frame.top <= mCompatibleScreenFrame.top &&
- frame.right >= mCompatibleScreenFrame.right &&
- frame.bottom >= mCompatibleScreenFrame.bottom;
- } else {
- return frame.left <= 0 && frame.top <= 0
- && frame.right >= screenWidth
- && frame.bottom >= screenHeight;
- }
- }
-
/**
* Return true if the window is opaque and fully drawn. This indicates
* it may obscure windows behind it.
@@ -8176,6 +6989,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
void removeLocked() {
+ disposeInputChannel();
+
if (mAttachedWindow != null) {
mAttachedWindow.mChildWindows.remove(this);
}
@@ -8188,6 +7003,15 @@ public class WindowManagerService extends IWindowManager.Stub
// we are doing this as part of processing a death note.)
}
}
+
+ void disposeInputChannel() {
+ if (mInputChannel != null) {
+ mInputManager.unregisterInputChannel(mInputChannel);
+
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
private class DeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
@@ -8429,6 +7253,11 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
}
}
+
+ String makeInputChannelName() {
+ return Integer.toHexString(System.identityHashCode(this))
+ + " " + mAttrs.getTitle();
+ }
@Override
public String toString() {
@@ -8531,6 +7360,9 @@ public class WindowManagerService extends IWindowManager.Stub
int groupId = -1;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ // The input dispatching timeout for this application token in nanoseconds.
+ long inputDispatchingTimeoutNanos;
// These are used for determining when all windows associated with
// an activity have been drawn, so they can be made visible together
@@ -8738,7 +7570,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = windows.size();
for (int i=0; i<N; i++) {
- ((WindowState)windows.get(i)).finishExit();
+ windows.get(i).finishExit();
}
updateReportedVisibilityLocked();
@@ -8938,6 +7770,7 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int ENABLE_SCREEN = 16;
public static final int APP_FREEZE_TIMEOUT = 17;
public static final int SEND_NEW_CONFIGURATION = 18;
+ public static final int REPORT_WINDOWS_CHANGE = 19;
private Session mLastReportedHold;
@@ -8979,6 +7812,7 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (RemoteException e) {
// Ignore if process has died.
}
+ notifyFocusChanged();
}
if (lastFocus != null) {
@@ -9163,7 +7997,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = mWindows.size();
while (i > 0) {
i--;
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mOrientationChanging) {
w.mOrientationChanging = false;
Slog.w(TAG, "Force clearing orientation change: " + w);
@@ -9186,12 +8020,12 @@ public class WindowManagerService extends IWindowManager.Stub
if (oldHold != newHold) {
try {
if (oldHold != null) {
- mBatteryStats.noteStopWakelock(oldHold.mUid,
+ mBatteryStats.noteStopWakelock(oldHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
if (newHold != null) {
- mBatteryStats.noteStartWakelock(newHold.mUid,
+ mBatteryStats.noteStartWakelock(newHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
@@ -9269,6 +8103,16 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
+ case REPORT_WINDOWS_CHANGE: {
+ if (mWindowsChanged) {
+ synchronized (mWindowMap) {
+ mWindowsChanged = false;
+ }
+ notifyWindowsChanged();
+ }
+ break;
+ }
+
}
}
}
@@ -9281,7 +8125,8 @@ public class WindowManagerService extends IWindowManager.Stub
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
- return new Session(client, inputContext);
+ Session session = new Session(client, inputContext);
+ return session;
}
public boolean inputMethodClientHasFocus(IInputMethodClient client) {
@@ -9291,7 +8136,7 @@ public class WindowManagerService extends IWindowManager.Stub
int idx = findDesiredInputMethodWindowIndexLocked(false);
WindowState imFocus;
if (idx > 0) {
- imFocus = (WindowState)mWindows.get(idx-1);
+ imFocus = mWindows.get(idx-1);
if (imFocus != null) {
if (imFocus.mSession.mClient != null &&
imFocus.mSession.mClient.asBinder() == client.asBinder()) {
@@ -9349,9 +8194,10 @@ public class WindowManagerService extends IWindowManager.Stub
// First remove all existing app windows.
i=0;
while (i < NW) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mAppToken != null) {
- WindowState win = (WindowState)mWindows.remove(i);
+ WindowState win = mWindows.remove(i);
+ mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Rebuild removing window: " + win);
NW--;
@@ -9398,7 +8244,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i;
for (i=0; i<N; i++) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
|| (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
@@ -9443,6 +8289,11 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
+ if (mDisplay == null) {
+ // Not yet initialized, nothing to do.
+ return;
+ }
+
boolean recoveringMemory = false;
if (mForceRemoves != null) {
recoveringMemory = true;
@@ -9487,6 +8338,10 @@ public class WindowManagerService extends IWindowManager.Stub
requestAnimationLocked(0);
}
}
+ if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
+ mH.removeMessages(H.REPORT_WINDOWS_CHANGE);
+ mH.sendMessage(mH.obtainMessage(H.REPORT_WINDOWS_CHANGE));
+ }
} catch (RuntimeException e) {
mInLayout = false;
Slog.e(TAG, "Unhandled exception while layout out windows", e);
@@ -9519,7 +8374,7 @@ public class WindowManagerService extends IWindowManager.Stub
// to another window).
int topAttached = -1;
for (i = N-1; i >= 0; i--) {
- WindowState win = (WindowState) mWindows.get(i);
+ WindowState win = mWindows.get(i);
// Don't do layout of a window if it is not visible, or
// soon won't be visible, to avoid wasting time and funky
@@ -9568,7 +8423,7 @@ public class WindowManagerService extends IWindowManager.Stub
// XXX does not deal with windows that are attached to windows
// that are themselves attached.
for (i = topAttached; i >= 0; i--) {
- WindowState win = (WindowState) mWindows.get(i);
+ WindowState win = mWindows.get(i);
// If this view is GONE, then skip it -- keep the current
// frame, and let the caller know so they can ignore it
@@ -9591,6 +8446,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
+
+ // Window frames may have changed. Tell the input dispatcher about it.
+ mInputMonitor.updateInputWindowsLw();
return mPolicy.finishLayoutLw();
}
@@ -9608,12 +8466,6 @@ public class WindowManagerService extends IWindowManager.Stub
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
}
- if (mFxSession == null) {
- mFxSession = new SurfaceSession();
- }
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
-
// Initialize state of exiting tokens.
for (i=mExitingTokens.size()-1; i>=0; i--) {
mExitingTokens.get(i).hasVisible = false;
@@ -9630,8 +8482,24 @@ public class WindowManagerService extends IWindowManager.Stub
float buttonBrightness = -1;
boolean focusDisplayed = false;
boolean animating = false;
+ boolean createWatermark = false;
+
+ if (mFxSession == null) {
+ mFxSession = new SurfaceSession();
+ createWatermark = true;
+ }
+
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
Surface.openTransaction();
+
+ if (createWatermark) {
+ createWatermark();
+ }
+ if (mWatermark != null) {
+ mWatermark.positionSurface(dw, dh);
+ }
+
try {
boolean wallpaperForceHidingChanged = false;
int repeats = 0;
@@ -9712,7 +8580,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = mWindows.size();
for (i=N-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -10145,7 +9013,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Clear them out.
forceHiding = false;
for (i=N-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mSurface != null) {
final WindowManager.LayoutParams attrs = w.mAttrs;
if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) {
@@ -10195,6 +9063,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
+ Integer.toHexString(changes));
+ mInputMonitor.updateInputWindowsLw();
} while (changes != 0);
// THIRD LOOP: Update the surfaces of all windows.
@@ -10211,7 +9080,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = mWindows.size();
for (i=N-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
boolean displayed = false;
final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -10391,7 +9260,6 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Exception hiding surface in " + w);
}
}
- mKeyWaiter.releasePendingPointerLocked(w.mSession);
}
// If we are waiting for this window to handle an
// orientation change, well, it is hidden, so
@@ -10651,8 +9519,14 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.e(TAG, "Unhandled exception in Window Manager", e);
}
+ mInputMonitor.updateInputWindowsLw();
+
Surface.closeTransaction();
+ if (mWatermark != null) {
+ mWatermark.drawIfNeeded();
+ }
+
if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG,
"With display frozen, orientationChangeComplete="
+ orientationChangeComplete);
@@ -10776,10 +9650,12 @@ public class WindowManagerService extends IWindowManager.Stub
requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
}
+ mInputMonitor.updateInputWindowsLw();
+
if (DEBUG_FREEZE) Slog.v(TAG, "Layout: mDisplayFrozen=" + mDisplayFrozen
+ " holdScreen=" + holdScreen);
if (!mDisplayFrozen) {
- mQueue.setHoldScreenLocked(holdScreen != null);
+ setHoldScreenLocked(holdScreen != null);
if (screenBrightness < 0 || screenBrightness > 1.0f) {
mPowerManager.setScreenBrightnessOverride(-1);
} else {
@@ -10810,6 +9686,21 @@ public class WindowManagerService extends IWindowManager.Stub
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
}
+
+ /**
+ * Must be called with the main window manager lock held.
+ */
+ void setHoldScreenLocked(boolean holding) {
+ boolean state = mHoldingScreenWakeLock.isHeld();
+ if (holding != state) {
+ if (holding) {
+ mHoldingScreenWakeLock.acquire();
+ } else {
+ mPolicy.screenOnStoppedLw();
+ mHoldingScreenWakeLock.release();
+ }
+ }
+ }
void requestAnimationLocked(long delay) {
if (!mAnimationPending) {
@@ -10867,7 +9758,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean leakedSurface = false;
Slog.i(TAG, "Out of memory for surface! Looking for leaks...");
for (int i=0; i<N; i++) {
- WindowState ws = (WindowState)mWindows.get(i);
+ WindowState ws = mWindows.get(i);
if (ws.mSurface != null) {
if (!mSessions.contains(ws.mSession)) {
Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): "
@@ -10899,7 +9790,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "No leaked surfaces; killing applicatons!");
SparseIntArray pidCandidates = new SparseIntArray();
for (int i=0; i<N; i++) {
- WindowState ws = (WindowState)mWindows.get(i);
+ WindowState ws = mWindows.get(i);
if (ws.mSurface != null) {
pidCandidates.append(ws.mSession.mPid, ws.mSession.mPid);
}
@@ -10966,14 +9857,20 @@ public class WindowManagerService extends IWindowManager.Stub
assignLayersLocked();
}
}
-
- if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
- mKeyWaiter.handleNewWindowLocked(newFocus);
+
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
+ // If we defer assigning layers, then the caller is responsible for
+ // doing this part.
+ finishUpdateFocusedWindowAfterAssignLayersLocked();
}
return true;
}
return false;
}
+
+ private void finishUpdateFocusedWindowAfterAssignLayersLocked() {
+ mInputMonitor.setInputFocusLw(mCurrentFocus);
+ }
private WindowState computeFocusedWindowLocked() {
WindowState result = null;
@@ -10985,7 +9882,7 @@ public class WindowManagerService extends IWindowManager.Stub
? mAppTokens.get(nextAppIndex) : null;
while (i >= 0) {
- win = (WindowState)mWindows.get(i);
+ win = mWindows.get(i);
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG, "Looking for focus: " + i
@@ -11046,15 +9943,6 @@ public class WindowManagerService extends IWindowManager.Stub
private void startFreezingDisplayLocked() {
if (mDisplayFrozen) {
- // Freezing the display also suspends key event delivery, to
- // keep events from going astray while the display is reconfigured.
- // If someone has changed orientation again while the screen is
- // still frozen, the events will continue to be blocked while the
- // successive orientation change is processed. To prevent spurious
- // ANRs, we reset the event dispatch timeout in this case.
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
- }
return;
}
@@ -11076,6 +9964,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException());
mDisplayFrozen = true;
+
+ mInputMonitor.freezeInputDispatchingLw();
+
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
mNextAppTransitionPackage = null;
@@ -11107,12 +9998,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
Surface.unfreezeDisplay(0);
- // Reset the key delivery timeout on unfreeze, too. We force a wakeup here
- // too because regular key delivery processing should resume immediately.
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
- mKeyWaiter.notifyAll();
- }
+ mInputMonitor.thawInputDispatchingLw();
// While the display is frozen we don't re-compute the orientation
// to avoid inconsistent states. However, something interesting
@@ -11134,6 +10020,194 @@ public class WindowManagerService extends IWindowManager.Stub
mScreenFrozenLock.release();
}
+ static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps,
+ DisplayMetrics dm) {
+ if (index < tokens.length) {
+ String str = tokens[index];
+ if (str != null && str.length() > 0) {
+ try {
+ int val = Integer.parseInt(str);
+ return val;
+ } catch (Exception e) {
+ }
+ }
+ }
+ if (defUnits == TypedValue.COMPLEX_UNIT_PX) {
+ return defDps;
+ }
+ int val = (int)TypedValue.applyDimension(defUnits, defDps, dm);
+ return val;
+ }
+
+ class Watermark {
+ final String[] mTokens;
+ final String mText;
+ final Paint mTextPaint;
+ final int mTextWidth;
+ final int mTextHeight;
+ final int mTextAscent;
+ final int mTextDescent;
+ final int mDeltaX;
+ final int mDeltaY;
+
+ Surface mSurface;
+ int mLastDW;
+ int mLastDH;
+ boolean mDrawNeeded;
+
+ Watermark(SurfaceSession session, String[] tokens) {
+ final DisplayMetrics dm = new DisplayMetrics();
+ mDisplay.getMetrics(dm);
+
+ if (false) {
+ Log.i(TAG, "*********************** WATERMARK");
+ for (int i=0; i<tokens.length; i++) {
+ Log.i(TAG, " TOKEN #" + i + ": " + tokens[i]);
+ }
+ }
+
+ mTokens = tokens;
+
+ StringBuilder builder = new StringBuilder(32);
+ int len = mTokens[0].length();
+ len = len & ~1;
+ for (int i=0; i<len; i+=2) {
+ int c1 = mTokens[0].charAt(i);
+ int c2 = mTokens[0].charAt(i+1);
+ if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10;
+ else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10;
+ else c1 -= '0';
+ if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10;
+ else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10;
+ else c2 -= '0';
+ builder.append((char)(255-((c1*16)+c2)));
+ }
+ mText = builder.toString();
+ if (false) {
+ Log.i(TAG, "Final text: " + mText);
+ }
+
+ int fontSize = getPropertyInt(tokens, 1,
+ TypedValue.COMPLEX_UNIT_DIP, 20, dm);
+
+ mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.setTextSize(fontSize);
+ mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+
+ FontMetricsInt fm = mTextPaint.getFontMetricsInt();
+ mTextWidth = (int)mTextPaint.measureText(mText);
+ mTextAscent = fm.ascent;
+ mTextDescent = fm.descent;
+ mTextHeight = fm.descent - fm.ascent;
+
+ mDeltaX = getPropertyInt(tokens, 2,
+ TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm);
+ mDeltaY = getPropertyInt(tokens, 3,
+ TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm);
+ int shadowColor = getPropertyInt(tokens, 4,
+ TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm);
+ int color = getPropertyInt(tokens, 5,
+ TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm);
+ int shadowRadius = getPropertyInt(tokens, 6,
+ TypedValue.COMPLEX_UNIT_PX, 7, dm);
+ int shadowDx = getPropertyInt(tokens, 8,
+ TypedValue.COMPLEX_UNIT_PX, 0, dm);
+ int shadowDy = getPropertyInt(tokens, 9,
+ TypedValue.COMPLEX_UNIT_PX, 0, dm);
+
+ mTextPaint.setColor(color);
+ mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
+
+ try {
+ mSurface = new Surface(session, 0,
+ "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
+ mSurface.setLayer(TYPE_LAYER_MULTIPLIER*100);
+ mSurface.setPosition(0, 0);
+ mSurface.show();
+ } catch (OutOfResourcesException e) {
+ }
+ }
+
+ void positionSurface(int dw, int dh) {
+ if (mLastDW != dw || mLastDH != dh) {
+ mLastDW = dw;
+ mLastDH = dh;
+ mSurface.setSize(dw, dh);
+ mDrawNeeded = true;
+ }
+ }
+
+ void drawIfNeeded() {
+ if (mDrawNeeded) {
+ final int dw = mLastDW;
+ final int dh = mLastDH;
+
+ mDrawNeeded = false;
+ Rect dirty = new Rect(0, 0, dw, dh);
+ Canvas c = null;
+ try {
+ c = mSurface.lockCanvas(dirty);
+ } catch (IllegalArgumentException e) {
+ } catch (OutOfResourcesException e) {
+ }
+ if (c != null) {
+ int deltaX = mDeltaX;
+ int deltaY = mDeltaY;
+
+ // deltaX shouldn't be close to a round fraction of our
+ // x step, or else things will line up too much.
+ int div = (dw+mTextWidth)/deltaX;
+ int rem = (dw+mTextWidth) - (div*deltaX);
+ int qdelta = deltaX/4;
+ if (rem < qdelta || rem > (deltaX-qdelta)) {
+ deltaX += deltaX/3;
+ }
+
+ int y = -mTextHeight;
+ int x = -mTextWidth;
+ while (y < (dh+mTextHeight)) {
+ c.drawText(mText, x, y, mTextPaint);
+ x += deltaX;
+ if (x >= dw) {
+ x -= (dw+mTextWidth);
+ y += deltaY;
+ }
+ }
+ mSurface.unlockCanvasAndPost(c);
+ }
+ }
+ }
+ }
+
+ void createWatermark() {
+ if (mWatermark != null) {
+ return;
+ }
+
+ File file = new File("/system/etc/setup.conf");
+ FileInputStream in = null;
+ try {
+ in = new FileInputStream(file);
+ DataInputStream ind = new DataInputStream(in);
+ String line = ind.readLine();
+ if (line != null) {
+ String[] toks = line.split("%");
+ if (toks != null && toks.length > 0) {
+ mWatermark = new Watermark(mFxSession, toks);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
@@ -11144,14 +10218,13 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- pw.println("Input State:");
- mQueue.dump(pw, " ");
+ mInputManager.dump(pw);
pw.println(" ");
synchronized(mWindowMap) {
pw.println("Current Window Manager state:");
for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
pw.print(" Window #"); pw.print(i); pw.print(' ');
pw.print(w); pw.println(":");
w.dump(pw, " ");
@@ -11362,24 +10435,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
- pw.println(" KeyWaiter state:");
- pw.print(" mLastWin="); pw.print(mKeyWaiter.mLastWin);
- pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
- pw.print(" mFinished="); pw.print(mKeyWaiter.mFinished);
- pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
- pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
- pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
}
}
+ // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection).
public void monitor() {
synchronized (mWindowMap) { }
synchronized (mKeyguardTokenWatcher) { }
- synchronized (mKeyWaiter) { }
- }
-
- public void virtualKeyFeedback(KeyEvent event) {
- mPolicy.keyFeedbackFromInput(event);
}
/**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 51ff959..6a05d3c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import com.android.internal.R;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -24,6 +25,7 @@ import com.android.server.ProcessStats;
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.WindowManagerService;
+import com.android.server.am.ActivityStack.ActivityState;
import dalvik.system.Zygote;
@@ -32,19 +34,20 @@ import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.AlertDialog;
+import android.app.AppGlobals;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IActivityController;
-import android.app.IActivityManager;
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
+import android.app.INotificationManager;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ResultInfo;
import android.app.Service;
import android.app.backup.IBackupManager;
import android.content.ActivityNotFoundException;
@@ -70,6 +73,7 @@ import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -88,11 +92,11 @@ import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -102,21 +106,30 @@ import android.util.Slog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -127,7 +140,8 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor {
+public final class ActivityManagerService extends ActivityManagerNative
+ implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
static final String TAG = "ActivityManager";
static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
@@ -139,6 +153,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final boolean DEBUG_BROADCAST = localLOGV || false;
static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
static final boolean DEBUG_SERVICE = localLOGV || false;
+ static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
static final boolean DEBUG_VISBILITY = localLOGV || false;
static final boolean DEBUG_PROCESSES = localLOGV || false;
static final boolean DEBUG_PROVIDER = localLOGV || false;
@@ -147,6 +162,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final boolean DEBUG_RESULTS = localLOGV || false;
static final boolean DEBUG_BACKUP = localLOGV || false;
static final boolean DEBUG_CONFIGURATION = localLOGV || false;
+ static final boolean DEBUG_POWER = localLOGV || false;
+ static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
static final boolean VALIDATE_TOKENS = false;
static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -177,47 +194,32 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Maximum number of recent tasks that we can remember.
static final int MAX_RECENT_TASKS = 20;
-
+
// Amount of time after a call to stopAppSwitches() during which we will
// prevent further untrusted switches from happening.
static final long APP_SWITCH_DELAY_TIME = 5*1000;
-
- // How long until we reset a task when the user returns to it. Currently
- // 30 minutes.
- static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
-
- // Set to true to disable the icon that is shown while a new activity
- // is being started.
- static final boolean SHOW_APP_STARTING_ICON = true;
-
- // How long we wait until giving up on the last activity to pause. This
- // is short because it directly impacts the responsiveness of starting the
- // next activity.
- static final int PAUSE_TIMEOUT = 500;
-
- /**
- * How long we can hold the launch wake lock before giving up.
- */
- static final int LAUNCH_TIMEOUT = 10*1000;
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
- // How long we wait until giving up on the last activity telling us it
- // is idle.
- static final int IDLE_TIMEOUT = 10*1000;
-
// How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000;
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
- // How long we wait until giving up on an activity telling us it has
- // finished destroying itself.
- static final int DESTROY_TIMEOUT = 10*1000;
-
+ // The rate at which we check for apps using excessive power -- 15 mins.
+ static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000;
+
+ // The minimum sample duration we will allow before deciding we have
+ // enough data on wake locks to start killing things.
+ static final int WAKE_LOCK_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
+
+ // The minimum sample duration we will allow before deciding we have
+ // enough data on CPU usage to start killing things.
+ static final int CPU_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
+
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_TIMEOUT = 10*1000;
@@ -284,6 +286,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// system/rootdir/init.rc on startup.
static final int SECONDARY_SERVER_ADJ;
+ // This is a process with a heavy-weight application. It is in the
+ // background, but we want to try to avoid killing it. Value set in
+ // system/rootdir/init.rc on startup.
+ static final int HEAVY_WEIGHT_APP_ADJ;
+
+ // This is a process only hosting components that are perceptible to the
+ // user, and we really want to avoid killing them, but they are not
+ // immediately visible. An example is background music playback. Value set in
+ // system/rootdir/init.rc on startup.
+ static final int PERCEPTIBLE_APP_ADJ;
+
// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear. Value set in
// system/rootdir/init.rc on startup.
@@ -309,6 +322,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final int HOME_APP_MEM;
static final int BACKUP_APP_MEM;
static final int SECONDARY_SERVER_MEM;
+ static final int HEAVY_WEIGHT_APP_MEM;
+ static final int PERCEPTIBLE_APP_MEM;
static final int VISIBLE_APP_MEM;
static final int FOREGROUND_APP_MEM;
@@ -329,68 +344,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// been idle for less than 120 seconds.
static final long EMPTY_APP_IDLE_OFFSET = 120*1000;
+ static int getIntProp(String name, boolean allowZero) {
+ String str = SystemProperties.get(name);
+ if (str == null) {
+ throw new IllegalArgumentException("Property not defined: " + name);
+ }
+ int val = Integer.valueOf(str);
+ if (val == 0 && !allowZero) {
+ throw new IllegalArgumentException("Property must not be zero: " + name);
+ }
+ return val;
+ }
+
static {
// These values are set in system/rootdir/init.rc on startup.
- FOREGROUND_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_ADJ"));
- VISIBLE_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ"));
- SECONDARY_SERVER_ADJ =
- Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ"));
- BACKUP_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ"));
- HOME_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ"));
- HIDDEN_APP_MIN_ADJ =
- Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MIN_ADJ"));
- EMPTY_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_ADJ"));
- HIDDEN_APP_MAX_ADJ = EMPTY_APP_ADJ-1;
- FOREGROUND_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_MEM"))*PAGE_SIZE;
- VISIBLE_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE;
- SECONDARY_SERVER_MEM =
- Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
- BACKUP_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE;
- HOME_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE;
- HIDDEN_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MEM"))*PAGE_SIZE;
- EMPTY_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_MEM"))*PAGE_SIZE;
+ FOREGROUND_APP_ADJ = getIntProp("ro.FOREGROUND_APP_ADJ", true);
+ VISIBLE_APP_ADJ = getIntProp("ro.VISIBLE_APP_ADJ", true);
+ PERCEPTIBLE_APP_ADJ = getIntProp("ro.PERCEPTIBLE_APP_ADJ", true);
+ HEAVY_WEIGHT_APP_ADJ = getIntProp("ro.HEAVY_WEIGHT_APP_ADJ", true);
+ SECONDARY_SERVER_ADJ = getIntProp("ro.SECONDARY_SERVER_ADJ", true);
+ BACKUP_APP_ADJ = getIntProp("ro.BACKUP_APP_ADJ", true);
+ HOME_APP_ADJ = getIntProp("ro.HOME_APP_ADJ", true);
+ HIDDEN_APP_MIN_ADJ = getIntProp("ro.HIDDEN_APP_MIN_ADJ", true);
+ EMPTY_APP_ADJ = getIntProp("ro.EMPTY_APP_ADJ", true);
+ // These days we use the last empty slot for hidden apps as well.
+ HIDDEN_APP_MAX_ADJ = EMPTY_APP_ADJ;
+ FOREGROUND_APP_MEM = getIntProp("ro.FOREGROUND_APP_MEM", false)*PAGE_SIZE;
+ VISIBLE_APP_MEM = getIntProp("ro.VISIBLE_APP_MEM", false)*PAGE_SIZE;
+ PERCEPTIBLE_APP_MEM = getIntProp("ro.PERCEPTIBLE_APP_MEM", false)*PAGE_SIZE;
+ HEAVY_WEIGHT_APP_MEM = getIntProp("ro.HEAVY_WEIGHT_APP_MEM", false)*PAGE_SIZE;
+ SECONDARY_SERVER_MEM = getIntProp("ro.SECONDARY_SERVER_MEM", false)*PAGE_SIZE;
+ BACKUP_APP_MEM = getIntProp("ro.BACKUP_APP_MEM", false)*PAGE_SIZE;
+ HOME_APP_MEM = getIntProp("ro.HOME_APP_MEM", false)*PAGE_SIZE;
+ HIDDEN_APP_MEM = getIntProp("ro.HIDDEN_APP_MEM", false)*PAGE_SIZE;
+ EMPTY_APP_MEM = getIntProp("ro.EMPTY_APP_MEM", false)*PAGE_SIZE;
}
static final int MY_PID = Process.myPid();
static final String[] EMPTY_STRING_ARRAY = new String[0];
- enum ActivityState {
- INITIALIZING,
- RESUMED,
- PAUSING,
- PAUSED,
- STOPPING,
- STOPPED,
- FINISHING,
- DESTROYING,
- DESTROYED
- }
-
- /**
- * The back history of all previous (and possibly still
- * running) activities. It contains HistoryRecord objects.
- */
- final ArrayList mHistory = new ArrayList();
-
+ public ActivityStack mMainStack;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
*/
- class PendingActivityLaunch {
- HistoryRecord r;
- HistoryRecord sourceRecord;
+ static class PendingActivityLaunch {
+ ActivityRecord r;
+ ActivityRecord sourceRecord;
Uri[] grantedUriPermissions;
int grantedMode;
boolean onlyIfNeeded;
@@ -400,18 +402,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new ArrayList<PendingActivityLaunch>();
/**
- * List of people waiting to find out about the next launched activity.
- */
- final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched
- = new ArrayList<IActivityManager.WaitResult>();
-
- /**
- * List of people waiting to find out about the next visible activity.
- */
- final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
- = new ArrayList<IActivityManager.WaitResult>();
-
- /**
* List of all active broadcasts that are to be executed immediately
* (without waiting for another broadcast to finish). Currently this only
* contains broadcasts to registered receivers, to avoid spinning up
@@ -441,56 +431,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean mBroadcastsScheduled = false;
/**
- * Set to indicate whether to issue an onUserLeaving callback when a
- * newly launched activity is being brought in front of us.
- */
- boolean mUserLeaving = false;
-
- /**
- * When we are in the process of pausing an activity, before starting the
- * next one, this variable holds the activity that is currently being paused.
- */
- HistoryRecord mPausingActivity = null;
-
- /**
- * Current activity that is resumed, or null if there is none.
- */
- HistoryRecord mResumedActivity = null;
-
- /**
* Activity we have told the window manager to have key focus.
*/
- HistoryRecord mFocusedActivity = null;
-
- /**
- * This is the last activity that we put into the paused state. This is
- * used to determine if we need to do an activity transition while sleeping,
- * when we normally hold the top activity paused.
- */
- HistoryRecord mLastPausedActivity = null;
-
- /**
- * List of activities that are waiting for a new activity
- * to become visible before completing whatever operation they are
- * supposed to do.
- */
- final ArrayList mWaitingVisibleActivities = new ArrayList();
-
- /**
- * List of activities that are ready to be stopped, but waiting
- * for the next activity to settle down before doing so. It contains
- * HistoryRecord objects.
- */
- final ArrayList<HistoryRecord> mStoppingActivities
- = new ArrayList<HistoryRecord>();
-
- /**
- * Animations that for the current transition have requested not to
- * be considered for the transition animation.
- */
- final ArrayList<HistoryRecord> mNoAnimActivities
- = new ArrayList<HistoryRecord>();
-
+ ActivityRecord mFocusedActivity = null;
/**
* List of intents that were used to start the most recent tasks.
*/
@@ -498,13 +441,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new ArrayList<TaskRecord>();
/**
- * List of activities that are ready to be finished, but waiting
- * for the previous activity to settle down before doing so. It contains
- * HistoryRecord objects.
- */
- final ArrayList mFinishingActivities = new ArrayList();
-
- /**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
* returned by the package manager), and the keys are ApplicationRecord
@@ -514,6 +450,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new ProcessMap<ProcessRecord>();
/**
+ * The currently running heavy-weight process, if any.
+ */
+ ProcessRecord mHeavyWeightProcess = null;
+
+ /**
* The last time that various processes have crashed.
*/
final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
@@ -599,16 +540,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* This is the process holding what we currently consider to be
* the "home" activity.
*/
- private ProcessRecord mHomeProcess;
+ ProcessRecord mHomeProcess;
/**
- * List of running activities, sorted by recent usage.
- * The first entry in the list is the least recently used.
- * It contains HistoryRecord objects.
- */
- private final ArrayList mLRUActivities = new ArrayList();
-
- /**
* Set of PendingResultRecord objects that are currently active.
*/
final HashSet mPendingResultRecords = new HashSet();
@@ -620,6 +554,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
/**
+ * Fingerprints (String.hashCode()) of stack traces that we've
+ * already logged DropBox entries for. Guarded by itself. If
+ * something (rogue user app) forces this over
+ * MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
+ */
+ private final HashSet<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>();
+ private static final int MAX_DUP_SUPPRESSED_STACKS = 5000;
+
+ /**
+ * Strict Mode background batched logging state.
+ *
+ * The string buffer is guarded by itself, and its lock is also
+ * used to determine if another batched write is already
+ * in-flight.
+ */
+ private final StringBuilder mStrictModeBuffer = new StringBuilder();
+
+ /**
+ * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
+ */
+ private boolean mPendingBroadcastTimeoutMessage;
+
+ /**
* Intent broadcast that we have tried to start, but are
* waiting for its application's process to be created. We only
* need one (instead of a list) because we always process broadcasts
@@ -629,6 +586,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
BroadcastRecord mPendingBroadcast = null;
/**
+ * The receiver index that is pending, to restart the broadcast if needed.
+ */
+ int mPendingBroadcastRecvIndex;
+
+ /**
* Keeps track of all IIntentReceivers that have been registered for
* broadcasts. Hash keys are the receiver IBinder, hash value is
* a ReceiverList.
@@ -678,8 +640,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* All currently bound service connections. Keys are the IBinder of
* the client's IServiceConnection.
*/
- final HashMap<IBinder, ConnectionRecord> mServiceConnections
- = new HashMap<IBinder, ConnectionRecord>();
+ final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections
+ = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
/**
* List of services that we have been asked to start,
@@ -727,21 +689,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* that a single provider may be published under multiple names, so
* there may be multiple entries here for a single one in mProvidersByClass.
*/
- final HashMap mProvidersByName = new HashMap();
+ final HashMap<String, ContentProviderRecord> mProvidersByName
+ = new HashMap<String, ContentProviderRecord>();
/**
* All of the currently running global content providers. Keys are a
* string containing the provider's implementation class and values are a
* ContentProviderRecord object containing the data about it.
*/
- final HashMap mProvidersByClass = new HashMap();
+ final HashMap<String, ContentProviderRecord> mProvidersByClass
+ = new HashMap<String, ContentProviderRecord>();
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
* removed from this list once it is published.
*/
- final ArrayList mLaunchingProviders = new ArrayList();
+ final ArrayList<ContentProviderRecord> mLaunchingProviders
+ = new ArrayList<ContentProviderRecord>();
/**
* Global set of specific Uri permissions that have been granted.
@@ -790,12 +755,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int mConfigurationSeq = 0;
/**
- * Set when we know we are going to be calling updateConfiguration()
- * soon, so want to skip intermediate config checks.
- */
- boolean mConfigWillChange;
-
- /**
* Hardware-reported OpenGLES version.
*/
final int GL_ES_VERSION;
@@ -818,10 +777,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ComponentName mTopComponent;
String mTopAction;
String mTopData;
+ boolean mProcessesReady = false;
boolean mSystemReady = false;
boolean mBooting = false;
boolean mWaitingUpdate = false;
boolean mDidUpdate = false;
+ boolean mOnBattery = false;
+ boolean mLaunchWarningShown = false;
Context mContext;
@@ -842,30 +804,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean mDidAppSwitch;
/**
- * Set while we are wanting to sleep, to prevent any
- * activities from being started/resumed.
+ * Last time (in realtime) at which we checked for power usage.
*/
- boolean mSleeping = false;
+ long mLastPowerCheckRealtime;
/**
- * Set if we are shutting down the system, similar to sleeping.
+ * Last time (in uptime) at which we checked for power usage.
*/
- boolean mShuttingDown = false;
-
+ long mLastPowerCheckUptime;
+
/**
- * Set when the system is going to sleep, until we have
- * successfully paused the current activity and released our wake lock.
- * At that point the system is allowed to actually sleep.
+ * Set while we are wanting to sleep, to prevent any
+ * activities from being started/resumed.
*/
- PowerManager.WakeLock mGoingToSleep;
+ boolean mSleeping = false;
/**
- * We don't want to allow the device to go to sleep while in the process
- * of launching an activity. This is primarily to allow alarm intent
- * receivers to launch an activity and get that to run before the device
- * goes back to sleep.
+ * Set if we are shutting down the system, similar to sleeping.
*/
- PowerManager.WakeLock mLaunchingActivity;
+ boolean mShuttingDown = false;
/**
* Task identifier that activities are currently being started
@@ -945,8 +902,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
long mLastWriteTime = 0;
- long mInitialStartTime = 0;
-
/**
* Set to true after the system has finished booting.
*/
@@ -978,7 +933,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (localLOGV) Slog.v(
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
- removeRequestedPss(mApp);
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread);
}
@@ -993,20 +947,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final int WAIT_FOR_DEBUGGER_MSG = 6;
static final int BROADCAST_INTENT_MSG = 7;
static final int BROADCAST_TIMEOUT_MSG = 8;
- static final int PAUSE_TIMEOUT_MSG = 9;
- static final int IDLE_TIMEOUT_MSG = 10;
- static final int IDLE_NOW_MSG = 11;
static final int SERVICE_TIMEOUT_MSG = 12;
static final int UPDATE_TIME_ZONE = 13;
static final int SHOW_UID_ERROR_MSG = 14;
static final int IM_FEELING_LUCKY_MSG = 15;
- static final int LAUNCH_TIMEOUT_MSG = 16;
- static final int DESTROY_TIMEOUT_MSG = 17;
- static final int RESUME_TOP_ACTIVITY_MSG = 19;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
+ static final int POST_HEAVY_NOTIFICATION_MSG = 24;
+ static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
+ static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
+ static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
AlertDialog mUidAlert;
@@ -1048,18 +1000,47 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return;
}
- broadcastIntentLocked(null, null, new Intent("android.intent.action.ANR"),
+ Intent intent = new Intent("android.intent.action.ANR");
+ if (!mProcessesReady) {
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
+ broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
false, false, MY_PID, Process.SYSTEM_UID);
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
- mContext, proc, (HistoryRecord)data.get("activity"));
+ mContext, proc, (ActivityRecord)data.get("activity"));
d.show();
proc.anrDialog = d;
}
ensureBootCompleted();
} break;
+ case SHOW_STRICT_MODE_VIOLATION_MSG: {
+ HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
+ synchronized (ActivityManagerService.this) {
+ ProcessRecord proc = (ProcessRecord) data.get("app");
+ if (proc == null) {
+ Slog.e(TAG, "App not found when showing strict mode dialog.");
+ break;
+ }
+ if (proc.crashDialog != null) {
+ Slog.e(TAG, "App already has strict mode dialog: " + proc);
+ return;
+ }
+ AppErrorResult res = (AppErrorResult) data.get("result");
+ if (!mSleeping && !mShuttingDown) {
+ Dialog d = new StrictModeViolationDialog(mContext, res, proc);
+ d.show();
+ proc.crashDialog = d;
+ } else {
+ // The device is asleep, so just pretend that the user
+ // saw a crash dialog and hit "force quit".
+ res.set(0);
+ }
+ }
+ ensureBootCompleted();
+ } break;
case SHOW_FACTORY_ERROR_MSG: {
Dialog d = new FactoryErrorDialog(
mContext, msg.getData().getCharSequence("msg"));
@@ -1101,50 +1082,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
- if (mDidDexOpt) {
- mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT);
- return;
- }
- // Only process broadcast timeouts if the system is ready. That way
- // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
- // to do heavy lifting for system up
- if (mSystemReady) {
- broadcastTimeout();
- }
- } break;
- case PAUSE_TIMEOUT_MSG: {
- IBinder token = (IBinder)msg.obj;
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- Slog.w(TAG, "Activity pause timeout for " + token);
- activityPaused(token, null, true);
- } break;
- case IDLE_TIMEOUT_MSG: {
- if (mDidDexOpt) {
- mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
- nmsg.obj = msg.obj;
- mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
- return;
+ synchronized (ActivityManagerService.this) {
+ broadcastTimeoutLocked(true);
}
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- IBinder token = (IBinder)msg.obj;
- Slog.w(TAG, "Activity idle timeout for " + token);
- activityIdleInternal(token, true, null);
- } break;
- case DESTROY_TIMEOUT_MSG: {
- IBinder token = (IBinder)msg.obj;
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- Slog.w(TAG, "Activity destroy timeout for " + token);
- activityDestroyed(token);
- } break;
- case IDLE_NOW_MSG: {
- IBinder token = (IBinder)msg.obj;
- activityIdle(token, null);
} break;
case SERVICE_TIMEOUT_MSG: {
if (mDidDexOpt) {
@@ -1188,25 +1128,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mUidAlert = null;
}
} break;
- case LAUNCH_TIMEOUT_MSG: {
- if (mDidDexOpt) {
- mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
- return;
- }
- synchronized (ActivityManagerService.this) {
- if (mLaunchingActivity.isHeld()) {
- Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
- mLaunchingActivity.release();
- }
- }
- } break;
- case RESUME_TOP_ACTIVITY_MSG: {
- synchronized (ActivityManagerService.this) {
- resumeTopActivityLocked(null);
- }
- } break;
case PROC_START_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
@@ -1236,6 +1157,70 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
case FINALIZE_PENDING_INTENT_MSG: {
((PendingIntentRecord)msg.obj).completeFinalize();
} break;
+ case POST_HEAVY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ ActivityRecord root = (ActivityRecord)msg.obj;
+ ProcessRecord process = root.app;
+ if (process == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(process.info.packageName, 0);
+ String text = mContext.getString(R.string.heavy_weight_notification,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_sys_adb; //context.getApplicationInfo().icon;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.tickerText = text;
+ notification.defaults = 0; // please be quiet
+ notification.sound = null;
+ notification.vibrate = null;
+ notification.setLatestEventInfo(context, text,
+ mContext.getText(R.string.heavy_weight_notification_detail),
+ PendingIntent.getActivity(mContext, 0, root.intent,
+ PendingIntent.FLAG_CANCEL_CURRENT));
+
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotification("android", R.string.heavy_weight_notification,
+ notification, outId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for heavy-weight app", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for heavy notification", e);
+ }
+ } break;
+ case CANCEL_HEAVY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotification("android",
+ R.string.heavy_weight_notification);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
+ case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
+ synchronized (ActivityManagerService.this) {
+ checkExcessivePowerUsageLocked(true);
+ removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
+ }
+ } break;
}
}
};
@@ -1299,11 +1284,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Context context = at.getSystemContext();
m.mContext = context;
m.mFactoryTest = factoryTest;
- PowerManager pm =
- (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- m.mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
- m.mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
- m.mLaunchingActivity.setReferenceCounted(false);
+ m.mMainStack = new ActivityStack(m, context, true);
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
@@ -1335,6 +1316,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ android.os.Process.setCanSelfBackground(false);
ActivityManagerService m = new ActivityManagerService();
@@ -1405,7 +1387,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mActivityManagerService.mProcessStatsThread) {
- pw.print(mActivityManagerService.mProcessStats.printCurrentState());
+ pw.print(mActivityManagerService.mProcessStats.printCurrentLoad());
+ pw.print(mActivityManagerService.mProcessStats.printCurrentState(
+ SystemClock.uptimeMillis()));
}
}
}
@@ -1428,9 +1412,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mBatteryStatsService = new BatteryStatsService(new File(
systemDir, "batterystats.bin").toString());
mBatteryStatsService.getActiveStatistics().readLocked();
- mBatteryStatsService.getActiveStatistics().writeLocked();
+ mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
+ mOnBattery = DEBUG_POWER ? true
+ : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
+ mBatteryStatsService.getActiveStatistics().setCallback(this);
- mUsageStatsService = new UsageStatsService( new File(
+ mUsageStatsService = new UsageStatsService(new File(
systemDir, "usagestats").toString());
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
@@ -1543,37 +1530,68 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(bstats) {
synchronized(mPidsSelfLocked) {
if (haveNewCpuStats) {
- if (mBatteryStatsService.isOnBattery()) {
- final int N = mProcessStats.countWorkingStats();
+ if (mOnBattery) {
+ int perc = bstats.startAddingCpuLocked();
+ int totalUTime = 0;
+ int totalSTime = 0;
+ final int N = mProcessStats.countStats();
for (int i=0; i<N; i++) {
- ProcessStats.Stats st
- = mProcessStats.getWorkingStats(i);
+ ProcessStats.Stats st = mProcessStats.getStats(i);
+ if (!st.working) {
+ continue;
+ }
ProcessRecord pr = mPidsSelfLocked.get(st.pid);
+ int otherUTime = (st.rel_utime*perc)/100;
+ int otherSTime = (st.rel_stime*perc)/100;
+ totalUTime += otherUTime;
+ totalSTime += otherSTime;
if (pr != null) {
BatteryStatsImpl.Uid.Proc ps = pr.batteryStats;
- ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+ ps.addCpuTimeLocked(st.rel_utime-otherUTime,
+ st.rel_stime-otherSTime);
ps.addSpeedStepTimes(cpuSpeedTimes);
+ pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10;
} else {
BatteryStatsImpl.Uid.Proc ps =
bstats.getProcessStatsLocked(st.name, st.pid);
if (ps != null) {
- ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+ ps.addCpuTimeLocked(st.rel_utime-otherUTime,
+ st.rel_stime-otherSTime);
ps.addSpeedStepTimes(cpuSpeedTimes);
}
}
}
+ bstats.finishAddingCpuLocked(perc, totalUTime,
+ totalSTime, cpuSpeedTimes);
}
}
}
if (mLastWriteTime < (now-BATTERY_STATS_TIME)) {
mLastWriteTime = now;
- mBatteryStatsService.getActiveStatistics().writeLocked();
+ mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
}
}
}
}
+ @Override
+ public void batteryNeedsCpuUpdate() {
+ updateCpuStatsNow();
+ }
+
+ @Override
+ public void batteryPowerChanged(boolean onBattery) {
+ // When plugging in, update the CPU stats first before changing
+ // the plug state.
+ updateCpuStatsNow();
+ synchronized (this) {
+ synchronized(mPidsSelfLocked) {
+ mOnBattery = DEBUG_POWER ? true : onBattery;
+ }
+ }
+ }
+
/**
* Initialize the application bind args. These are passed to each
* process when the bindApplication() IPC is sent to the process. They're
@@ -1592,7 +1610,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return mAppBindArgs;
}
- private final void setFocusedActivityLocked(HistoryRecord r) {
+ final void setFocusedActivityLocked(ActivityRecord r) {
if (mFocusedActivity != r) {
mFocusedActivity = r;
mWindowManager.setFocusedApp(r, true);
@@ -1677,65 +1695,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void updateLruProcessLocked(ProcessRecord app,
+ final void updateLruProcessLocked(ProcessRecord app,
boolean oomAdj, boolean updateActivityTime) {
mLruSeq++;
updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0);
}
- private final boolean updateLRUListLocked(HistoryRecord r) {
- final boolean hadit = mLRUActivities.remove(r);
- mLRUActivities.add(r);
- return hadit;
- }
-
- private final HistoryRecord topRunningActivityLocked(HistoryRecord notTop) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (!r.finishing && r != notTop) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- private final HistoryRecord topRunningNonDelayedActivityLocked(HistoryRecord notTop) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (!r.finishing && !r.delayedResume && r != notTop) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- /**
- * This is a simplified version of topRunningActivityLocked that provides a number of
- * optional skip-over modes. It is intended for use with the ActivityController hook only.
- *
- * @param token If non-null, any history records matching this token will be skipped.
- * @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
- *
- * @return Returns the HistoryRecord of the next activity on the stack.
- */
- private final HistoryRecord topRunningActivityLocked(IBinder token, int taskId) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- // Note: the taskId check depends on real taskId fields being non-zero
- if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- private final ProcessRecord getProcessRecordLocked(
+ final ProcessRecord getProcessRecordLocked(
String processName, int uid) {
if (uid == Process.SYSTEM_UID) {
// The system gets to run in any process. If there are multiple
@@ -1749,8 +1715,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return proc;
}
- private void ensurePackageDexOpt(String packageName) {
- IPackageManager pm = ActivityThread.getPackageManager();
+ void ensurePackageDexOpt(String packageName) {
+ IPackageManager pm = AppGlobals.getPackageManager();
try {
if (pm.performDexOpt(packageName)) {
mDidDexOpt = true;
@@ -1759,157 +1725,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private boolean isNextTransitionForward() {
+ boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
|| transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
|| transit == WindowManagerPolicy.TRANSIT_TASK_TO_FRONT;
}
- private final boolean realStartActivityLocked(HistoryRecord r,
- ProcessRecord app, boolean andResume, boolean checkConfig)
- throws RemoteException {
-
- r.startFreezingScreenLocked(app, 0);
- mWindowManager.setAppVisibility(r, true);
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order. Note that
- // as a result of this, it can call back into the activity
- // manager with a new orientation. We don't care about that,
- // because the activity is not currently running so we are
- // just restarting it anyway.
- if (checkConfig) {
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- r.mayFreezeScreenLocked(app) ? r : null);
- updateConfigurationLocked(config, r);
- }
-
- r.app = app;
-
- if (localLOGV) Slog.v(TAG, "Launching: " + r);
-
- int idx = app.activities.indexOf(r);
- if (idx < 0) {
- app.activities.add(r);
- }
- updateLruProcessLocked(app, true, true);
-
- try {
- if (app.thread == null) {
- throw new RemoteException();
- }
- List<ResultInfo> results = null;
- List<Intent> newIntents = null;
- if (andResume) {
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
- + " icicle=" + r.icicle
- + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
- if (andResume) {
- EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
- }
- if (r.isHomeActivity) {
- mHomeProcess = app;
- }
- ensurePackageDexOpt(r.intent.getComponent().getPackageName());
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
- System.identityHashCode(r),
- r.info, r.icicle, results, newIntents, !andResume,
- isNextTransitionForward());
- } catch (RemoteException e) {
- if (r.launchFailed) {
- // This is the second time we failed -- finish activity
- // and give up.
- Slog.e(TAG, "Second failure launching "
- + r.intent.getComponent().flattenToShortString()
- + ", giving up", e);
- appDiedLocked(app, app.pid, app.thread);
- requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
- "2nd-crash");
- return false;
- }
-
- // This is the first time we failed -- restart process and
- // retry.
- app.activities.remove(r);
- throw e;
- }
-
- r.launchFailed = false;
- if (updateLRUListLocked(r)) {
- Slog.w(TAG, "Activity " + r
- + " being launched, but already in LRU list");
- }
-
- if (andResume) {
- // As part of the process of launching, ActivityThread also performs
- // a resume.
- r.state = ActivityState.RESUMED;
- r.icicle = null;
- r.haveState = false;
- r.stopped = false;
- mResumedActivity = r;
- r.task.touchActiveTime();
- completeResumeLocked(r);
- pauseIfSleepingLocked();
- } else {
- // This activity is not starting in the resumed state... which
- // should look like we asked it to pause+stop (but remain visible),
- // and it has done so and reported back the current icicle and
- // other state.
- r.state = ActivityState.STOPPED;
- r.stopped = true;
- }
-
- // Launch the new version setup screen if needed. We do this -after-
- // launching the initial activity (that is, home), so that it can have
- // a chance to initialize itself while in the background, making the
- // switch back to it faster and look better.
- startSetupActivityLocked();
-
- return true;
- }
-
- private final void startSpecificActivityLocked(HistoryRecord r,
- boolean andResume, boolean checkConfig) {
- // Is this activity's application already running?
- ProcessRecord app = getProcessRecordLocked(r.processName,
- r.info.applicationInfo.uid);
-
- if (r.startTime == 0) {
- r.startTime = SystemClock.uptimeMillis();
- if (mInitialStartTime == 0) {
- mInitialStartTime = r.startTime;
- }
- } else if (mInitialStartTime == 0) {
- mInitialStartTime = SystemClock.uptimeMillis();
- }
-
- if (app != null && app.thread != null) {
- try {
- realStartActivityLocked(r, app, andResume, checkConfig);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when starting activity "
- + r.intent.getComponent().flattenToShortString(), e);
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent(), false);
- }
-
- private final ProcessRecord startProcessLocked(String processName,
+ final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
ProcessRecord app = getProcessRecordLocked(processName, info.uid);
@@ -1927,10 +1750,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (!knownToBeDead || app.thread == null) {
// We already have the app running, or are waiting for it to
// come up (we have a pid but not yet its thread), so keep it.
+ if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
return app;
} else {
// An application record is attached to a previous process,
// clean it up now.
+ if (DEBUG_PROCESSES) Slog.v(TAG, "App died: " + app);
handleAppDiedLocked(app, true);
}
}
@@ -1942,6 +1767,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// If we are in the background, then check to see if this process
// is bad. If so, we will just silently fail.
if (mBadProcesses.get(info.processName, info.uid) != null) {
+ if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+ + "/" + info.processName);
return null;
}
} else {
@@ -1949,6 +1776,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// crash count so that we won't make it bad until they see at
// least one crash dialog again, and make the process good again
// if it had been bad.
+ if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+ + "/" + info.processName);
mProcessCrashTimes.remove(info.processName, info.uid);
if (mBadProcesses.get(info.processName, info.uid) != null) {
EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid,
@@ -1970,12 +1799,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// If the system is not ready yet, then hold off on starting this
// process until it is.
- if (!mSystemReady
+ if (!mProcessesReady
&& !isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if (!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
+ if (DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);
return app;
}
@@ -1997,6 +1827,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
app.pid = 0;
}
+ if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
+ "startProcessLocked removing on hold: " + app);
mProcessesOnHold.remove(app);
updateCpuStats();
@@ -2055,7 +1887,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
hostingNameStr != null ? hostingNameStr : "");
if (app.persistent) {
- Watchdog.getInstance().processStarted(app, app.processName, pid);
+ Watchdog.getInstance().processStarted(app.processName, pid);
}
StringBuilder buf = mStringBuilder;
@@ -2110,365 +1942,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
- if (mPausingActivity != null) {
- RuntimeException e = new RuntimeException();
- Slog.e(TAG, "Trying to pause when pause is already pending for "
- + mPausingActivity, e);
- }
- HistoryRecord prev = mResumedActivity;
- if (prev == null) {
- RuntimeException e = new RuntimeException();
- Slog.e(TAG, "Trying to pause when nothing is resumed", e);
- resumeTopActivityLocked(null);
- return;
- }
- if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
- mResumedActivity = null;
- mPausingActivity = prev;
- mLastPausedActivity = prev;
- prev.state = ActivityState.PAUSING;
- prev.task.touchActiveTime();
-
- updateCpuStats();
-
- if (prev.app != null && prev.app.thread != null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
- try {
- EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
- System.identityHashCode(prev),
- prev.shortComponentName);
- prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
- prev.configChangeFlags);
- updateUsageStats(prev, false);
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- }
- } else {
- mPausingActivity = null;
- mLastPausedActivity = null;
- }
-
- // If we are not going to sleep, we want to ensure the device is
- // awake until the next activity is started.
- if (!mSleeping && !mShuttingDown) {
- mLaunchingActivity.acquire();
- if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
- // To be safe, don't allow the wake lock to be held for too long.
- Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
- }
- }
-
-
- if (mPausingActivity != null) {
- // Have the window manager pause its key dispatching until the new
- // activity has started. If we're pausing the activity just because
- // the screen is being turned off and the UI is sleeping, don't interrupt
- // key dispatch; the same activity will pick it up again on wakeup.
- if (!uiSleeping) {
- prev.pauseKeyDispatchingLocked();
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
- }
-
- // Schedule a pause timeout in case the app doesn't respond.
- // We don't give it much time because this directly impacts the
- // responsiveness seen by the user.
- Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
- msg.obj = prev;
- mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
- if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
- } else {
- // This activity failed to schedule the
- // pause, so just treat it as being paused now.
- if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
- resumeTopActivityLocked(null);
- }
- }
-
- private final void completePauseLocked() {
- HistoryRecord prev = mPausingActivity;
- if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
-
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
- prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
- } else if (prev.app != null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
- if (prev.waitingVisible) {
- prev.waitingVisible = false;
- mWaitingVisibleActivities.remove(prev);
- if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
- TAG, "Complete pause, no longer waiting: " + prev);
- }
- if (prev.configDestroy) {
- // The previous is being paused because the configuration
- // is changing, which means it is actually stopping...
- // To juggle the fact that we are also starting a new
- // instance right now, we need to first completely stop
- // the current instance before starting the new one.
- if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
- destroyActivityLocked(prev, true);
- } else {
- mStoppingActivities.add(prev);
- if (mStoppingActivities.size() > 3) {
- // If we already have a few activities waiting to stop,
- // then give up on things going idle and start clearing
- // them out.
- if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
- }
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
- prev = null;
- }
- mPausingActivity = null;
- }
-
- if (!mSleeping && !mShuttingDown) {
- resumeTopActivityLocked(prev);
- } else {
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
- }
- if (mShuttingDown) {
- notifyAll();
- }
- }
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
- }
-
- if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) {
- long diff = 0;
- synchronized (mProcessStatsThread) {
- diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume;
- }
- if (diff > 0) {
- BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
- synchronized (bsi) {
- BatteryStatsImpl.Uid.Proc ps =
- bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
- prev.info.packageName);
- if (ps != null) {
- ps.addForegroundTimeLocked(diff);
- }
- }
- }
- }
- prev.cpuTimeAtResume = 0; // reset it
- }
-
- /**
- * Once we know that we have asked an application to put an activity in
- * the resumed state (either by launching it or explicitly telling it),
- * this function updates the rest of our state to match that fact.
- */
- private final void completeResumeLocked(HistoryRecord next) {
- next.idle = false;
- next.results = null;
- next.newIntents = null;
-
- // schedule an idle timeout in case the app doesn't do it for us.
- Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
- msg.obj = next;
- mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
-
- if (false) {
- // The activity was never told to pause, so just keep
- // things going as-is. To maintain our own state,
- // we need to emulate it coming back and saying it is
- // idle.
- msg = mHandler.obtainMessage(IDLE_NOW_MSG);
- msg.obj = next;
- mHandler.sendMessage(msg);
- }
-
- reportResumedActivityLocked(next);
-
- next.thumbnail = null;
- setFocusedActivityLocked(next);
- next.resumeKeyDispatchingLocked();
- ensureActivitiesVisibleLocked(null, 0);
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
-
- // Mark the point when the activity is resuming
- // TODO: To be more accurate, the mark should be before the onCreate,
- // not after the onResume. But for subsequent starts, onResume is fine.
- if (next.app != null) {
- synchronized (mProcessStatsThread) {
- next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid);
- }
- } else {
- next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
- }
- }
-
- /**
- * Make sure that all activities that need to be visible (that is, they
- * currently can be seen by the user) actually are.
- */
- private final void ensureActivitiesVisibleLocked(HistoryRecord top,
- HistoryRecord starting, String onlyThisProcess, int configChanges) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "ensureActivitiesVisible behind " + top
- + " configChanges=0x" + Integer.toHexString(configChanges));
-
- // If the top activity is not fullscreen, then we need to
- // make sure any activities under it are now visible.
- final int count = mHistory.size();
- int i = count-1;
- while (mHistory.get(i) != top) {
- i--;
- }
- HistoryRecord r;
- boolean behindFullscreen = false;
- for (; i>=0; i--) {
- r = (HistoryRecord)mHistory.get(i);
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Make visible? " + r + " finishing=" + r.finishing
- + " state=" + r.state);
- if (r.finishing) {
- continue;
- }
-
- final boolean doThisProcess = onlyThisProcess == null
- || onlyThisProcess.equals(r.processName);
-
- // First: if this is not the current activity being started, make
- // sure it matches the current configuration.
- if (r != starting && doThisProcess) {
- ensureActivityConfigurationLocked(r, 0);
- }
-
- if (r.app == null || r.app.thread == null) {
- if (onlyThisProcess == null
- || onlyThisProcess.equals(r.processName)) {
- // This activity needs to be visible, but isn't even
- // running... get it started, but don't resume it
- // at this point.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Start and freeze screen for " + r);
- if (r != starting) {
- r.startFreezingScreenLocked(r.app, configChanges);
- }
- if (!r.visible) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Starting and making visible: " + r);
- mWindowManager.setAppVisibility(r, true);
- }
- if (r != starting) {
- startSpecificActivityLocked(r, false, false);
- }
- }
-
- } else if (r.visible) {
- // If this activity is already visible, then there is nothing
- // else to do here.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Skipping: already visible at " + r);
- r.stopFreezingScreenLocked(false);
-
- } else if (onlyThisProcess == null) {
- // This activity is not currently visible, but is running.
- // Tell it to become visible.
- r.visible = true;
- if (r.state != ActivityState.RESUMED && r != starting) {
- // If this activity is paused, tell it
- // to now show its window.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Making visible and scheduling visibility: " + r);
- try {
- mWindowManager.setAppVisibility(r, true);
- r.app.thread.scheduleWindowVisibility(r, true);
- r.stopFreezingScreenLocked(false);
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making visibile: "
- + r.intent.getComponent(), e);
- }
- }
- }
-
- // Aggregate current change flags.
- configChanges |= r.configChangeFlags;
-
- if (r.fullscreen) {
- // At this point, nothing else needs to be shown
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Stopping: fullscreen at " + r);
- behindFullscreen = true;
- i--;
- break;
- }
- }
-
- // Now for any activities that aren't visible to the user, make
- // sure they no longer are keeping the screen frozen.
- while (i >= 0) {
- r = (HistoryRecord)mHistory.get(i);
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Make invisible? " + r + " finishing=" + r.finishing
- + " state=" + r.state
- + " behindFullscreen=" + behindFullscreen);
- if (!r.finishing) {
- if (behindFullscreen) {
- if (r.visible) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Making invisible: " + r);
- r.visible = false;
- try {
- mWindowManager.setAppVisibility(r, false);
- if ((r.state == ActivityState.STOPPING
- || r.state == ActivityState.STOPPED)
- && r.app != null && r.app.thread != null) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Scheduling invisibility: " + r);
- r.app.thread.scheduleWindowVisibility(r, false);
- }
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making hidden: "
- + r.intent.getComponent(), e);
- }
- } else {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Already invisible: " + r);
- }
- } else if (r.fullscreen) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Now behindFullscreen: " + r);
- behindFullscreen = true;
- }
- }
- i--;
- }
- }
-
- /**
- * Version of ensureActivitiesVisible that can easily be called anywhere.
- */
- private final void ensureActivitiesVisibleLocked(HistoryRecord starting,
- int configChanges) {
- HistoryRecord r = topRunningActivityLocked(null);
- if (r != null) {
- ensureActivitiesVisibleLocked(r, starting, null, configChanges);
- }
- }
-
- private void updateUsageStats(HistoryRecord resumedComponent, boolean resumed) {
+ void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) {
if (resumed) {
mUsageStatsService.noteResumeComponent(resumedComponent.realActivity);
} else {
@@ -2476,7 +1950,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private boolean startHomeActivityLocked() {
+ boolean startHomeActivityLocked() {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -2503,7 +1977,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivityLocked(null, intent, null, null, 0, aInfo,
+ mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
null, null, 0, 0, 0, false, false);
}
}
@@ -2515,7 +1989,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
/**
* Starts the "new version setup screen" if appropriate.
*/
- private void startSetupActivityLocked() {
+ void startSetupActivityLocked() {
// Only do this once per boot.
if (mCheckedForSetup) {
return;
@@ -2559,14 +2033,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
- startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
+ mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
null, null, 0, 0, 0, false, false);
}
}
}
}
- private void reportResumedActivityLocked(HistoryRecord r) {
+ void reportResumedActivityLocked(ActivityRecord r) {
//Slog.i(TAG, "**** REPORT RESUME: " + r);
final int identHash = System.identityHashCode(r);
@@ -2585,1210 +2059,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
mWatchers.finishBroadcast();
}
-
- /**
- * Ensure that the top activity in the stack is resumed.
- *
- * @param prev The previously resumed activity, for when in the process
- * of pausing; can be null to call from elsewhere.
- *
- * @return Returns true if something is being resumed, or false if
- * nothing happened.
- */
- private final boolean resumeTopActivityLocked(HistoryRecord prev) {
- // Find the first activity that is not finishing.
- HistoryRecord next = topRunningActivityLocked(null);
-
- // Remember how we'll process this pause/resume situation, and ensure
- // that the state is reset however we wind up proceeding.
- final boolean userLeaving = mUserLeaving;
- mUserLeaving = false;
-
- if (next == null) {
- // There are no more activities! Let's just start up the
- // Launcher...
- return startHomeActivityLocked();
- }
-
- next.delayedResume = false;
-
- // If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return false;
- }
-
- // If we are sleeping, and there is no resumed activity, and the top
- // activity is paused, well that is the state we want.
- if ((mSleeping || mShuttingDown)
- && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return false;
- }
-
- // The activity may be waiting for stop, but that is no longer
- // appropriate for it.
- mStoppingActivities.remove(next);
- mWaitingVisibleActivities.remove(next);
-
- if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
-
- // If we are currently pausing an activity, then don't do anything
- // until that is done.
- if (mPausingActivity != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
- return false;
- }
-
- // We need to start pausing the current activity so the top one
- // can be resumed...
- if (mResumedActivity != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
- startPausingLocked(userLeaving, false);
- return true;
- }
-
- if (prev != null && prev != next) {
- if (!prev.waitingVisible && next != null && !next.nowVisible) {
- prev.waitingVisible = true;
- mWaitingVisibleActivities.add(prev);
- if (DEBUG_SWITCH) Slog.v(
- TAG, "Resuming top, waiting visible to hide: " + prev);
- } else {
- // The next activity is already visible, so hide the previous
- // activity's windows right now so we can show the new one ASAP.
- // We only do this if the previous is finishing, which should mean
- // it is on top of the one being resumed so hiding it quickly
- // is good. Otherwise, we want to do the normal route of allowing
- // the resumed activity to be shown so we can decide if the
- // previous should actually be hidden depending on whether the
- // new one is found to be full-screen or not.
- if (prev.finishing) {
- mWindowManager.setAppVisibility(prev, false);
- if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
- + prev + ", waitingVisible="
- + (prev != null ? prev.waitingVisible : null)
- + ", nowVisible=" + next.nowVisible);
- } else {
- if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
- + prev + ", waitingVisible="
- + (prev != null ? prev.waitingVisible : null)
- + ", nowVisible=" + next.nowVisible);
- }
- }
- }
-
- // We are starting up the next activity, so tell the window manager
- // that the previous one will be hidden soon. This way it can know
- // to ignore it when computing the desired screen orientation.
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare close transition: prev=" + prev);
- if (mNoAnimActivities.contains(prev)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
- : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
- }
- mWindowManager.setAppWillBeHidden(prev);
- mWindowManager.setAppVisibility(prev, false);
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: prev=" + prev);
- if (mNoAnimActivities.contains(next)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
- : WindowManagerPolicy.TRANSIT_TASK_OPEN);
- }
- }
- if (false) {
- mWindowManager.setAppWillBeHidden(prev);
- mWindowManager.setAppVisibility(prev, false);
- }
- } else if (mHistory.size() > 1) {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: no previous");
- if (mNoAnimActivities.contains(next)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
- }
- }
-
- if (next.app != null && next.app.thread != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
-
- // This activity is now becoming visible.
- mWindowManager.setAppVisibility(next, true);
-
- HistoryRecord lastResumedActivity = mResumedActivity;
- ActivityState lastState = next.state;
-
- updateCpuStats();
-
- next.state = ActivityState.RESUMED;
- mResumedActivity = next;
- next.task.touchActiveTime();
- updateLruProcessLocked(next.app, true, true);
- updateLRUListLocked(next);
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean updated;
- synchronized (this) {
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next : null);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- updated = updateConfigurationLocked(config, next);
- }
- if (!updated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- HistoryRecord nextNext = topRunningActivityLocked(null);
- if (DEBUG_SWITCH) Slog.i(TAG,
- "Activity config changed during resume: " + next
- + ", new next: " + nextNext);
- if (nextNext != next) {
- // Do over!
- mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
- }
- setFocusedActivityLocked(next);
- ensureActivitiesVisibleLocked(null, 0);
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return true;
- }
-
- try {
- // Deliver all pending results.
- ArrayList a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Delivering results to " + next
- + ": " + a);
- next.app.thread.scheduleSendResult(next, a);
- }
- }
-
- if (next.newIntents != null) {
- next.app.thread.scheduleNewIntent(next.newIntents, next);
- }
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
- System.identityHashCode(next),
- next.task.taskId, next.shortComponentName);
-
- next.app.thread.scheduleResumeActivity(next,
- isNextTransitionForward());
-
- pauseIfSleepingLocked();
-
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- next.state = lastState;
- mResumedActivity = lastResumedActivity;
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_ICON) {
- mWindowManager.setAppStartingWindow(
- next, next.packageName, next.theme,
- next.nonLocalizedLabel,
- next.labelRes, next.icon, null, true);
- }
- }
- startSpecificActivityLocked(next, true, false);
- return true;
- }
-
- // From this point on, if something goes wrong there is no way
- // to recover the activity.
- try {
- next.visible = true;
- completeResumeLocked(next);
- } catch (Exception e) {
- // If any exception gets thrown, toss away this
- // activity and try the next one.
- Slog.w(TAG, "Exception thrown during resume of " + next, e);
- requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
- "resume-exception");
- return true;
- }
-
- // Didn't need to use the icicle, and it is now out of date.
- next.icicle = null;
- next.haveState = false;
- next.stopped = false;
-
- } else {
- // Whoops, need to restart this activity!
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_ICON) {
- mWindowManager.setAppStartingWindow(
- next, next.packageName, next.theme,
- next.nonLocalizedLabel,
- next.labelRes, next.icon, null, true);
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
- }
- startSpecificActivityLocked(next, true, true);
- }
-
- return true;
- }
-
- private final void startActivityLocked(HistoryRecord r, boolean newTask,
- boolean doResume) {
- final int NH = mHistory.size();
-
- int addPos = -1;
-
- if (!newTask) {
- // If starting in an existing task, find where that is...
- HistoryRecord next = null;
- boolean startIt = true;
- for (int i = NH-1; i >= 0; i--) {
- HistoryRecord p = (HistoryRecord)mHistory.get(i);
- if (p.finishing) {
- continue;
- }
- if (p.task == r.task) {
- // Here it is! Now, if this is not yet visible to the
- // user, then just add it without starting; it will
- // get started when the user navigates back to it.
- addPos = i+1;
- if (!startIt) {
- mHistory.add(addPos, r);
- r.inHistory = true;
- r.task.numActivities++;
- mWindowManager.addAppToken(addPos, r, r.task.taskId,
- r.info.screenOrientation, r.fullscreen);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- return;
- }
- break;
- }
- if (p.fullscreen) {
- startIt = false;
- }
- next = p;
- }
- }
-
- // Place a new activity at top of stack, so it is next to interact
- // with the user.
- if (addPos < 0) {
- addPos = mHistory.size();
- }
-
- // If we are not placing the new activity frontmost, we do not want
- // to deliver the onUserLeaving callback to the actual frontmost
- // activity
- if (addPos < NH) {
- mUserLeaving = false;
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false");
- }
-
- // Slot the activity into the history stack and proceed
- mHistory.add(addPos, r);
- r.inHistory = true;
- r.frontOfTask = newTask;
- r.task.numActivities++;
- if (NH > 0) {
- // We want to show the starting preview window if we are
- // switching to a new task, or the next activity's process is
- // not currently running.
- boolean showStartingIcon = newTask;
- ProcessRecord proc = r.app;
- if (proc == null) {
- proc = mProcessNames.get(r.processName, r.info.applicationInfo.uid);
- }
- if (proc == null || proc.thread == null) {
- showStartingIcon = true;
- }
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: starting " + r);
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- mNoAnimActivities.add(r);
- } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_OPEN);
- mNoAnimActivities.remove(r);
- } else {
- mWindowManager.prepareAppTransition(newTask
- ? WindowManagerPolicy.TRANSIT_TASK_OPEN
- : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
- mNoAnimActivities.remove(r);
- }
- mWindowManager.addAppToken(
- addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
- boolean doShow = true;
- if (newTask) {
- // Even though this activity is starting fresh, we still need
- // to reset it to make sure we apply affinities to move any
- // existing activities from other tasks in to it.
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((r.intent.getFlags()
- &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- resetTaskIfNeededLocked(r, r);
- doShow = topRunningNonDelayedActivityLocked(null) == r;
- }
- }
- if (SHOW_APP_STARTING_ICON && doShow) {
- // Figure out if we are transitioning from another activity that is
- // "has the same starting icon" as the next one. This allows the
- // window manager to keep the previous window it had previously
- // created, if it still had one.
- HistoryRecord prev = mResumedActivity;
- if (prev != null) {
- // We don't want to reuse the previous starting preview if:
- // (1) The current activity is in a different task.
- if (prev.task != r.task) prev = null;
- // (2) The current activity is already displayed.
- else if (prev.nowVisible) prev = null;
- }
- mWindowManager.setAppStartingWindow(
- r, r.packageName, r.theme, r.nonLocalizedLabel,
- r.labelRes, r.icon, prev, showStartingIcon);
- }
- } else {
- // If this is the first activity, don't do any fancy animations,
- // because there is nothing for it to animate on top of.
- mWindowManager.addAppToken(addPos, r, r.task.taskId,
- r.info.screenOrientation, r.fullscreen);
- }
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- }
-
- /**
- * Perform clear operation as requested by
- * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
- * stack to the given task, then look for
- * an instance of that activity in the stack and, if found, finish all
- * activities on top of it and return the instance.
- *
- * @param newR Description of the new activity being started.
- * @return Returns the old activity that should be continue to be used,
- * or null if none was found.
- */
- private final HistoryRecord performClearTaskLocked(int taskId,
- HistoryRecord newR, int launchFlags, boolean doClear) {
- int i = mHistory.size();
-
- // First find the requested task.
- while (i > 0) {
- i--;
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (r.task.taskId == taskId) {
- i++;
- break;
- }
- }
-
- // Now clear it.
- while (i > 0) {
- i--;
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (r.task.taskId != taskId) {
- return null;
- }
- if (r.realActivity.equals(newR.realActivity)) {
- // Here it is! Now finish everything in front...
- HistoryRecord ret = r;
- if (doClear) {
- while (i < (mHistory.size()-1)) {
- i++;
- r = (HistoryRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- i--;
- }
- }
- }
-
- // Finally, if this is a normal launch mode (that is, not
- // expecting onNewIntent()), then we will finish the current
- // instance of the activity so a new fresh one can be started.
- if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
- && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
- if (!ret.finishing) {
- int index = indexOfTokenLocked(ret);
- if (index >= 0) {
- finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
- null, "clear");
- }
- return null;
- }
- }
-
- return ret;
- }
- }
-
- return null;
- }
-
- /**
- * Find the activity in the history stack within the given task. Returns
- * the index within the history at which it's found, or < 0 if not found.
- */
- private final int findActivityInHistoryLocked(HistoryRecord r, int task) {
- int i = mHistory.size();
- while (i > 0) {
- i--;
- HistoryRecord candidate = (HistoryRecord)mHistory.get(i);
- if (candidate.task.taskId != task) {
- break;
- }
- if (candidate.realActivity.equals(r.realActivity)) {
- return i;
- }
- }
-
- return -1;
- }
-
- /**
- * Reorder the history stack so that the activity at the given index is
- * brought to the front.
- */
- private final HistoryRecord moveActivityToFrontLocked(int where) {
- HistoryRecord newTop = (HistoryRecord)mHistory.remove(where);
- int top = mHistory.size();
- HistoryRecord oldTop = (HistoryRecord)mHistory.get(top-1);
- mHistory.add(top, newTop);
- oldTop.frontOfTask = false;
- newTop.frontOfTask = true;
- return newTop;
- }
-
- /**
- * Deliver a new Intent to an existing activity, so that its onNewIntent()
- * method will be called at the proper time.
- */
- private final void deliverNewIntentLocked(HistoryRecord r, Intent intent) {
- boolean sent = false;
- if (r.state == ActivityState.RESUMED
- && r.app != null && r.app.thread != null) {
- try {
- ArrayList<Intent> ar = new ArrayList<Intent>();
- ar.add(new Intent(intent));
- r.app.thread.scheduleNewIntent(ar, r);
- sent = true;
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending new intent to " + r, e);
- }
- }
- if (!sent) {
- r.addNewIntentLocked(new Intent(intent));
- }
- }
-
- private final void logStartActivity(int tag, HistoryRecord r,
- TaskRecord task) {
- EventLog.writeEvent(tag,
- System.identityHashCode(r), task.taskId,
- r.shortComponentName, r.intent.getAction(),
- r.intent.getType(), r.intent.getDataString(),
- r.intent.getFlags());
- }
-
- private final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType,
- Uri[] grantedUriPermissions,
- int grantedMode, ActivityInfo aInfo, IBinder resultTo,
- String resultWho, int requestCode,
- int callingPid, int callingUid, boolean onlyIfNeeded,
- boolean componentSpecified) {
- Slog.i(TAG, "Starting activity: " + intent);
-
- HistoryRecord sourceRecord = null;
- HistoryRecord resultRecord = null;
- if (resultTo != null) {
- int index = indexOfTokenLocked(resultTo);
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Sending result to " + resultTo + " (index " + index + ")");
- if (index >= 0) {
- sourceRecord = (HistoryRecord)mHistory.get(index);
- if (requestCode >= 0 && !sourceRecord.finishing) {
- resultRecord = sourceRecord;
- }
- }
- }
-
- int launchFlags = intent.getFlags();
-
- if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
- && sourceRecord != null) {
- // Transfer the result target from the source activity to the new
- // one being started, including any failures.
- if (requestCode >= 0) {
- return START_FORWARD_AND_REQUEST_CONFLICT;
- }
- resultRecord = sourceRecord.resultTo;
- resultWho = sourceRecord.resultWho;
- requestCode = sourceRecord.requestCode;
- sourceRecord.resultTo = null;
- if (resultRecord != null) {
- resultRecord.removeResultsLocked(
- sourceRecord, resultWho, requestCode);
- }
- }
-
- int err = START_SUCCESS;
-
- if (intent.getComponent() == null) {
- // We couldn't find a class that can handle the given Intent.
- // That's the end of that!
- err = START_INTENT_NOT_RESOLVED;
- }
-
- if (err == START_SUCCESS && aInfo == null) {
- // We couldn't find the specific class specified in the Intent.
- // Also the end of the line.
- err = START_CLASS_NOT_FOUND;
- }
-
- ProcessRecord callerApp = null;
- if (err == START_SUCCESS && caller != null) {
- callerApp = getRecordForAppLocked(caller);
- if (callerApp != null) {
- callingPid = callerApp.pid;
- callingUid = callerApp.info.uid;
- } else {
- Slog.w(TAG, "Unable to find app for caller " + caller
- + " (pid=" + callingPid + ") when starting: "
- + intent.toString());
- err = START_PERMISSION_DENIED;
- }
- }
-
- if (err != START_SUCCESS) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- return err;
- }
-
- final int perm = checkComponentPermission(aInfo.permission, callingPid,
- callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- String msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + aInfo.permission;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- if (mController != null) {
- boolean abort = false;
- try {
- // The Intent we give to the watcher has the extra data
- // stripped off, since it can contain private information.
- Intent watchIntent = intent.cloneFilter();
- abort = !mController.activityStarting(watchIntent,
- aInfo.applicationInfo.packageName);
- } catch (RemoteException e) {
- mController = null;
- }
-
- if (abort) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- // We pretend to the caller that it was really started, but
- // they will just get a cancel result.
- return START_SUCCESS;
- }
- }
-
- HistoryRecord r = new HistoryRecord(this, callerApp, callingUid,
- intent, resolvedType, aInfo, mConfiguration,
- resultRecord, resultWho, requestCode, componentSpecified);
-
- if (mResumedActivity == null
- || mResumedActivity.info.applicationInfo.uid != callingUid) {
- if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
- PendingActivityLaunch pal = new PendingActivityLaunch();
- pal.r = r;
- pal.sourceRecord = sourceRecord;
- pal.grantedUriPermissions = grantedUriPermissions;
- pal.grantedMode = grantedMode;
- pal.onlyIfNeeded = onlyIfNeeded;
- mPendingActivityLaunches.add(pal);
- return START_SWITCHES_CANCELED;
- }
- }
-
- if (mDidAppSwitch) {
- // This is the second allowed switch since we stopped switches,
- // so now just generally allow switches. Use case: user presses
- // home (switches disabled, switch to home, mDidAppSwitch now true);
- // user taps a home icon (coming from home so allowed, we hit here
- // and now allow anyone to switch again).
- mAppSwitchesAllowedTime = 0;
- } else {
- mDidAppSwitch = true;
- }
-
- doPendingActivityLaunchesLocked(false);
-
- return startActivityUncheckedLocked(r, sourceRecord,
- grantedUriPermissions, grantedMode, onlyIfNeeded, true);
- }
-
- private final void doPendingActivityLaunchesLocked(boolean doResume) {
+ final void doPendingActivityLaunchesLocked(boolean doResume) {
final int N = mPendingActivityLaunches.size();
if (N <= 0) {
return;
}
for (int i=0; i<N; i++) {
PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
- startActivityUncheckedLocked(pal.r, pal.sourceRecord,
+ mMainStack.startActivityUncheckedLocked(pal.r, pal.sourceRecord,
pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded,
doResume && i == (N-1));
}
mPendingActivityLaunches.clear();
}
- private final int startActivityUncheckedLocked(HistoryRecord r,
- HistoryRecord sourceRecord, Uri[] grantedUriPermissions,
- int grantedMode, boolean onlyIfNeeded, boolean doResume) {
- final Intent intent = r.intent;
- final int callingUid = r.launchedFromUid;
-
- int launchFlags = intent.getFlags();
-
- // We'll invoke onUserLeaving before onPause only if the launching
- // activity did not explicitly state that this is an automated launch.
- mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
- if (DEBUG_USER_LEAVING) Slog.v(TAG,
- "startActivity() => mUserLeaving=" + mUserLeaving);
-
- // If the caller has asked not to resume at this point, we make note
- // of this in the record so that we can skip it when trying to find
- // the top running activity.
- if (!doResume) {
- r.delayedResume = true;
- }
-
- HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
- != 0 ? r : null;
-
- // If the onlyIfNeeded flag is set, then we can do this if the activity
- // being launched is the same as the one making the call... or, as
- // a special case, if we do not know the caller then we count the
- // current top activity as the caller.
- if (onlyIfNeeded) {
- HistoryRecord checkedCaller = sourceRecord;
- if (checkedCaller == null) {
- checkedCaller = topRunningNonDelayedActivityLocked(notTop);
- }
- if (!checkedCaller.realActivity.equals(r.realActivity)) {
- // Caller is not the same as launcher, so always needed.
- onlyIfNeeded = false;
- }
- }
-
- if (grantedUriPermissions != null && callingUid > 0) {
- for (int i=0; i<grantedUriPermissions.length; i++) {
- grantUriPermissionLocked(callingUid, r.packageName,
- grantedUriPermissions[i], grantedMode, r);
- }
- }
-
- grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- intent, r);
-
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
- + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // The original activity who is starting us is running as a single
- // instance... this new activity it is starting must go on its
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
-
- if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // For whatever reason this activity is being launched into a new
- // task... yet the caller has requested a result back. Well, that
- // is pretty messed up, so instead immediately send back a cancel
- // and let the new task continue launched as normal without a
- // dependency on its originator.
- Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- r.resultTo = null;
- }
-
- boolean addingToTask = false;
- if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // If bring to front is requested, and no result is requested, and
- // we can find a task that was started with this same
- // component, then instead of launching bring that one to the front.
- if (r.resultTo == null) {
- // See if there is a task to bring to the front. If this is
- // a SINGLE_INSTANCE activity, there can be one and only one
- // instance of it in the history, and it is always in its own
- // unique task, so we do a special search.
- HistoryRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
- ? findTaskLocked(intent, r.info)
- : findActivityLocked(intent, r.info);
- if (taskTop != null) {
- if (taskTop.task.intent == null) {
- // This task was started because of movement of
- // the activity based on affinity... now that we
- // are actually launching it, we can assign the
- // base intent.
- taskTop.task.setIntent(intent, r.info);
- }
- // If the target task is not in the front, then we need
- // to bring it to the front... except... well, with
- // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
- // to have the same behavior as if a new instance was
- // being started, which means not bringing it to the front
- // if the caller is not itself in the front.
- HistoryRecord curTop = topRunningNonDelayedActivityLocked(notTop);
- if (curTop != null && curTop.task != taskTop.task) {
- r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- boolean callerAtFront = sourceRecord == null
- || curTop.task == sourceRecord.task;
- if (callerAtFront) {
- // We really do want to push this one into the
- // user's face, right now.
- moveTaskToFrontLocked(taskTop.task, r);
- }
- }
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- taskTop = resetTaskIfNeededLocked(taskTop, r);
- }
- if (onlyIfNeeded) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it! And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return START_RETURN_INTENT_TO_CALLER;
- }
- if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // In this situation we want to remove all activities
- // from the task up to the one being started. In most
- // cases this means we are resetting the task to its
- // initial state.
- HistoryRecord top = performClearTaskLocked(
- taskTop.task.taskId, r, launchFlags, true);
- if (top != null) {
- if (top.frontOfTask) {
- // Activity aliases may mean we use different
- // intents for the top activity, so make sure
- // the task now has the identity of the new
- // intent.
- top.task.setIntent(r.intent, r.info);
- }
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- deliverNewIntentLocked(top, r.intent);
- } else {
- // A special case: we need to
- // start the activity because it is not currently
- // running, and the caller has asked to clear the
- // current task to have this activity at the top.
- addingToTask = true;
- // Now pretend like this activity is being started
- // by the top of its task, so it is put in the
- // right place.
- sourceRecord = taskTop;
- }
- } else if (r.realActivity.equals(taskTop.task.realActivity)) {
- // In this case the top activity on the task is the
- // same as the one being launched, so we take that
- // as a request to bring the task to the foreground.
- // If the top activity in the task is the root
- // activity, deliver this new intent to it if it
- // desires.
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- && taskTop.realActivity.equals(r.realActivity)) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
- if (taskTop.frontOfTask) {
- taskTop.task.setIntent(r.intent, r.info);
- }
- deliverNewIntentLocked(taskTop, r.intent);
- } else if (!r.intent.filterEquals(taskTop.task.intent)) {
- // In this case we are launching the root activity
- // of the task, but with a different intent. We
- // should start a new instance on top.
- addingToTask = true;
- sourceRecord = taskTop;
- }
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
- // In this case an activity is being launched in to an
- // existing task, without resetting that task. This
- // is typically the situation of launching an activity
- // from a notification or shortcut. We want to place
- // the new activity on top of the current task.
- addingToTask = true;
- sourceRecord = taskTop;
- } else if (!taskTop.task.rootWasReset) {
- // In this case we are launching in to an existing task
- // that has not yet been started from its front door.
- // The current task has been brought to the front.
- // Ideally, we'd probably like to place this new task
- // at the bottom of its stack, but that's a little hard
- // to do with the current organization of the code so
- // for now we'll just drop it.
- taskTop.task.setIntent(r.intent, r.info);
- }
- if (!addingToTask) {
- // We didn't do anything... but it was needed (a.k.a., client
- // don't use that intent!) And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return START_TASK_TO_FRONT;
- }
- }
- }
- }
-
- //String uri = r.intent.toURI();
- //Intent intent2 = new Intent(uri);
- //Slog.i(TAG, "Given intent: " + r.intent);
- //Slog.i(TAG, "URI is: " + uri);
- //Slog.i(TAG, "To intent: " + intent2);
-
- if (r.packageName != null) {
- // If the activity being launched is the same as the one currently
- // at the top, then we need to check if it should only be launched
- // once.
- HistoryRecord top = topRunningNonDelayedActivityLocked(notTop);
- if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity)) {
- if (top.app != null && top.app.thread != null) {
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- if (onlyIfNeeded) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it!
- return START_RETURN_INTENT_TO_CALLER;
- }
- deliverNewIntentLocked(top, r.intent);
- return START_DELIVERED_TO_TOP;
- }
- }
- }
- }
-
- } else {
- if (r.resultTo != null) {
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- }
- return START_CLASS_NOT_FOUND;
- }
-
- boolean newTask = false;
-
- // Should this be considered a new task?
- if (r.resultTo == null && !addingToTask
- && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // todo: should do better management of integers.
- mCurTask++;
- if (mCurTask <= 0) {
- mCurTask = 1;
- }
- r.task = new TaskRecord(mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new task " + r.task);
- newTask = true;
- addRecentTaskLocked(r.task);
-
- } else if (sourceRecord != null) {
- if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- // In this case, we are adding the activity to an existing
- // task, but the caller has asked to clear that task if the
- // activity is already running.
- HistoryRecord top = performClearTaskLocked(
- sourceRecord.task.taskId, r, launchFlags, true);
- if (top != null) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- deliverNewIntentLocked(top, r.intent);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return START_DELIVERED_TO_TOP;
- }
- } else if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
- // In this case, we are launching an activity in our own task
- // that may already be running somewhere in the history, and
- // we want to shuffle it to the front of the stack if so.
- int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
- if (where >= 0) {
- HistoryRecord top = moveActivityToFrontLocked(where);
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- deliverNewIntentLocked(top, r.intent);
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return START_DELIVERED_TO_TOP;
- }
- }
- // An existing activity is starting this new activity, so we want
- // to keep the new one in the same task as the one that is starting
- // it.
- r.task = sourceRecord.task;
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in existing task " + r.task);
-
- } else {
- // This not being started from an existing activity, and not part
- // of a new task... just put it in the top task, though these days
- // this case should never happen.
- final int N = mHistory.size();
- HistoryRecord prev =
- N > 0 ? (HistoryRecord)mHistory.get(N-1) : null;
- r.task = prev != null
- ? prev.task
- : new TaskRecord(mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new guessed " + r.task);
- }
- if (newTask) {
- EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
- }
- logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
- startActivityLocked(r, newTask, doResume);
- return START_SUCCESS;
- }
-
- void reportActivityLaunchedLocked(boolean timeout, HistoryRecord r,
- long thisTime, long totalTime) {
- for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
- WaitResult w = mWaitingActivityLaunched.get(i);
- w.timeout = timeout;
- if (r != null) {
- w.who = new ComponentName(r.info.packageName, r.info.name);
- }
- w.thisTime = thisTime;
- w.totalTime = totalTime;
- }
- notify();
- }
-
- void reportActivityVisibleLocked(HistoryRecord r) {
- for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
- WaitResult w = mWaitingActivityVisible.get(i);
- w.timeout = false;
- if (r != null) {
- w.who = new ComponentName(r.info.packageName, r.info.name);
- }
- w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
- w.thisTime = w.totalTime;
- }
- notify();
- }
-
- private final int startActivityMayWait(IApplicationThread caller,
- Intent intent, String resolvedType, Uri[] grantedUriPermissions,
- int grantedMode, IBinder resultTo,
- String resultWho, int requestCode, boolean onlyIfNeeded,
- boolean debug, WaitResult outResult, Configuration config) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- final boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
- // Collect information about the target of the Intent.
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- ActivityThread.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | STOCK_PM_FLAGS);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
-
- if (aInfo != null) {
- // Store the found target back into the intent, because now that
- // we have it we never want to do this again. For example, if the
- // user navigates back to this point in the history, we should
- // always restart the exact same activity.
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
-
- // Don't debug things in the system process
- if (debug) {
- if (!aInfo.processName.equals("system")) {
- setDebugApp(aInfo.processName, true, false);
- }
- }
- }
-
- synchronized (this) {
- int callingPid;
- int callingUid;
- if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
- } else {
- callingPid = callingUid = -1;
- }
-
- mConfigWillChange = config != null && mConfiguration.diff(config) != 0;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Starting activity when config will change = " + mConfigWillChange);
-
- final long origId = Binder.clearCallingIdentity();
-
- int res = startActivityLocked(caller, intent, resolvedType,
- grantedUriPermissions, grantedMode, aInfo,
- resultTo, resultWho, requestCode, callingPid, callingUid,
- onlyIfNeeded, componentSpecified);
-
- if (mConfigWillChange) {
- // If the caller also wants to switch to a new configuration,
- // do so now. This allows a clean switch, as we are waiting
- // for the current activity to pause (so we will not destroy
- // it), and have not yet started the next activity.
- enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
- "updateConfiguration()");
- mConfigWillChange = false;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Updating to new configuration after starting activity.");
- updateConfigurationLocked(config, null);
- }
-
- Binder.restoreCallingIdentity(origId);
-
- if (outResult != null) {
- outResult.result = res;
- if (res == IActivityManager.START_SUCCESS) {
- mWaitingActivityLaunched.add(outResult);
- do {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- } else if (res == IActivityManager.START_TASK_TO_FRONT) {
- HistoryRecord r = this.topRunningActivityLocked(null);
- if (r.nowVisible) {
- outResult.timeout = false;
- outResult.who = new ComponentName(r.info.packageName, r.info.name);
- outResult.totalTime = 0;
- outResult.thisTime = 0;
- } else {
- outResult.thisTime = SystemClock.uptimeMillis();
- mWaitingActivityVisible.add(outResult);
- do {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- }
- }
- }
-
- return res;
- }
- }
-
public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
- return startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, null);
}
@@ -3799,7 +2090,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
WaitResult res = new WaitResult();
- startActivityMayWait(caller, intent, resolvedType,
+ mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, res, null);
return res;
@@ -3810,7 +2101,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, config);
}
@@ -3834,8 +2125,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
- if (mResumedActivity != null
- && mResumedActivity.info.applicationInfo.uid ==
+ if (mMainStack.mResumedActivity != null
+ && mMainStack.mResumedActivity.info.applicationInfo.uid ==
Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
}
@@ -3853,11 +2144,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
synchronized (this) {
- int index = indexOfTokenLocked(callingActivity);
+ int index = mMainStack.indexOfTokenLocked(callingActivity);
if (index < 0) {
return false;
}
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r.app == null || r.app.thread == null) {
// The caller is not running... d'oh!
return false;
@@ -3871,7 +2162,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ActivityInfo aInfo = null;
try {
List<ResolveInfo> resolves =
- ActivityThread.getPackageManager().queryIntentActivities(
+ AppGlobals.getPackageManager().queryIntentActivities(
intent, r.resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
@@ -3915,7 +2206,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.finishing = true;
// Propagate reply information over to the new activity.
- final HistoryRecord resultTo = r.resultTo;
+ final ActivityRecord resultTo = r.resultTo;
final String resultWho = r.resultWho;
final int requestCode = r.requestCode;
r.resultTo = null;
@@ -3926,7 +2217,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
// XXX we are not dealing with propagating grantedUriPermissions...
// those are not yet exposed to user code, so there is no need.
- int res = startActivityLocked(r.app.thread, intent,
+ int res = mMainStack.startActivityLocked(r.app.thread, intent,
r.resolvedType, null, 0, aInfo, resultTo, resultWho,
requestCode, -1, r.launchedFromUid, false, false);
Binder.restoreCallingIdentity(origId);
@@ -3960,7 +2251,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
- ActivityThread.getPackageManager().resolveIntent(
+ AppGlobals.getPackageManager().resolveIntent(
intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
aInfo = rInfo != null ? rInfo.activityInfo : null;
@@ -3978,13 +2269,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
synchronized(this) {
- return startActivityLocked(null, intent, resolvedType,
+ return mMainStack.startActivityLocked(null, intent, resolvedType,
null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid,
onlyIfNeeded, componentSpecified);
}
}
- private final void addRecentTaskLocked(TaskRecord task) {
+ final void addRecentTaskLocked(TaskRecord task) {
// Remove any existing entries that are the same kind of task.
int N = mRecentTasks.size();
for (int i=0; i<N; i++) {
@@ -4010,11 +2301,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public void setRequestedOrientation(IBinder token,
int requestedOrientation) {
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
mWindowManager.setAppOrientation(r, requestedOrientation);
Configuration config = mWindowManager.updateOrientationFromAppTokens(
@@ -4023,7 +2314,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (config != null) {
r.frozenBeforeDestroy = true;
if (!updateConfigurationLocked(config, r)) {
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
}
Binder.restoreCallingIdentity(origId);
@@ -4032,250 +2323,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public int getRequestedOrientation(IBinder token) {
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return mWindowManager.getAppOrientation(r);
}
}
- private final void stopActivityLocked(HistoryRecord r) {
- if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
- || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
- if (!r.finishing) {
- requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
- "no-history");
- }
- } else if (r.app != null && r.app.thread != null) {
- if (mFocusedActivity == r) {
- setFocusedActivityLocked(topRunningActivityLocked(null));
- }
- r.resumeKeyDispatchingLocked();
- try {
- r.stopped = false;
- r.state = ActivityState.STOPPING;
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Stopping visible=" + r.visible + " for " + r);
- if (!r.visible) {
- mWindowManager.setAppVisibility(r, false);
- }
- r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
- } catch (Exception e) {
- // Maybe just ignore exceptions here... if the process
- // has crashed, our death notification will clean things
- // up.
- Slog.w(TAG, "Exception thrown during pause", e);
- // Just in case, assume it to be stopped.
- r.stopped = true;
- r.state = ActivityState.STOPPED;
- if (r.configDestroy) {
- destroyActivityLocked(r, true);
- }
- }
- }
- }
-
- /**
- * @return Returns true if the activity is being finished, false if for
- * some reason it is being left as-is.
- */
- private final boolean requestFinishActivityLocked(IBinder token, int resultCode,
- Intent resultData, String reason) {
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Finishing activity: token=" + token
- + ", result=" + resultCode + ", data=" + resultData);
-
- int index = indexOfTokenLocked(token);
- if (index < 0) {
- return false;
- }
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
-
- // Is this the last activity left?
- boolean lastActivity = true;
- for (int i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord p = (HistoryRecord)mHistory.get(i);
- if (!p.finishing && p != r) {
- lastActivity = false;
- break;
- }
- }
-
- // If this is the last activity, but it is the home activity, then
- // just don't finish it.
- if (lastActivity) {
- if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
- return false;
- }
- }
-
- finishActivityLocked(r, index, resultCode, resultData, reason);
- return true;
- }
-
- /**
- * @return Returns true if this activity has been removed from the history
- * list, or false if it is still in the list and will be removed later.
- */
- private final boolean finishActivityLocked(HistoryRecord r, int index,
- int resultCode, Intent resultData, String reason) {
- if (r.finishing) {
- Slog.w(TAG, "Duplicate finish request for " + r);
- return false;
- }
-
- r.finishing = true;
- EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName, reason);
- r.task.numActivities--;
- if (index < (mHistory.size()-1)) {
- HistoryRecord next = (HistoryRecord)mHistory.get(index+1);
- if (next.task == r.task) {
- if (r.frontOfTask) {
- // The next activity is now the front of the task.
- next.frontOfTask = true;
- }
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- // If the caller asked that this activity (and all above it)
- // be cleared when the task is reset, don't lose that information,
- // but propagate it up to the next activity.
- next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- }
- }
- }
-
- r.pauseKeyDispatchingLocked();
- if (mFocusedActivity == r) {
- setFocusedActivityLocked(topRunningActivityLocked(null));
- }
-
- // send the result
- HistoryRecord resultTo = r.resultTo;
- if (resultTo != null) {
- if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
- + " who=" + r.resultWho + " req=" + r.requestCode
- + " res=" + resultCode + " data=" + resultData);
- if (r.info.applicationInfo.uid > 0) {
- grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
- r.packageName, resultData, r);
- }
- resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
- resultData);
- r.resultTo = null;
- }
- else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
-
- // Make sure this HistoryRecord is not holding on to other resources,
- // because clients have remote IPC references to this object so we
- // can't assume that will go away and want to avoid circular IPC refs.
- r.results = null;
- r.pendingResults = null;
- r.newIntents = null;
- r.icicle = null;
-
- if (mPendingThumbnails.size() > 0) {
- // There are clients waiting to receive thumbnails so, in case
- // this is an activity that someone is waiting for, add it
- // to the pending list so we can correctly update the clients.
- mCancelledThumbnails.add(r);
- }
-
- if (mResumedActivity == r) {
- boolean endTask = index <= 0
- || ((HistoryRecord)mHistory.get(index-1)).task != r.task;
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare close transition: finishing " + r);
- mWindowManager.prepareAppTransition(endTask
- ? WindowManagerPolicy.TRANSIT_TASK_CLOSE
- : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
-
- // Tell window manager to prepare for this one to be removed.
- mWindowManager.setAppVisibility(r, false);
-
- if (mPausingActivity == null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
- startPausingLocked(false, false);
- }
-
- } else if (r.state != ActivityState.PAUSING) {
- // If the activity is PAUSING, we will complete the finish once
- // it is done pausing; else we can just directly finish it here.
- if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
- return finishCurrentActivityLocked(r, index,
- FINISH_AFTER_PAUSE) == null;
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
- }
-
- return false;
- }
-
- private static final int FINISH_IMMEDIATELY = 0;
- private static final int FINISH_AFTER_PAUSE = 1;
- private static final int FINISH_AFTER_VISIBLE = 2;
-
- private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r,
- int mode) {
- final int index = indexOfTokenLocked(r);
- if (index < 0) {
- return null;
- }
-
- return finishCurrentActivityLocked(r, index, mode);
- }
-
- private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r,
- int index, int mode) {
- // First things first: if this activity is currently visible,
- // and the resumed activity is not yet visible, then hold off on
- // finishing until the resumed one becomes visible.
- if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
- if (!mStoppingActivities.contains(r)) {
- mStoppingActivities.add(r);
- if (mStoppingActivities.size() > 3) {
- // If we already have a few activities waiting to stop,
- // then give up on things going idle and start clearing
- // them out.
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
- }
- r.state = ActivityState.STOPPING;
- updateOomAdjLocked();
- return r;
- }
-
- // make sure the record is cleaned out of other places.
- mStoppingActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
- if (mResumedActivity == r) {
- mResumedActivity = null;
- }
- final ActivityState prevState = r.state;
- r.state = ActivityState.FINISHING;
-
- if (mode == FINISH_IMMEDIATELY
- || prevState == ActivityState.STOPPED
- || prevState == ActivityState.INITIALIZING) {
- // If this activity is already stopped, we can just finish
- // it right now.
- return destroyActivityLocked(r, true) ? null : r;
- } else {
- // Need to go through the full pause cycle to get this
- // activity into the stopped state and then finish it.
- if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
- mFinishingActivities.add(r);
- resumeTopActivityLocked(null);
- }
- return r;
- }
-
/**
* This is the internal entry point for handling Activity.finish().
*
@@ -4294,7 +2350,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(this) {
if (mController != null) {
// Find the first activity that is not finishing.
- HistoryRecord next = topRunningActivityLocked(token, 0);
+ ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
@@ -4310,57 +2366,119 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
final long origId = Binder.clearCallingIdentity();
- boolean res = requestFinishActivityLocked(token, resultCode,
+ boolean res = mMainStack.requestFinishActivityLocked(token, resultCode,
resultData, "app-request");
Binder.restoreCallingIdentity(origId);
return res;
}
}
- void sendActivityResultLocked(int callingUid, HistoryRecord r,
- String resultWho, int requestCode, int resultCode, Intent data) {
-
- if (callingUid > 0) {
- grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- data, r);
+ public final void finishHeavyWeightApp() {
+ if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: finishHeavyWeightApp() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
-
- if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
- + " : who=" + resultWho + " req=" + requestCode
- + " res=" + resultCode + " data=" + data);
- if (mResumedActivity == r && r.app != null && r.app.thread != null) {
- try {
- ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
- list.add(new ResultInfo(resultWho, requestCode,
- resultCode, data));
- r.app.thread.scheduleSendResult(r, list);
+
+ synchronized(this) {
+ if (mHeavyWeightProcess == null) {
return;
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending result to " + r, e);
}
+
+ ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(
+ mHeavyWeightProcess.activities);
+ for (int i=0; i<activities.size(); i++) {
+ ActivityRecord r = activities.get(i);
+ if (!r.finishing) {
+ int index = mMainStack.indexOfTokenLocked(r);
+ if (index >= 0) {
+ mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
+ null, "finish-heavy");
+ }
+ }
+ }
+
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
-
- r.addResultLocked(null, resultWho, requestCode, resultCode, data);
}
-
+
+ public void crashApplication(int uid, int initialPid, String packageName,
+ String message) {
+ if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: crashApplication() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ synchronized(this) {
+ ProcessRecord proc = null;
+
+ // Figure out which process to kill. We don't trust that initialPid
+ // still has any relation to current pids, so must scan through the
+ // list.
+ synchronized (mPidsSelfLocked) {
+ for (int i=0; i<mPidsSelfLocked.size(); i++) {
+ ProcessRecord p = mPidsSelfLocked.valueAt(i);
+ if (p.info.uid != uid) {
+ continue;
+ }
+ if (p.pid == initialPid) {
+ proc = p;
+ break;
+ }
+ for (String str : p.pkgList) {
+ if (str.equals(packageName)) {
+ proc = p;
+ }
+ }
+ }
+ }
+
+ if (proc == null) {
+ Slog.w(TAG, "crashApplication: nothing for uid=" + uid
+ + " initialPid=" + initialPid
+ + " packageName=" + packageName);
+ return;
+ }
+
+ if (proc.thread != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ proc.thread.scheduleCrash(message);
+ } catch (RemoteException e) {
+ }
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
public final void finishSubActivity(IBinder token, String resultWho,
int requestCode) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- HistoryRecord self = (HistoryRecord)mHistory.get(index);
+ ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
int i;
- for (i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.resultTo == self && r.requestCode == requestCode) {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
- finishActivityLocked(r, i,
+ mMainStack.finishActivityLocked(r, i,
Activity.RESULT_CANCELED, null, "request-sub");
}
}
@@ -4373,8 +2491,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public boolean willActivityBeVisible(IBinder token) {
synchronized(this) {
int i;
- for (i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r == token) {
return true;
}
@@ -4389,11 +2507,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public void overridePendingTransition(IBinder token, String packageName,
int enterAnim, int exitAnim) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- HistoryRecord self = (HistoryRecord)mHistory.get(index);
+ ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
@@ -4408,188 +2526,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
/**
- * Perform clean-up of service connections in an activity record.
- */
- private final void cleanUpActivityServicesLocked(HistoryRecord r) {
- // Throw away any services that have been bound by this activity.
- if (r.connections != null) {
- Iterator<ConnectionRecord> it = r.connections.iterator();
- while (it.hasNext()) {
- ConnectionRecord c = it.next();
- removeConnectionLocked(c, null, r);
- }
- r.connections = null;
- }
- }
-
- /**
- * Perform the common clean-up of an activity record. This is called both
- * as part of destroyActivityLocked() (when destroying the client-side
- * representation) and cleaning things up as a result of its hosting
- * processing going away, in which case there is no remaining client-side
- * state to destroy so only the cleanup here is needed.
- */
- private final void cleanUpActivityLocked(HistoryRecord r, boolean cleanServices) {
- if (mResumedActivity == r) {
- mResumedActivity = null;
- }
- if (mFocusedActivity == r) {
- mFocusedActivity = null;
- }
-
- r.configDestroy = false;
- r.frozenBeforeDestroy = false;
-
- // Make sure this record is no longer in the pending finishes list.
- // This could happen, for example, if we are trimming activities
- // down to the max limit while they are still waiting to finish.
- mFinishingActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
-
- // Remove any pending results.
- if (r.finishing && r.pendingResults != null) {
- for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
- PendingIntentRecord rec = apr.get();
- if (rec != null) {
- cancelIntentSenderLocked(rec, false);
- }
- }
- r.pendingResults = null;
- }
-
- if (cleanServices) {
- cleanUpActivityServicesLocked(r);
- }
-
- if (mPendingThumbnails.size() > 0) {
- // There are clients waiting to receive thumbnails so, in case
- // this is an activity that someone is waiting for, add it
- // to the pending list so we can correctly update the clients.
- mCancelledThumbnails.add(r);
- }
-
- // Get rid of any pending idle timeouts.
- mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
- }
-
- private final void removeActivityFromHistoryLocked(HistoryRecord r) {
- if (r.state != ActivityState.DESTROYED) {
- mHistory.remove(r);
- r.inHistory = false;
- r.state = ActivityState.DESTROYED;
- mWindowManager.removeAppToken(r);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- cleanUpActivityServicesLocked(r);
- removeActivityUriPermissionsLocked(r);
- }
- }
-
- /**
- * Destroy the current CLIENT SIDE instance of an activity. This may be
- * called both when actually finishing an activity, or when performing
- * a configuration switch where we destroy the current client-side object
- * but then create a new client-side object for this same HistoryRecord.
- */
- private final boolean destroyActivityLocked(HistoryRecord r,
- boolean removeFromApp) {
- if (DEBUG_SWITCH) Slog.v(
- TAG, "Removing activity: token=" + r
- + ", app=" + (r.app != null ? r.app.processName : "(null)"));
- EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
-
- boolean removedFromHistory = false;
-
- cleanUpActivityLocked(r, false);
-
- final boolean hadApp = r.app != null;
-
- if (hadApp) {
- if (removeFromApp) {
- int idx = r.app.activities.indexOf(r);
- if (idx >= 0) {
- r.app.activities.remove(idx);
- }
- if (r.persistent) {
- decPersistentCountLocked(r.app);
- }
- if (r.app.activities.size() == 0) {
- // No longer have activities, so update location in
- // LRU list.
- updateLruProcessLocked(r.app, true, false);
- }
- }
-
- boolean skipDestroy = false;
-
- try {
- if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
- r.app.thread.scheduleDestroyActivity(r, r.finishing,
- r.configChangeFlags);
- } catch (Exception e) {
- // We can just ignore exceptions here... if the process
- // has crashed, our death notification will clean things
- // up.
- //Slog.w(TAG, "Exception thrown during finish", e);
- if (r.finishing) {
- removeActivityFromHistoryLocked(r);
- removedFromHistory = true;
- skipDestroy = true;
- }
- }
-
- r.app = null;
- r.nowVisible = false;
-
- if (r.finishing && !skipDestroy) {
- r.state = ActivityState.DESTROYING;
- Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
- msg.obj = r;
- mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
- } else {
- r.state = ActivityState.DESTROYED;
- }
- } else {
- // remove this record from the history.
- if (r.finishing) {
- removeActivityFromHistoryLocked(r);
- removedFromHistory = true;
- } else {
- r.state = ActivityState.DESTROYED;
- }
- }
-
- r.configChangeFlags = 0;
-
- if (!mLRUActivities.remove(r) && hadApp) {
- Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
- }
-
- return removedFromHistory;
- }
-
- private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) {
- int i = list.size();
- if (localLOGV) Slog.v(
- TAG, "Removing app " + app + " from list " + list
- + " with " + i + " entries");
- while (i > 0) {
- i--;
- HistoryRecord r = (HistoryRecord)list.get(i);
- if (localLOGV) Slog.v(
- TAG, "Record #" + i + " " + r + ": app=" + r.app);
- if (r.app == app) {
- if (localLOGV) Slog.v(TAG, "Removing this entry!");
- list.remove(i);
- }
- }
- }
-
- /**
* Main function for removing an existing process from the activity manager
* as a result of that process going away. Clears out all connections
* to the process.
@@ -4602,30 +2538,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// Just in case...
- if (mPausingActivity != null && mPausingActivity.app == app) {
- if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + mPausingActivity);
- mPausingActivity = null;
+ if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity);
+ mMainStack.mPausingActivity = null;
}
- if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
- mLastPausedActivity = null;
+ if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) {
+ mMainStack.mLastPausedActivity = null;
}
// Remove this application's activities from active lists.
- removeHistoryRecordsForAppLocked(mLRUActivities, app);
- removeHistoryRecordsForAppLocked(mStoppingActivities, app);
- removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
- removeHistoryRecordsForAppLocked(mFinishingActivities, app);
+ mMainStack.removeHistoryRecordsForAppLocked(app);
boolean atTop = true;
boolean hasVisibleActivities = false;
// Clean out the history list.
- int i = mHistory.size();
+ int i = mMainStack.mHistory.size();
if (localLOGV) Slog.v(
TAG, "Removing app " + app + " from history with " + i + " entries");
while (i > 0) {
i--;
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (localLOGV) Slog.v(
TAG, "Record #" + i + " " + r + ": app=" + r.app);
if (r.app == app) {
@@ -4633,14 +2566,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (localLOGV) Slog.v(
TAG, "Removing this entry! frozen=" + r.haveState
+ " finishing=" + r.finishing);
- mHistory.remove(i);
+ mMainStack.mHistory.remove(i);
r.inHistory = false;
mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
+ mWindowManager.validateAppTokens(mMainStack.mHistory);
}
- removeActivityUriPermissionsLocked(r);
+ r.removeUriPermissionsLocked();
} else {
// We have the current state for this activity, so
@@ -4657,7 +2590,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- cleanUpActivityLocked(r, true);
+ r.stack.cleanUpActivityLocked(r, true);
r.state = ActivityState.STOPPED;
}
atTop = false;
@@ -4674,14 +2607,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (!restarting) {
- if (!resumeTopActivityLocked(null)) {
+ if (!mMainStack.resumeTopActivityLocked(null)) {
// If there was nothing to resume, and we are not already
// restarting this process, but there is a visible activity that
// is hosted by the process... then make sure all visible
// activities are running, taking care of restarting this
// process.
if (hasVisibleActivities) {
- ensureActivitiesVisibleLocked(null, 0);
+ mMainStack.ensureActivitiesVisibleLocked(null, 0);
}
}
}
@@ -4700,7 +2633,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return -1;
}
- private final ProcessRecord getRecordForAppLocked(
+ final ProcessRecord getRecordForAppLocked(
IApplicationThread thread) {
if (thread == null) {
return null;
@@ -4710,11 +2643,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return appIndex >= 0 ? mLruProcesses.get(appIndex) : null;
}
- private final void appDiedLocked(ProcessRecord app, int pid,
+ final void appDiedLocked(ProcessRecord app, int pid,
IApplicationThread thread) {
mProcDeaths[0]++;
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ stats.noteProcessDiedLocked(app.info.uid, pid);
+ }
+
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
@@ -4752,8 +2690,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
(rec.lastLowMemory+GC_MIN_INTERVAL) <= now) {
// The low memory report is overriding any current
// state for a GC request. Make sure to do
- // visible/foreground processes first.
- if (rec.setAdj <= VISIBLE_APP_ADJ) {
+ // heavy/important/visible/foreground processes first.
+ if (rec.setAdj <= HEAVY_WEIGHT_APP_ADJ) {
rec.lastRequestedGc = 0;
} else {
rec.lastRequestedGc = rec.lastLowMemory;
@@ -4783,10 +2721,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* @param clearTraces causes the dump file to be erased prior to the new
* traces being written, if true; when false, the new traces will be
* appended to any existing file content.
- * @param pids of dalvik VM processes to dump stack traces for
+ * @param firstPids of dalvik VM processes to dump stack traces for first
+ * @param lastPids of dalvik VM processes to dump stack traces for last
* @return file containing stack traces, or null if no dump file is configured
*/
- public static File dumpStackTraces(boolean clearTraces, ArrayList<Integer> pids) {
+ public static File dumpStackTraces(boolean clearTraces, ArrayList<Integer> firstPids,
+ ProcessStats processStats, SparseArray<Boolean> lastPids) {
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (tracesPath == null || tracesPath.length() == 0) {
return null;
@@ -4814,25 +2754,94 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
try {
observer.startWatching();
- int num = pids.size();
- for (int i = 0; i < num; i++) {
- synchronized (observer) {
- Process.sendSignal(pids.get(i), Process.SIGNAL_QUIT);
- observer.wait(200); // Wait for write-close, give up after 200msec
+
+ // First collect all of the stacks of the most important pids.
+ try {
+ int num = firstPids.size();
+ for (int i = 0; i < num; i++) {
+ synchronized (observer) {
+ Process.sendSignal(firstPids.get(i), Process.SIGNAL_QUIT);
+ observer.wait(200); // Wait for write-close, give up after 200msec
+ }
}
+ } catch (InterruptedException e) {
+ Log.wtf(TAG, e);
}
- } catch (InterruptedException e) {
- Log.wtf(TAG, e);
+
+ // Next measure CPU usage.
+ if (processStats != null) {
+ processStats.init();
+ System.gc();
+ processStats.update();
+ try {
+ synchronized (processStats) {
+ processStats.wait(500); // measure over 1/2 second.
+ }
+ } catch (InterruptedException e) {
+ }
+ processStats.update();
+
+ // We'll take the stack crawls of just the top apps using CPU.
+ final int N = processStats.countWorkingStats();
+ int numProcs = 0;
+ for (int i=0; i<N && numProcs<5; i++) {
+ ProcessStats.Stats stats = processStats.getWorkingStats(i);
+ if (lastPids.indexOfKey(stats.pid) >= 0) {
+ numProcs++;
+ try {
+ synchronized (observer) {
+ Process.sendSignal(stats.pid, Process.SIGNAL_QUIT);
+ observer.wait(200); // Wait for write-close, give up after 200msec
+ }
+ } catch (InterruptedException e) {
+ Log.wtf(TAG, e);
+ }
+
+ }
+ }
+ }
+
+ return tracesFile;
+
} finally {
observer.stopWatching();
}
+ }
+
+ private final class AppNotResponding implements Runnable {
+ private final ProcessRecord mApp;
+ private final String mAnnotation;
+
+ public AppNotResponding(ProcessRecord app, String annotation) {
+ mApp = app;
+ mAnnotation = annotation;
+ }
- return tracesFile;
+ @Override
+ public void run() {
+ appNotResponding(mApp, null, null, mAnnotation);
+ }
}
- final void appNotResponding(ProcessRecord app, HistoryRecord activity,
- HistoryRecord parent, final String annotation) {
- ArrayList<Integer> pids = new ArrayList<Integer>(20);
+ final void appNotResponding(ProcessRecord app, ActivityRecord activity,
+ ActivityRecord parent, final String annotation) {
+ ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
+ SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
+
+ if (mController != null) {
+ try {
+ // 0 == continue, -1 = kill process immediately
+ int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);
+ if (res < 0 && app.pid != MY_PID) Process.killProcess(app.pid);
+ } catch (RemoteException e) {
+ mController = null;
+ }
+ }
+
+ long anrTime = SystemClock.uptimeMillis();
+ if (MONITOR_CPU_USAGE) {
+ updateCpuStatsNow();
+ }
synchronized (this) {
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
@@ -4856,25 +2865,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
annotation);
// Dump thread traces as quickly as we can, starting with "interesting" processes.
- pids.add(app.pid);
+ firstPids.add(app.pid);
int parentPid = app.pid;
if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;
- if (parentPid != app.pid) pids.add(parentPid);
+ if (parentPid != app.pid) firstPids.add(parentPid);
- if (MY_PID != app.pid && MY_PID != parentPid) pids.add(MY_PID);
+ if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r != null && r.thread != null) {
int pid = r.pid;
- if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) pids.add(pid);
+ if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
+ if (r.persistent) {
+ firstPids.add(pid);
+ } else {
+ lastPids.put(pid, Boolean.TRUE);
+ }
+ }
}
}
}
- File tracesFile = dumpStackTraces(true, pids);
-
// Log the ANR to the main log.
StringBuilder info = mStringBuilder;
info.setLength(0);
@@ -4890,15 +2903,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
info.append("Parent: ").append(parent.shortComponentName).append("\n");
}
+ final ProcessStats processStats = new ProcessStats(true);
+
+ File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids);
+
String cpuInfo = null;
if (MONITOR_CPU_USAGE) {
updateCpuStatsNow();
synchronized (mProcessStatsThread) {
- cpuInfo = mProcessStats.printCurrentState();
+ cpuInfo = mProcessStats.printCurrentState(anrTime);
}
+ info.append(processStats.printCurrentLoad());
info.append(cpuInfo);
}
+ info.append(processStats.printCurrentState(anrTime));
+
Slog.e(TAG, info.toString());
if (tracesFile == null) {
// There is no trace file, so dump (only) the alleged culprit's threads to the log
@@ -4950,82 +2970,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void decPersistentCountLocked(ProcessRecord app)
- {
- app.persistentActivities--;
- if (app.persistentActivities > 0) {
- // Still more of 'em...
- return;
- }
- if (app.persistent) {
- // Ah, but the application itself is persistent. Whatever!
- return;
- }
-
- // App is no longer persistent... make sure it and the ones
- // following it in the LRU list have the correc oom_adj.
- updateOomAdjLocked();
- }
-
- public void setPersistent(IBinder token, boolean isPersistent) {
- if (checkCallingPermission(android.Manifest.permission.PERSISTENT_ACTIVITY)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: setPersistent() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.PERSISTENT_ACTIVITY;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- synchronized(this) {
- int index = indexOfTokenLocked(token);
- if (index < 0) {
- return;
- }
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
- ProcessRecord app = r.app;
-
- if (localLOGV) Slog.v(
- TAG, "Setting persistence " + isPersistent + ": " + r);
-
- if (isPersistent) {
- if (r.persistent) {
- // Okay okay, I heard you already!
- if (localLOGV) Slog.v(TAG, "Already persistent!");
- return;
- }
- r.persistent = true;
- app.persistentActivities++;
- if (localLOGV) Slog.v(TAG, "Num persistent now: " + app.persistentActivities);
- if (app.persistentActivities > 1) {
- // We aren't the first...
- if (localLOGV) Slog.v(TAG, "Not the first!");
- return;
- }
- if (app.persistent) {
- // This would be redundant.
- if (localLOGV) Slog.v(TAG, "App is persistent!");
- return;
- }
-
- // App is now persistent... make sure it and the ones
- // following it now have the correct oom_adj.
- final long origId = Binder.clearCallingIdentity();
- updateOomAdjLocked();
- Binder.restoreCallingIdentity(origId);
-
- } else {
- if (!r.persistent) {
- // Okay okay, I heard you already!
- return;
+ final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
+ if (!mLaunchWarningShown) {
+ mLaunchWarningShown = true;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ final Dialog d = new LaunchWarningWindow(mContext, cur, next);
+ d.show();
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ d.dismiss();
+ mLaunchWarningShown = false;
+ }
+ }
+ }, 4000);
+ }
}
- r.persistent = false;
- final long origId = Binder.clearCallingIdentity();
- decPersistentCountLocked(app);
- Binder.restoreCallingIdentity(origId);
-
- }
+ });
}
}
@@ -5035,7 +3000,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int pid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
int pkgUid = -1;
synchronized(this) {
try {
@@ -5064,11 +3029,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
Uri.fromParts("package", packageName, null));
intent.putExtra(Intent.EXTRA_UID, pkgUid);
- synchronized (this) {
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null,
- false, false, MY_PID, Process.SYSTEM_UID);
- }
+ broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
+ null, null, 0, null, null, null, false, false);
} catch (RemoteException e) {
}
} finally {
@@ -5092,7 +3054,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
long callingId = Binder.clearCallingIdentity();
try {
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
int pkgUid = -1;
synchronized(this) {
try {
@@ -5124,7 +3086,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
long callingId = Binder.clearCallingIdentity();
try {
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
int pkgUid = -1;
synchronized(this) {
try {
@@ -5172,6 +3134,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public void closeSystemDialogs(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
if (reason != null) {
intent.putExtra("reason", reason);
}
@@ -5194,10 +3157,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mWindowManager.closeSystemDialogs(reason);
- for (i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
- finishActivityLocked(r, i,
+ r.stack.finishActivityLocked(r, i,
Activity.RESULT_CANCELED, null, "close-sys");
}
}
@@ -5249,6 +3212,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
forceStopPackageLocked(packageName, uid, false, false, true);
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
+ if (!mProcessesReady) {
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
intent.putExtra(Intent.EXTRA_UID, uid);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
@@ -5298,7 +3264,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (uid < 0) {
try {
- uid = ActivityThread.getPackageManager().getPackageUid(name);
+ uid = AppGlobals.getPackageManager().getPackageUid(name);
} catch (RemoteException e) {
}
}
@@ -5318,8 +3284,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean didSomething = killPackageProcessesLocked(name, uid, -100,
callerWillRestart, doit);
- for (i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.packageName.equals(name)) {
if (!doit) {
return true;
@@ -5330,7 +3296,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.app.removed = true;
}
r.app = null;
- finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall");
+ r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall");
}
}
@@ -5362,7 +3328,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ac.removePackage(name);
}
}
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
return didSomething;
@@ -5376,6 +3342,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ "/" + uid + ")");
mProcessNames.remove(name, uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
boolean needRestart = false;
if (app.pid > 0 && app.pid != MY_PID) {
int pid = app.pid;
@@ -5417,6 +3387,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.info.uid,
app.processName);
mProcessNames.remove(app.processName, app.info.uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
// Take care of any launching providers waiting for this process.
checkAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
@@ -5443,6 +3417,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid) {
Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
+ mPendingBroadcast.state = BroadcastRecord.IDLE;
+ mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
scheduleBroadcastsLocked();
}
@@ -5518,7 +3494,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- boolean normalMode = mSystemReady || isAllowedWhileBooting(app.info);
+ boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
if (!normalMode) {
@@ -5577,18 +3553,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Remove this record from the list of starting applications.
mPersistentStartingProcesses.remove(app);
+ if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
+ "Attach application locked removing on hold: " + app);
mProcessesOnHold.remove(app);
boolean badApp = false;
boolean didSomething = false;
// See if the top visible activity is waiting to run in this process...
- HistoryRecord hr = topRunningActivityLocked(null);
+ ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
if (hr != null && normalMode) {
if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
- if (realStartActivityLocked(hr, app, true, true)) {
+ if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (Exception e) {
@@ -5597,7 +3575,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
badApp = true;
}
} else {
- ensureActivitiesVisibleLocked(hr, null, processName, 0);
+ mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
}
}
@@ -5635,7 +3613,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Slog.w(TAG, "Exception in new application when starting receiver "
+ br.curComponent.flattenToShortString(), e);
badApp = true;
- logBroadcastReceiverDiscard(br);
+ logBroadcastReceiverDiscardLocked(br);
finishReceiverLocked(br.receiver, br.resultCode, br.resultData,
br.resultExtras, br.resultAbort, true);
scheduleBroadcastsLocked();
@@ -5681,195 +3659,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public final void activityIdle(IBinder token, Configuration config) {
final long origId = Binder.clearCallingIdentity();
- activityIdleInternal(token, false, config);
+ mMainStack.activityIdleInternal(token, false, config);
Binder.restoreCallingIdentity(origId);
}
- final ArrayList<HistoryRecord> processStoppingActivitiesLocked(
- boolean remove) {
- int N = mStoppingActivities.size();
- if (N <= 0) return null;
-
- ArrayList<HistoryRecord> stops = null;
-
- final boolean nowVisible = mResumedActivity != null
- && mResumedActivity.nowVisible
- && !mResumedActivity.waitingVisible;
- for (int i=0; i<N; i++) {
- HistoryRecord s = mStoppingActivities.get(i);
- if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
- + nowVisible + " waitingVisible=" + s.waitingVisible
- + " finishing=" + s.finishing);
- if (s.waitingVisible && nowVisible) {
- mWaitingVisibleActivities.remove(s);
- s.waitingVisible = false;
- if (s.finishing) {
- // If this activity is finishing, it is sitting on top of
- // everyone else but we now know it is no longer needed...
- // so get rid of it. Otherwise, we need to go through the
- // normal flow and hide it once we determine that it is
- // hidden by the activities in front of it.
- if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
- mWindowManager.setAppVisibility(s, false);
- }
- }
- if (!s.waitingVisible && remove) {
- if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
- if (stops == null) {
- stops = new ArrayList<HistoryRecord>();
- }
- stops.add(s);
- mStoppingActivities.remove(i);
- N--;
- i--;
- }
- }
-
- return stops;
- }
-
void enableScreenAfterBoot() {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
mWindowManager.enableScreenAfterBoot();
}
- final void activityIdleInternal(IBinder token, boolean fromTimeout,
- Configuration config) {
- if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
-
- ArrayList<HistoryRecord> stops = null;
- ArrayList<HistoryRecord> finishes = null;
- ArrayList<HistoryRecord> thumbnails = null;
- int NS = 0;
- int NF = 0;
- int NT = 0;
- IApplicationThread sendThumbnail = null;
- boolean booting = false;
- boolean enableScreen = false;
-
- synchronized (this) {
- if (token != null) {
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
- }
-
- // Get the activity record.
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
-
- if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
- }
-
- // This is a hack to semi-deal with a race condition
- // in the client where it can be constructed with a
- // newer configuration from when we asked it to launch.
- // We'll update with whatever configuration it now says
- // it used to launch.
- if (config != null) {
- r.configuration = config;
- }
-
- // No longer need to keep the device awake.
- if (mResumedActivity == r && mLaunchingActivity.isHeld()) {
- mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- mLaunchingActivity.release();
- }
-
- // We are now idle. If someone is waiting for a thumbnail from
- // us, we can now deliver.
- r.idle = true;
- scheduleAppGcsLocked();
- if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
- sendThumbnail = r.app.thread;
- r.thumbnailNeeded = false;
- }
-
- // If this activity is fullscreen, set up to hide those under it.
-
- if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
- ensureActivitiesVisibleLocked(null, 0);
-
- //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (!mBooted && !fromTimeout) {
- mBooted = true;
- enableScreen = true;
- }
-
- } else if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
- }
-
- // Atomically retrieve all of the other things to do.
- stops = processStoppingActivitiesLocked(true);
- NS = stops != null ? stops.size() : 0;
- if ((NF=mFinishingActivities.size()) > 0) {
- finishes = new ArrayList<HistoryRecord>(mFinishingActivities);
- mFinishingActivities.clear();
- }
- if ((NT=mCancelledThumbnails.size()) > 0) {
- thumbnails = new ArrayList<HistoryRecord>(mCancelledThumbnails);
- mCancelledThumbnails.clear();
- }
-
- booting = mBooting;
- mBooting = false;
- }
-
- int i;
-
- // Send thumbnail if requested.
- if (sendThumbnail != null) {
- try {
- sendThumbnail.requestThumbnail(token);
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
- sendPendingThumbnail(null, token, null, null, true);
- }
- }
-
- // Stop any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (i=0; i<NS; i++) {
- HistoryRecord r = (HistoryRecord)stops.get(i);
- synchronized (this) {
- if (r.finishing) {
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
- } else {
- stopActivityLocked(r);
- }
- }
- }
-
- // Finish any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (i=0; i<NF; i++) {
- HistoryRecord r = (HistoryRecord)finishes.get(i);
- synchronized (this) {
- destroyActivityLocked(r, true);
- }
- }
-
- // Report back to any thumbnail receivers.
- for (i=0; i<NT; i++) {
- HistoryRecord r = (HistoryRecord)thumbnails.get(i);
- sendPendingThumbnail(r, null, null, null, true);
- }
-
- if (booting) {
- finishBooting();
- }
-
- trimApplications();
- //dump();
- //mWindowManager.dump();
-
- if (enableScreen) {
- enableScreenAfterBoot();
- }
- }
-
final void finishBooting() {
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
@@ -5899,12 +3698,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ArrayList<ProcessRecord> procs =
new ArrayList<ProcessRecord>(mProcessesOnHold);
for (int ip=0; ip<NP; ip++) {
- this.startProcessLocked(procs.get(ip), "on-hold", null);
+ if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: "
+ + procs.get(ip));
+ startProcessLocked(procs.get(ip), "on-hold", null);
}
}
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+ // Start looking for apps that are abusing wake locks.
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
// Tell anyone interested that we are done booting!
+ SystemProperties.set("sys.boot_completed", "1");
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_BOOT_COMPLETED, null),
null, null, 0, null, null,
@@ -5940,60 +3745,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
final long origId = Binder.clearCallingIdentity();
- activityPaused(token, icicle, false);
+ mMainStack.activityPaused(token, icicle, false);
Binder.restoreCallingIdentity(origId);
}
- final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
- if (DEBUG_PAUSE) Slog.v(
- TAG, "Activity paused: token=" + token + ", icicle=" + icicle
- + ", timeout=" + timeout);
-
- HistoryRecord r = null;
-
- synchronized (this) {
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- r = (HistoryRecord)mHistory.get(index);
- if (!timeout) {
- r.icicle = icicle;
- r.haveState = true;
- }
- mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- if (mPausingActivity == r) {
- r.state = ActivityState.PAUSED;
- completePauseLocked();
- } else {
- EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
- System.identityHashCode(r), r.shortComponentName,
- mPausingActivity != null
- ? mPausingActivity.shortComponentName : "(none)");
- }
- }
- }
- }
-
public final void activityStopped(IBinder token, Bitmap thumbnail,
CharSequence description) {
if (localLOGV) Slog.v(
TAG, "Activity stopped: token=" + token);
- HistoryRecord r = null;
+ ActivityRecord r = null;
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- r = (HistoryRecord)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
r.thumbnail = thumbnail;
r.description = description;
r.stopped = true;
r.state = ActivityState.STOPPED;
if (!r.finishing) {
if (r.configDestroy) {
- destroyActivityLocked(r, true);
- resumeTopActivityLocked(null);
+ r.stack.destroyActivityLocked(r, true);
+ r.stack.resumeTopActivityLocked(null);
}
}
}
@@ -6010,39 +3786,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public final void activityDestroyed(IBinder token) {
if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token);
- synchronized (this) {
- mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
-
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
- if (r.state == ActivityState.DESTROYING) {
- final long origId = Binder.clearCallingIdentity();
- removeActivityFromHistoryLocked(r);
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
+ mMainStack.activityDestroyed(token);
}
public String getCallingPackage(IBinder token) {
synchronized (this) {
- HistoryRecord r = getCallingRecordLocked(token);
+ ActivityRecord r = getCallingRecordLocked(token);
return r != null && r.app != null ? r.info.packageName : null;
}
}
public ComponentName getCallingActivity(IBinder token) {
synchronized (this) {
- HistoryRecord r = getCallingRecordLocked(token);
+ ActivityRecord r = getCallingRecordLocked(token);
return r != null ? r.intent.getComponent() : null;
}
}
- private HistoryRecord getCallingRecordLocked(IBinder token) {
- int index = indexOfTokenLocked(token);
+ private ActivityRecord getCallingRecordLocked(IBinder token) {
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r != null) {
return r.resultTo;
}
@@ -6052,9 +3816,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public ComponentName getActivityClassForToken(IBinder token) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return r.intent.getComponent();
}
return null;
@@ -6063,9 +3827,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public String getPackageForToken(IBinder token) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return r.packageName;
}
return null;
@@ -6092,7 +3856,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
try {
if (callingUid != 0 && callingUid != Process.SYSTEM_UID &&
Process.supportsProcesses()) {
- int uid = ActivityThread.getPackageManager()
+ int uid = AppGlobals.getPackageManager()
.getPackageUid(packageName);
if (uid != Binder.getCallingUid()) {
String msg = "Permission Denial: getIntentSender() from pid="
@@ -6104,57 +3868,66 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
throw new SecurityException(msg);
}
}
+
+ return getIntentSenderLocked(type, packageName, callingUid,
+ token, resultWho, requestCode, intent, resolvedType, flags);
+
} catch (RemoteException e) {
throw new SecurityException(e);
}
- HistoryRecord activity = null;
- if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- int index = indexOfTokenLocked(token);
- if (index < 0) {
- return null;
- }
- activity = (HistoryRecord)mHistory.get(index);
- if (activity.finishing) {
- return null;
- }
+ }
+ }
+
+ IIntentSender getIntentSenderLocked(int type,
+ String packageName, int callingUid, IBinder token, String resultWho,
+ int requestCode, Intent intent, String resolvedType, int flags) {
+ ActivityRecord activity = null;
+ if (type == INTENT_SENDER_ACTIVITY_RESULT) {
+ int index = mMainStack.indexOfTokenLocked(token);
+ if (index < 0) {
+ return null;
+ }
+ activity = (ActivityRecord)mMainStack.mHistory.get(index);
+ if (activity.finishing) {
+ return null;
}
+ }
- final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
- final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
- final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
- flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
- |PendingIntent.FLAG_UPDATE_CURRENT);
+ final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
+ final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
+ final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
+ flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
+ |PendingIntent.FLAG_UPDATE_CURRENT);
- PendingIntentRecord.Key key = new PendingIntentRecord.Key(
- type, packageName, activity, resultWho,
- requestCode, intent, resolvedType, flags);
- WeakReference<PendingIntentRecord> ref;
- ref = mIntentSenderRecords.get(key);
- PendingIntentRecord rec = ref != null ? ref.get() : null;
- if (rec != null) {
- if (!cancelCurrent) {
- if (updateCurrent) {
- rec.key.requestIntent.replaceExtras(intent);
- }
- return rec;
+ PendingIntentRecord.Key key = new PendingIntentRecord.Key(
+ type, packageName, activity, resultWho,
+ requestCode, intent, resolvedType, flags);
+ WeakReference<PendingIntentRecord> ref;
+ ref = mIntentSenderRecords.get(key);
+ PendingIntentRecord rec = ref != null ? ref.get() : null;
+ if (rec != null) {
+ if (!cancelCurrent) {
+ if (updateCurrent) {
+ rec.key.requestIntent.replaceExtras(intent);
}
- rec.canceled = true;
- mIntentSenderRecords.remove(key);
- }
- if (noCreate) {
return rec;
}
- rec = new PendingIntentRecord(this, key, callingUid);
- mIntentSenderRecords.put(key, rec.ref);
- if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- if (activity.pendingResults == null) {
- activity.pendingResults
- = new HashSet<WeakReference<PendingIntentRecord>>();
- }
- activity.pendingResults.add(rec.ref);
- }
+ rec.canceled = true;
+ mIntentSenderRecords.remove(key);
+ }
+ if (noCreate) {
return rec;
}
+ rec = new PendingIntentRecord(this, key, callingUid);
+ mIntentSenderRecords.put(key, rec.ref);
+ if (type == INTENT_SENDER_ACTIVITY_RESULT) {
+ if (activity.pendingResults == null) {
+ activity.pendingResults
+ = new HashSet<WeakReference<PendingIntentRecord>>();
+ }
+ activity.pendingResults.add(rec.ref);
+ }
+ return rec;
}
public void cancelIntentSender(IIntentSender sender) {
@@ -6164,7 +3937,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(this) {
PendingIntentRecord rec = (PendingIntentRecord)sender;
try {
- int uid = ActivityThread.getPackageManager()
+ int uid = AppGlobals.getPackageManager()
.getPackageUid(rec.key.packageName);
if (uid != Binder.getCallingUid()) {
String msg = "Permission Denial: cancelIntentSender() from pid="
@@ -6323,7 +4096,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return PackageManager.PERMISSION_GRANTED;
}
try {
- return ActivityThread.getPackageManager()
+ return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
// Should never happen, but if it does... deny!
@@ -6376,26 +4149,75 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
private final boolean checkHoldingPermissionsLocked(IPackageManager pm,
- ProviderInfo pi, int uid, int modeFlags) {
+ ProviderInfo pi, Uri uri, int uid, int modeFlags) {
+ boolean readPerm = (modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
+ boolean writePerm = (modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+ "checkHoldingPermissionsLocked: uri=" + uri + " uid=" + uid);
try {
- if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
- if ((pi.readPermission != null) &&
+ // Is the component private from the target uid?
+ final boolean prv = !pi.exported && pi.applicationInfo.uid != uid;
+
+ // Acceptable if the there is no read permission needed from the
+ // target or the target is holding the read permission.
+ if (!readPerm) {
+ if ((!prv && pi.readPermission == null) ||
(pm.checkUidPermission(pi.readPermission, uid)
- != PackageManager.PERMISSION_GRANTED)) {
- return false;
+ == PackageManager.PERMISSION_GRANTED)) {
+ readPerm = true;
}
}
- if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
- if ((pi.writePermission != null) &&
+
+ // Acceptable if the there is no write permission needed from the
+ // target or the target is holding the read permission.
+ if (!writePerm) {
+ if (!prv && (pi.writePermission == null) ||
(pm.checkUidPermission(pi.writePermission, uid)
- != PackageManager.PERMISSION_GRANTED)) {
- return false;
+ == PackageManager.PERMISSION_GRANTED)) {
+ writePerm = true;
+ }
+ }
+
+ // Acceptable if there is a path permission matching the URI that
+ // the target holds the permission on.
+ PathPermission[] pps = pi.pathPermissions;
+ if (pps != null && (!readPerm || !writePerm)) {
+ final String path = uri.getPath();
+ int i = pps.length;
+ while (i > 0 && (!readPerm || !writePerm)) {
+ i--;
+ PathPermission pp = pps[i];
+ if (!readPerm) {
+ final String pprperm = pp.getReadPermission();
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking read perm for "
+ + pprperm + " for " + pp.getPath()
+ + ": match=" + pp.match(path)
+ + " check=" + pm.checkUidPermission(pprperm, uid));
+ if (pprperm != null && pp.match(path) &&
+ (pm.checkUidPermission(pprperm, uid)
+ == PackageManager.PERMISSION_GRANTED)) {
+ readPerm = true;
+ }
+ }
+ if (!writePerm) {
+ final String ppwperm = pp.getWritePermission();
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking write perm "
+ + ppwperm + " for " + pp.getPath()
+ + ": match=" + pp.match(path)
+ + " check=" + pm.checkUidPermission(ppwperm, uid));
+ if (ppwperm != null && pp.match(path) &&
+ (pm.checkUidPermission(ppwperm, uid)
+ == PackageManager.PERMISSION_GRANTED)) {
+ writePerm = true;
+ }
+ }
}
}
- return true;
} catch (RemoteException e) {
return false;
}
+
+ return readPerm && writePerm;
}
private final boolean checkUriPermissionLocked(Uri uri, int uid,
@@ -6431,30 +4253,36 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private void grantUriPermissionLocked(int callingUid,
- String targetPkg, Uri uri, int modeFlags, HistoryRecord activity) {
+ /**
+ * Check if the targetPkg can be granted permission to access uri by
+ * the callingUid using the given modeFlags. Throws a security exception
+ * if callingUid is not allowed to do this. Returns the uid of the target
+ * if the URI permission grant should be performed; returns -1 if it is not
+ * needed (for example targetPkg already has permission to access the URI).
+ */
+ int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
+ Uri uri, int modeFlags) {
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
- return;
+ return -1;
}
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Requested grant " + targetPkg + " permission to " + uri);
+ "Checking grant " + targetPkg + " permission to " + uri);
- final IPackageManager pm = ActivityThread.getPackageManager();
+ final IPackageManager pm = AppGlobals.getPackageManager();
// If this is not a content: uri, we can't do anything with it.
if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Can't grant URI permission for non-content URI: " + uri);
- return;
+ return -1;
}
String name = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6466,7 +4294,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (pi == null) {
Slog.w(TAG, "No content provider found for: " + name);
- return;
+ return -1;
}
int targetUid;
@@ -6475,18 +4303,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (targetUid < 0) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Can't grant URI permission no uid for: " + targetPkg);
- return;
+ return -1;
}
} catch (RemoteException ex) {
- return;
+ return -1;
}
// First... does the target actually need this permission?
- if (checkHoldingPermissionsLocked(pm, pi, targetUid, modeFlags)) {
+ if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
// No need to grant the target this permission.
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Target " + targetPkg + " already has full permission to " + uri);
- return;
+ return -1;
}
// Second... is the provider allowing granting of URI permissions?
@@ -6516,19 +4344,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Third... does the caller itself have permission to access
// this uri?
- if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) {
+ if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
throw new SecurityException("Uid " + callingUid
+ " does not have permission to uri " + uri);
}
}
- // Okay! So here we are: the caller has the assumed permission
+ return targetUid;
+ }
+
+ void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
+ Uri uri, int modeFlags, UriPermissionOwner owner) {
+ modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (modeFlags == 0) {
+ return;
+ }
+
+ // So here we are: the caller has the assumed permission
// to the uri, and the target doesn't. Let's now give this to
// the target.
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Granting " + targetPkg + " permission to " + uri);
+ "Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
HashMap<Uri, UriPermission> targetUris
= mGrantedUriPermissions.get(targetUid);
@@ -6541,37 +4380,68 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (perm == null) {
perm = new UriPermission(targetUid, uri);
targetUris.put(uri, perm);
-
}
+
perm.modeFlags |= modeFlags;
- if (activity == null) {
+ if (owner == null) {
perm.globalModeFlags |= modeFlags;
} else if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
- perm.readActivities.add(activity);
- if (activity.readUriPermissions == null) {
- activity.readUriPermissions = new HashSet<UriPermission>();
- }
- activity.readUriPermissions.add(perm);
+ perm.readOwners.add(owner);
+ owner.addReadPermission(perm);
} else if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
- perm.writeActivities.add(activity);
- if (activity.writeUriPermissions == null) {
- activity.writeUriPermissions = new HashSet<UriPermission>();
- }
- activity.writeUriPermissions.add(perm);
+ perm.writeOwners.add(owner);
+ owner.addWritePermission(perm);
}
}
- private void grantUriPermissionFromIntentLocked(int callingUid,
- String targetPkg, Intent intent, HistoryRecord activity) {
- if (intent == null) {
+ void grantUriPermissionLocked(int callingUid,
+ String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+ int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
+ if (targetUid < 0) {
return;
}
+
+ grantUriPermissionUncheckedLocked(targetUid, targetPkg, uri, modeFlags, owner);
+ }
+
+ /**
+ * Like checkGrantUriPermissionLocked, but takes an Intent.
+ */
+ int checkGrantUriPermissionFromIntentLocked(int callingUid,
+ String targetPkg, Intent intent) {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+ "Checking URI perm to " + (intent != null ? intent.getData() : null)
+ + " from " + intent + "; flags=0x"
+ + Integer.toHexString(intent != null ? intent.getFlags() : 0));
+
+ if (intent == null) {
+ return -1;
+ }
Uri data = intent.getData();
if (data == null) {
+ return -1;
+ }
+ return checkGrantUriPermissionLocked(callingUid, targetPkg, data,
+ intent.getFlags());
+ }
+
+ /**
+ * Like grantUriPermissionUncheckedLocked, but takes an Intent.
+ */
+ void grantUriPermissionUncheckedFromIntentLocked(int targetUid,
+ String targetPkg, Intent intent, UriPermissionOwner owner) {
+ grantUriPermissionUncheckedLocked(targetUid, targetPkg, intent.getData(),
+ intent.getFlags(), owner);
+ }
+
+ void grantUriPermissionFromIntentLocked(int callingUid,
+ String targetPkg, Intent intent, UriPermissionOwner owner) {
+ int targetUid = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg, intent);
+ if (targetUid < 0) {
return;
}
- grantUriPermissionLocked(callingUid, targetPkg, data,
- intent.getFlags(), activity);
+
+ grantUriPermissionUncheckedFromIntentLocked(targetUid, targetPkg, intent, owner);
}
public void grantUriPermission(IApplicationThread caller, String targetPkg,
@@ -6584,12 +4454,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ " when granting permission to uri " + uri);
}
if (targetPkg == null) {
- Slog.w(TAG, "grantUriPermission: null target");
- return;
+ throw new IllegalArgumentException("null target");
}
if (uri == null) {
- Slog.w(TAG, "grantUriPermission: null uri");
- return;
+ throw new IllegalArgumentException("null uri");
}
grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags,
@@ -6597,7 +4465,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private void removeUriPermissionIfNeededLocked(UriPermission perm) {
+ void removeUriPermissionIfNeededLocked(UriPermission perm) {
if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION
|Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) {
HashMap<Uri, UriPermission> perms
@@ -6613,29 +4481,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private void removeActivityUriPermissionsLocked(HistoryRecord activity) {
- if (activity.readUriPermissions != null) {
- for (UriPermission perm : activity.readUriPermissions) {
- perm.readActivities.remove(activity);
- if (perm.readActivities.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- removeUriPermissionIfNeededLocked(perm);
- }
- }
- }
- if (activity.writeUriPermissions != null) {
- for (UriPermission perm : activity.writeUriPermissions) {
- perm.writeActivities.remove(activity);
- if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- removeUriPermissionIfNeededLocked(perm);
- }
- }
- }
- }
-
private void revokeUriPermissionLocked(int callingUid, Uri uri,
int modeFlags) {
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -6647,12 +4492,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Revoking all granted permissions to " + uri);
- final IPackageManager pm = ActivityThread.getPackageManager();
+ final IPackageManager pm = AppGlobals.getPackageManager();
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProvidersByName.get(authority);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6668,7 +4512,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// Does the caller have this permission on the URI?
- if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) {
+ if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
// Right now, if you are not the original owner of the permission,
// you are not allowed to revoke it.
//if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
@@ -6742,12 +4586,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return;
}
- final IPackageManager pm = ActivityThread.getPackageManager();
+ final IPackageManager pm = AppGlobals.getPackageManager();
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProvidersByName.get(authority);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6766,6 +4609,56 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ @Override
+ public IBinder newUriPermissionOwner(String name) {
+ synchronized(this) {
+ UriPermissionOwner owner = new UriPermissionOwner(this, name);
+ return owner.getExternalTokenLocked();
+ }
+ }
+
+ @Override
+ public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
+ Uri uri, int modeFlags) {
+ synchronized(this) {
+ UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+ if (owner == null) {
+ throw new IllegalArgumentException("Unknown owner: " + token);
+ }
+ if (fromUid != Binder.getCallingUid()) {
+ if (Binder.getCallingUid() != Process.myUid()) {
+ // Only system code can grant URI permissions on behalf
+ // of other users.
+ throw new SecurityException("nice try");
+ }
+ }
+ if (targetPkg == null) {
+ throw new IllegalArgumentException("null target");
+ }
+ if (uri == null) {
+ throw new IllegalArgumentException("null uri");
+ }
+
+ grantUriPermissionLocked(fromUid, targetPkg, uri, modeFlags, owner);
+ }
+ }
+
+ @Override
+ public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode) {
+ synchronized(this) {
+ UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+ if (owner == null) {
+ throw new IllegalArgumentException("Unknown owner: " + token);
+ }
+
+ if (uri == null) {
+ owner.removeUriPermissionsLocked(mode);
+ } else {
+ owner.removeUriPermissionLocked(uri, mode);
+ }
+ }
+ }
+
public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
synchronized (this) {
ProcessRecord app =
@@ -6797,7 +4690,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
PendingThumbnailsRecord pending = null;
IApplicationThread topThumbnail = null;
- HistoryRecord topRecord = null;
+ ActivityRecord topRecord = null;
synchronized(this) {
if (localLOGV) Slog.v(
@@ -6822,18 +4715,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
throw new SecurityException(msg);
}
- int pos = mHistory.size()-1;
- HistoryRecord next =
- pos >= 0 ? (HistoryRecord)mHistory.get(pos) : null;
- HistoryRecord top = null;
+ int pos = mMainStack.mHistory.size()-1;
+ ActivityRecord next =
+ pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
+ ActivityRecord top = null;
CharSequence topDescription = null;
TaskRecord curTask = null;
int numActivities = 0;
int numRunning = 0;
while (pos >= 0 && maxNum > 0) {
- final HistoryRecord r = next;
+ final ActivityRecord r = next;
pos--;
- next = pos >= 0 ? (HistoryRecord)mHistory.get(pos) : null;
+ next = pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
// Initialize state for next task if needed.
if (top == null ||
@@ -6935,7 +4828,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
enforceCallingPermission(android.Manifest.permission.GET_TASKS,
"getRecentTasks()");
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
final int N = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res
@@ -6982,12 +4875,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
int j;
- TaskRecord startTask = ((HistoryRecord)mHistory.get(startIndex)).task;
+ TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task;
TaskRecord jt = startTask;
// First look backwards
for (j=startIndex-1; j>=0; j--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(j);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
if (r.task != jt) {
jt = r.task;
if (affinity.equals(jt.affinity)) {
@@ -6997,10 +4890,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// Now look forwards
- final int N = mHistory.size();
+ final int N = mMainStack.mHistory.size();
jt = startTask;
for (j=startIndex+1; j<N; j++) {
- HistoryRecord r = (HistoryRecord)mHistory.get(j);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
if (r.task != jt) {
if (affinity.equals(jt.affinity)) {
return j;
@@ -7010,7 +4903,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// Might it be at the top?
- if (affinity.equals(((HistoryRecord)mHistory.get(N-1)).task.affinity)) {
+ if (affinity.equals(((ActivityRecord)mMainStack.mHistory.get(N-1)).task.affinity)) {
return N-1;
}
@@ -7018,293 +4911,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
/**
- * Perform a reset of the given task, if needed as part of launching it.
- * Returns the new HistoryRecord at the top of the task.
- */
- private final HistoryRecord resetTaskIfNeededLocked(HistoryRecord taskTop,
- HistoryRecord newActivity) {
- boolean forceReset = (newActivity.info.flags
- &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
- if ((newActivity.info.flags
- &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
- forceReset = true;
- }
- }
-
- final TaskRecord task = taskTop.task;
-
- // We are going to move through the history list so that we can look
- // at each activity 'target' with 'below' either the interesting
- // activity immediately below it in the stack or null.
- HistoryRecord target = null;
- int targetI = 0;
- int taskTopI = -1;
- int replyChainEnd = -1;
- int lastReparentPos = -1;
- for (int i=mHistory.size()-1; i>=-1; i--) {
- HistoryRecord below = i >= 0 ? (HistoryRecord)mHistory.get(i) : null;
-
- if (below != null && below.finishing) {
- continue;
- }
- if (target == null) {
- target = below;
- targetI = i;
- // If we were in the middle of a reply chain before this
- // task, it doesn't appear like the root of the chain wants
- // anything interesting, so drop it.
- replyChainEnd = -1;
- continue;
- }
-
- final int flags = target.info.flags;
-
- final boolean finishOnTaskLaunch =
- (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
- final boolean allowTaskReparenting =
- (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
-
- if (target.task == task) {
- // We are inside of the task being reset... we'll either
- // finish this activity, push it out for another task,
- // or leave it as-is. We only do this
- // for activities that are not the root of the task (since
- // if we finish the root, we may no longer have the task!).
- if (taskTopI < 0) {
- taskTopI = targetI;
- }
- if (below != null && below.task == task) {
- final boolean clearWhenTaskReset =
- (target.intent.getFlags()
- &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
- if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) {
- // If this activity is sending a reply to a previous
- // activity, we can't do anything with it now until
- // we reach the start of the reply chain.
- // XXX note that we are assuming the result is always
- // to the previous activity, which is almost always
- // the case but we really shouldn't count on.
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting
- && target.taskAffinity != null
- && !target.taskAffinity.equals(task.affinity)) {
- // If this activity has an affinity for another
- // task, then we need to move it out of here. We will
- // move it as far out of the way as possible, to the
- // bottom of the activity stack. This also keeps it
- // correctly ordered with any activities we previously
- // moved.
- HistoryRecord p = (HistoryRecord)mHistory.get(0);
- if (target.taskAffinity != null
- && target.taskAffinity.equals(p.task.affinity)) {
- // If the activity currently at the bottom has the
- // same task affinity as the one we are moving,
- // then merge it into the same task.
- target.task = p.task;
- if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
- + " out to bottom task " + p.task);
- } else {
- mCurTask++;
- if (mCurTask <= 0) {
- mCurTask = 1;
- }
- target.task = new TaskRecord(mCurTask, target.info, null,
- (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- target.task.affinityIntent = target.intent;
- if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
- + " out to new task " + target.task);
- }
- mWindowManager.setAppGroupId(target, task.taskId);
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- int dstPos = 0;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (HistoryRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
- + " out to target's task " + target.task);
- task.numActivities--;
- p.task = target.task;
- target.task.numActivities++;
- mHistory.remove(srcPos);
- mHistory.add(dstPos, p);
- mWindowManager.moveAppToken(dstPos, p);
- mWindowManager.setAppGroupId(p, p.task.taskId);
- dstPos++;
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- i++;
- }
- if (taskTop == p) {
- taskTop = below;
- }
- if (taskTopI == replyChainEnd) {
- taskTopI = -1;
- }
- replyChainEnd = -1;
- addRecentTaskLocked(target.task);
- } else if (forceReset || finishOnTaskLaunch
- || clearWhenTaskReset) {
- // If the activity should just be removed -- either
- // because it asks for it, or the task should be
- // cleared -- then finish it and anything that is
- // part of its reply chain.
- if (clearWhenTaskReset) {
- // In this case, we want to finish this activity
- // and everything above it, so be sneaky and pretend
- // like these are all in the reply chain.
- replyChainEnd = targetI+1;
- while (replyChainEnd < mHistory.size() &&
- ((HistoryRecord)mHistory.get(
- replyChainEnd)).task == task) {
- replyChainEnd++;
- }
- replyChainEnd--;
- } else if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- HistoryRecord p = null;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (HistoryRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset")) {
- replyChainEnd--;
- srcPos--;
- }
- }
- if (taskTop == p) {
- taskTop = below;
- }
- if (taskTopI == replyChainEnd) {
- taskTopI = -1;
- }
- replyChainEnd = -1;
- } else {
- // If we were in the middle of a chain, well the
- // activity that started it all doesn't want anything
- // special, so leave it all as-is.
- replyChainEnd = -1;
- }
- } else {
- // Reached the bottom of the task -- any reply chain
- // should be left as-is.
- replyChainEnd = -1;
- }
-
- } else if (target.resultTo != null) {
- // If this activity is sending a reply to a previous
- // activity, we can't do anything with it now until
- // we reach the start of the reply chain.
- // XXX note that we are assuming the result is always
- // to the previous activity, which is almost always
- // the case but we really shouldn't count on.
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
-
- } else if (taskTopI >= 0 && allowTaskReparenting
- && task.affinity != null
- && task.affinity.equals(target.taskAffinity)) {
- // We are inside of another task... if this activity has
- // an affinity for our task, then either remove it if we are
- // clearing or move it over to our task. Note that
- // we currently punt on the case where we are resetting a
- // task that is not at the top but who has activities above
- // with an affinity to it... this is really not a normal
- // case, and we will need to later pull that task to the front
- // and usually at that point we will do the reset and pick
- // up those remaining activities. (This only happens if
- // someone starts an activity in a new task from an activity
- // in a task that is not currently on top.)
- if (forceReset || finishOnTaskLaunch) {
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- HistoryRecord p = null;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (HistoryRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset")) {
- taskTopI--;
- lastReparentPos--;
- replyChainEnd--;
- srcPos--;
- }
- }
- replyChainEnd = -1;
- } else {
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
- HistoryRecord p = (HistoryRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (lastReparentPos < 0) {
- lastReparentPos = taskTopI;
- taskTop = p;
- } else {
- lastReparentPos--;
- }
- mHistory.remove(srcPos);
- p.task.numActivities--;
- p.task = task;
- mHistory.add(lastReparentPos, p);
- if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
- + " in to resetting task " + task);
- task.numActivities++;
- mWindowManager.moveAppToken(lastReparentPos, p);
- mWindowManager.setAppGroupId(p, p.task.taskId);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- }
- replyChainEnd = -1;
-
- // Now we've moved it in to place... but what if this is
- // a singleTop activity and we have put it on top of another
- // instance of the same activity? Then we drop the instance
- // below so it remains singleTop.
- if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
- for (int j=lastReparentPos-1; j>=0; j--) {
- HistoryRecord p = (HistoryRecord)mHistory.get(j);
- if (p.finishing) {
- continue;
- }
- if (p.intent.getComponent().equals(target.intent.getComponent())) {
- if (finishActivityLocked(p, j,
- Activity.RESULT_CANCELED, null, "replace")) {
- taskTopI--;
- lastReparentPos--;
- }
- }
- }
- }
- }
- }
-
- target = below;
- targetI = i;
- }
-
- return taskTop;
- }
-
- /**
* TODO: Add mController hook
*/
public void moveTaskToFront(int task) {
@@ -7322,14 +4928,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (tr.taskId == task) {
- moveTaskToFrontLocked(tr, null);
+ mMainStack.moveTaskToFrontLocked(tr, null);
return;
}
}
- for (int i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord hr = (HistoryRecord)mHistory.get(i);
+ for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
if (hr.task.taskId == task) {
- moveTaskToFrontLocked(hr.task, null);
+ mMainStack.moveTaskToFrontLocked(hr.task, null);
return;
}
}
@@ -7339,84 +4945,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void moveTaskToFrontLocked(TaskRecord tr, HistoryRecord reason) {
- if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
-
- final int task = tr.taskId;
- int top = mHistory.size()-1;
-
- if (top < 0 || ((HistoryRecord)mHistory.get(top)).task.taskId == task) {
- // nothing to do!
- return;
- }
-
- ArrayList moved = new ArrayList();
-
- // Applying the affinities may have removed entries from the history,
- // so get the size again.
- top = mHistory.size()-1;
- int pos = top;
-
- // Shift all activities with this task up to the top
- // of the stack, keeping them in the same internal order.
- while (pos >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(pos);
- if (localLOGV) Slog.v(
- TAG, "At " + pos + " ckp " + r.task + ": " + r);
- boolean first = true;
- if (r.task.taskId == task) {
- if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
- mHistory.remove(pos);
- mHistory.add(top, r);
- moved.add(0, r);
- top--;
- if (first) {
- addRecentTaskLocked(r.task);
- first = false;
- }
- }
- pos--;
- }
-
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare to front transition: task=" + tr);
- if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- HistoryRecord r = topRunningActivityLocked(null);
- if (r != null) {
- mNoAnimActivities.add(r);
- }
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
- }
-
- mWindowManager.moveAppTokensToTop(moved);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- finishTaskMoveLocked(task);
- EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task);
- }
-
- private final void finishTaskMoveLocked(int task) {
- resumeTopActivityLocked(null);
- }
-
public void moveTaskToBack(int task) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToBack()");
synchronized(this) {
- if (mResumedActivity != null && mResumedActivity.task.taskId == task) {
+ if (mMainStack.mResumedActivity != null
+ && mMainStack.mResumedActivity.task.taskId == task) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), "Task to back")) {
return;
}
}
final long origId = Binder.clearCallingIdentity();
- moveTaskToBackLocked(task, null);
+ mMainStack.moveTaskToBackLocked(task, null);
Binder.restoreCallingIdentity(origId);
}
}
@@ -7435,93 +4977,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
int taskId = getTaskForActivityLocked(token, !nonRoot);
if (taskId >= 0) {
- return moveTaskToBackLocked(taskId, null);
+ return mMainStack.moveTaskToBackLocked(taskId, null);
}
Binder.restoreCallingIdentity(origId);
}
return false;
}
- /**
- * Worker method for rearranging history stack. Implements the function of moving all
- * activities for a specific task (gathering them if disjoint) into a single group at the
- * bottom of the stack.
- *
- * If a watcher is installed, the action is preflighted and the watcher has an opportunity
- * to premeptively cancel the move.
- *
- * @param task The taskId to collect and move to the bottom.
- * @return Returns true if the move completed, false if not.
- */
- private final boolean moveTaskToBackLocked(int task, HistoryRecord reason) {
- Slog.i(TAG, "moveTaskToBack: " + task);
-
- // If we have a watcher, preflight the move before committing to it. First check
- // for *other* available tasks, but if none are available, then try again allowing the
- // current task to be selected.
- if (mController != null) {
- HistoryRecord next = topRunningActivityLocked(null, task);
- if (next == null) {
- next = topRunningActivityLocked(null, 0);
- }
- if (next != null) {
- // ask watcher if this is allowed
- boolean moveOK = true;
- try {
- moveOK = mController.activityResuming(next.packageName);
- } catch (RemoteException e) {
- mController = null;
- }
- if (!moveOK) {
- return false;
- }
- }
- }
-
- ArrayList moved = new ArrayList();
-
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare to back transition: task=" + task);
-
- final int N = mHistory.size();
- int bottom = 0;
- int pos = 0;
-
- // Shift all activities with this task down to the bottom
- // of the stack, keeping them in the same internal order.
- while (pos < N) {
- HistoryRecord r = (HistoryRecord)mHistory.get(pos);
- if (localLOGV) Slog.v(
- TAG, "At " + pos + " ckp " + r.task + ": " + r);
- if (r.task.taskId == task) {
- if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
- mHistory.remove(pos);
- mHistory.add(bottom, r);
- moved.add(r);
- bottom++;
- }
- pos++;
- }
-
- if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- HistoryRecord r = topRunningActivityLocked(null);
- if (r != null) {
- mNoAnimActivities.add(r);
- }
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
- }
- mWindowManager.moveAppTokensToBottom(moved);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- finishTaskMoveLocked(task);
- return true;
- }
-
public void moveTaskBackwards(int task) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskBackwards()");
@@ -7548,10 +5010,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
- final int N = mHistory.size();
+ final int N = mMainStack.mHistory.size();
TaskRecord lastTask = null;
for (int i=0; i<N; i++) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r == token) {
if (!onlyRoot || lastTask != r.task) {
return r.task.taskId;
@@ -7564,89 +5026,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return -1;
}
- /**
- * Returns the top activity in any existing task matching the given
- * Intent. Returns null if no such task is found.
- */
- private HistoryRecord findTaskLocked(Intent intent, ActivityInfo info) {
- ComponentName cls = intent.getComponent();
- if (info.targetActivity != null) {
- cls = new ComponentName(info.packageName, info.targetActivity);
- }
-
- TaskRecord cp = null;
-
- final int N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (!r.finishing && r.task != cp
- && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- cp = r.task;
- //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
- // + "/aff=" + r.task.affinity + " to new cls="
- // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
- if (r.task.affinity != null) {
- if (r.task.affinity.equals(info.taskAffinity)) {
- //Slog.i(TAG, "Found matching affinity!");
- return r;
- }
- } else if (r.task.intent != null
- && r.task.intent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
- return r;
- } else if (r.task.affinityIntent != null
- && r.task.affinityIntent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
- return r;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Returns the first activity (starting from the top of the stack) that
- * is the same as the given activity. Returns null if no such activity
- * is found.
- */
- private HistoryRecord findActivityLocked(Intent intent, ActivityInfo info) {
- ComponentName cls = intent.getComponent();
- if (info.targetActivity != null) {
- cls = new ComponentName(info.packageName, info.targetActivity);
- }
-
- final int N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (!r.finishing) {
- if (r.intent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
- return r;
- }
- }
- }
-
- return null;
- }
-
public void finishOtherInstances(IBinder token, ComponentName className) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
- int N = mHistory.size();
+ int N = mMainStack.mHistory.size();
TaskRecord lastTask = null;
for (int i=0; i<N; i++) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.realActivity.equals(className)
&& r != token && lastTask != r.task) {
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
null, "others")) {
i--;
N--;
@@ -7671,7 +5061,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Binder.restoreCallingIdentity(origId);
}
- final void sendPendingThumbnail(HistoryRecord r, IBinder token,
+ final void sendPendingThumbnail(ActivityRecord r, IBinder token,
Bitmap thumbnail, CharSequence description, boolean always) {
TaskRecord task = null;
ArrayList receivers = null;
@@ -7680,11 +5070,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(this) {
if (r == null) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- r = (HistoryRecord)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
}
if (thumbnail == null) {
thumbnail = r.thumbnail;
@@ -7745,7 +5135,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private final List generateApplicationProvidersLocked(ProcessRecord app) {
List providers = null;
try {
- providers = ActivityThread.getPackageManager().
+ providers = AppGlobals.getPackageManager().
queryContentProviders(app.processName, app.info.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
@@ -7755,8 +5145,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
- ContentProviderRecord cpr =
- (ContentProviderRecord)mProvidersByClass.get(cpi.name);
+ ContentProviderRecord cpr = mProvidersByClass.get(cpi.name);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info);
mProvidersByClass.put(cpi.name, cpr);
@@ -7770,13 +5159,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
private final String checkContentProviderPermissionLocked(
- ProviderInfo cpi, ProcessRecord r, int mode) {
+ ProviderInfo cpi, ProcessRecord r) {
final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
cpi.exported ? -1 : cpi.applicationInfo.uid)
- == PackageManager.PERMISSION_GRANTED
- && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+ == PackageManager.PERMISSION_GRANTED) {
return null;
}
if (checkComponentPermission(cpi.writePermission, callingPid, callingUid,
@@ -7793,8 +5181,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
PathPermission pp = pps[i];
if (checkComponentPermission(pp.getReadPermission(), callingPid, callingUid,
cpi.exported ? -1 : cpi.applicationInfo.uid)
- == PackageManager.PERMISSION_GRANTED
- && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+ == PackageManager.PERMISSION_GRANTED) {
return null;
}
if (checkComponentPermission(pp.getWritePermission(), callingPid, callingUid,
@@ -7805,6 +5192,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+ if (perms != null) {
+ for (Map.Entry<Uri, UriPermission> uri : perms.entrySet()) {
+ if (uri.getKey().getAuthority().equals(cpi.authority)) {
+ return null;
+ }
+ }
+ }
+
String msg = "Permission Denial: opening provider " + cpi.name
+ " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
+ ", uid=" + callingUid + ") requires "
@@ -7831,13 +5227,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// First check if this content provider has been published...
- cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ cpr = mProvidersByName.get(name);
if (cpr != null) {
cpi = cpr.info;
- if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
- return new ContentProviderHolder(cpi,
- cpi.readPermission != null
- ? cpi.readPermission : cpi.writePermission);
+ String msg;
+ if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+ throw new SecurityException(msg);
}
if (r != null && cpr.canRunHere(r)) {
@@ -7869,8 +5264,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
}
cpr.clients.add(r);
- if (cpr.app != null && r.setAdj >= VISIBLE_APP_ADJ) {
- // If this is a visible app accessing the provider,
+ if (cpr.app != null && r.setAdj <= PERCEPTIBLE_APP_ADJ) {
+ // If this is a perceptible app accessing the provider,
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
// content providers are often expensive to start.
@@ -7888,7 +5283,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else {
try {
- cpi = ActivityThread.getPackageManager().
+ cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
@@ -7897,13 +5292,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return null;
}
- if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
- return new ContentProviderHolder(cpi,
- cpi.readPermission != null
- ? cpi.readPermission : cpi.writePermission);
+ String msg;
+ if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+ throw new SecurityException(msg);
}
- if (!mSystemReady && !mDidUpdate && !mWaitingUpdate
+ if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate
&& !cpi.processName.equals("system")) {
// If this content provider does not run in the system
// process, and the system is not yet ready to run other
@@ -7912,12 +5306,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
"Attempt to launch content provider before system ready");
}
- cpr = (ContentProviderRecord)mProvidersByClass.get(cpi.name);
+ cpr = mProvidersByClass.get(cpi.name);
final boolean firstClass = cpr == null;
if (firstClass) {
try {
ApplicationInfo ai =
- ActivityThread.getPackageManager().
+ AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS);
@@ -8046,7 +5440,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
*/
public void removeContentProvider(IApplicationThread caller, String name) {
synchronized (this) {
- ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if(cpr == null) {
// remove from mProvidersByClass
if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -8060,8 +5454,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
" when removing content provider " + name);
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord)
- mProvidersByClass.get(cpr.info.name);
+ ContentProviderRecord localCpr = mProvidersByClass.get(cpr.info.name);
if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+ r.info.processName + " from process "
+ localCpr.appInfo.processName);
@@ -8085,7 +5478,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private void removeContentProviderExternal(String name) {
synchronized (this) {
- ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -8093,7 +5486,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord) mProvidersByClass.get(cpr.info.name);
+ ContentProviderRecord localCpr = mProvidersByClass.get(cpr.info.name);
localCpr.externals--;
if (localCpr.externals < 0) {
Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -8125,8 +5518,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (src == null || src.info == null || src.provider == null) {
continue;
}
- ContentProviderRecord dst =
- (ContentProviderRecord)r.pubProviders.get(src.info.name);
+ ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
mProvidersByClass.put(dst.info.name, dst);
String names[] = dst.info.authority.split(";");
@@ -8177,6 +5569,38 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ /**
+ * Allows app to retrieve the MIME type of a URI without having permission
+ * to access its content provider.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+ * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ */
+ public String getProviderMimeType(Uri uri) {
+ final String name = uri.getAuthority();
+ final long ident = Binder.clearCallingIdentity();
+ ContentProviderHolder holder = null;
+
+ try {
+ holder = getContentProviderExternal(name);
+ if (holder != null) {
+ return holder.provider.getType(uri);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Content provider dead retrieving " + uri, e);
+ return null;
+ } finally {
+ if (holder != null) {
+ removeContentProviderExternal(name);
+ }
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ return null;
+ }
+
// =========================================================
// GLOBAL MANAGEMENT
// =========================================================
@@ -8219,12 +5643,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
"unhandledBack()");
synchronized(this) {
- int count = mHistory.size();
+ int count = mMainStack.mHistory.size();
if (DEBUG_SWITCH) Slog.d(
TAG, "Performing unhandledBack(): stack size = " + count);
if (count > 1) {
final long origId = Binder.clearCallingIdentity();
- finishActivityLocked((HistoryRecord)mHistory.get(count-1),
+ mMainStack.finishActivityLocked((ActivityRecord)mMainStack.mHistory.get(count-1),
count-1, Activity.RESULT_CANCELED, null, "unhandled-back");
Binder.restoreCallingIdentity(origId);
}
@@ -8267,11 +5691,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mSleeping = true;
mWindowManager.setEventDispatching(false);
- if (mResumedActivity != null) {
- pauseIfSleepingLocked();
+ if (mMainStack.mResumedActivity != null) {
+ mMainStack.pauseIfSleepingLocked();
} else {
Slog.w(TAG, "goingToSleep with no resumed activity!");
}
+
+ // Initialize the wake times of all processes.
+ checkExcessivePowerUsageLocked(false);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
}
}
@@ -8288,10 +5718,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mShuttingDown = true;
mWindowManager.setEventDispatching(false);
- if (mResumedActivity != null) {
- pauseIfSleepingLocked();
+ if (mMainStack.mResumedActivity != null) {
+ mMainStack.pauseIfSleepingLocked();
final long endTime = System.currentTimeMillis() + timeout;
- while (mResumedActivity != null || mPausingActivity != null) {
+ while (mMainStack.mResumedActivity != null
+ || mMainStack.mPausingActivity != null) {
long delay = endTime - System.currentTimeMillis();
if (delay <= 0) {
Slog.w(TAG, "Activity manager shutdown timed out");
@@ -8312,35 +5743,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return timedout;
}
- void pauseIfSleepingLocked() {
- if (mSleeping || mShuttingDown) {
- if (!mGoingToSleep.isHeld()) {
- mGoingToSleep.acquire();
- if (mLaunchingActivity.isHeld()) {
- mLaunchingActivity.release();
- mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- }
- }
-
- // If we are not currently pausing an activity, get the current
- // one to pause. If we are pausing one, we will just let that stuff
- // run and release the wake lock when all done.
- if (mPausingActivity == null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
- startPausingLocked(false, true);
- }
- }
- }
-
public void wakingUp() {
synchronized(this) {
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
+ if (mMainStack.mGoingToSleep.isHeld()) {
+ mMainStack.mGoingToSleep.release();
}
mWindowManager.setEventDispatching(true);
mSleeping = false;
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
}
@@ -8474,14 +5884,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// and started launching other packages.
if (!mSystemReady) {
try {
- ActivityThread.getPackageManager().enterSafeMode();
+ AppGlobals.getPackageManager().enterSafeMode();
} catch (RemoteException e) {
}
View v = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.safe_mode, null);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
- lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+ lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
@@ -8560,123 +5970,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return killed;
}
- public void reportPss(IApplicationThread caller, int pss) {
- Watchdog.PssRequestor req;
- String name;
- ProcessRecord callerApp;
- synchronized (this) {
- if (caller == null) {
- return;
- }
- callerApp = getRecordForAppLocked(caller);
- if (callerApp == null) {
- return;
- }
- callerApp.lastPss = pss;
- req = callerApp;
- name = callerApp.processName;
- }
- Watchdog.getInstance().reportPss(req, name, pss);
- if (!callerApp.persistent) {
- removeRequestedPss(callerApp);
- }
- }
-
- public void requestPss(Runnable completeCallback) {
- ArrayList<ProcessRecord> procs;
- synchronized (this) {
- mRequestPssCallback = completeCallback;
- mRequestPssList.clear();
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord proc = mLruProcesses.get(i);
- if (!proc.persistent) {
- mRequestPssList.add(proc);
- }
- }
- procs = new ArrayList<ProcessRecord>(mRequestPssList);
- }
-
- int oldPri = Process.getThreadPriority(Process.myTid());
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- for (int i=procs.size()-1; i>=0; i--) {
- ProcessRecord proc = procs.get(i);
- proc.lastPss = 0;
- proc.requestPss();
- }
- Process.setThreadPriority(oldPri);
- }
-
- void removeRequestedPss(ProcessRecord proc) {
- Runnable callback = null;
- synchronized (this) {
- if (mRequestPssList.remove(proc)) {
- if (mRequestPssList.size() == 0) {
- callback = mRequestPssCallback;
- mRequestPssCallback = null;
- }
- }
- }
-
- if (callback != null) {
- callback.run();
- }
- }
-
- public void collectPss(Watchdog.PssStats stats) {
- stats.mEmptyPss = 0;
- stats.mEmptyCount = 0;
- stats.mBackgroundPss = 0;
- stats.mBackgroundCount = 0;
- stats.mServicePss = 0;
- stats.mServiceCount = 0;
- stats.mVisiblePss = 0;
- stats.mVisibleCount = 0;
- stats.mForegroundPss = 0;
- stats.mForegroundCount = 0;
- stats.mNoPssCount = 0;
- synchronized (this) {
- int i;
- int NPD = mProcDeaths.length < stats.mProcDeaths.length
- ? mProcDeaths.length : stats.mProcDeaths.length;
- int aggr = 0;
- for (i=0; i<NPD; i++) {
- aggr += mProcDeaths[i];
- stats.mProcDeaths[i] = aggr;
- }
- while (i<stats.mProcDeaths.length) {
- stats.mProcDeaths[i] = 0;
- i++;
- }
-
- for (i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord proc = mLruProcesses.get(i);
- if (proc.persistent) {
- continue;
- }
- //Slog.i(TAG, "Proc " + proc + ": pss=" + proc.lastPss);
- if (proc.lastPss == 0) {
- stats.mNoPssCount++;
- continue;
- }
- if (proc.setAdj >= HIDDEN_APP_MIN_ADJ) {
- if (proc.empty) {
- stats.mEmptyPss += proc.lastPss;
- stats.mEmptyCount++;
- } else {
- stats.mBackgroundPss += proc.lastPss;
- stats.mBackgroundCount++;
- }
- } else if (proc.setAdj >= VISIBLE_APP_ADJ) {
- stats.mVisiblePss += proc.lastPss;
- stats.mVisibleCount++;
- } else {
- stats.mForegroundPss += proc.lastPss;
- stats.mForegroundCount++;
- }
- }
- }
- }
-
public final void startRunning(String pkg, String cls, String action,
String data) {
synchronized(this) {
@@ -8725,6 +6018,77 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return mSystemReady;
}
+ private static File getCalledPreBootReceiversFile() {
+ File dataDir = Environment.getDataDirectory();
+ File systemDir = new File(dataDir, "system");
+ File fname = new File(systemDir, "called_pre_boots.dat");
+ return fname;
+ }
+
+ private static ArrayList<ComponentName> readLastDonePreBootReceivers() {
+ ArrayList<ComponentName> lastDoneReceivers = new ArrayList<ComponentName>();
+ File file = getCalledPreBootReceiversFile();
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(fis, 2048));
+ int vers = dis.readInt();
+ String codename = dis.readUTF();
+ if (vers == android.os.Build.VERSION.SDK_INT
+ && codename.equals(android.os.Build.VERSION.CODENAME)) {
+ int num = dis.readInt();
+ while (num > 0) {
+ num--;
+ String pkg = dis.readUTF();
+ String cls = dis.readUTF();
+ lastDoneReceivers.add(new ComponentName(pkg, cls));
+ }
+ }
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure reading last done pre-boot receivers", e);
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return lastDoneReceivers;
+ }
+
+ private static void writeLastDonePreBootReceivers(ArrayList<ComponentName> list) {
+ File file = getCalledPreBootReceiversFile();
+ FileOutputStream fos = null;
+ DataOutputStream dos = null;
+ try {
+ Slog.i(TAG, "Writing new set of last done pre-boot receivers...");
+ fos = new FileOutputStream(file);
+ dos = new DataOutputStream(new BufferedOutputStream(fos, 2048));
+ dos.writeInt(android.os.Build.VERSION.SDK_INT);
+ dos.writeUTF(android.os.Build.VERSION.CODENAME);
+ dos.writeInt(list.size());
+ for (int i=0; i<list.size(); i++) {
+ dos.writeUTF(list.get(i).getPackageName());
+ dos.writeUTF(list.get(i).getClassName());
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure writing last done pre-boot receivers", e);
+ file.delete();
+ } finally {
+ FileUtils.sync(fos);
+ if (dos != null) {
+ try {
+ dos.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
public void systemReady(final Runnable goingCallback) {
// In the simulator, startRunning will never have been called, which
// normally sets a few crucial variables. Do it here instead.
@@ -8747,7 +6111,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
List<ResolveInfo> ris = null;
try {
- ris = ActivityThread.getPackageManager().queryIntentReceivers(
+ ris = AppGlobals.getPackageManager().queryIntentReceivers(
intent, null, 0);
} catch (RemoteException e) {
}
@@ -8759,20 +6123,42 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+
+ ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
+
+ final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
+ for (int i=0; i<ris.size(); i++) {
+ ActivityInfo ai = ris.get(i).activityInfo;
+ ComponentName comp = new ComponentName(ai.packageName, ai.name);
+ if (lastDoneReceivers.contains(comp)) {
+ ris.remove(i);
+ i--;
+ }
+ }
+
for (int i=0; i<ris.size(); i++) {
ActivityInfo ai = ris.get(i).activityInfo;
- intent.setComponent(new ComponentName(ai.packageName, ai.name));
+ ComponentName comp = new ComponentName(ai.packageName, ai.name);
+ doneReceivers.add(comp);
+ intent.setComponent(comp);
IIntentReceiver finisher = null;
if (i == ris.size()-1) {
finisher = new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
- boolean sticky)
- throws RemoteException {
- synchronized (ActivityManagerService.this) {
- mDidUpdate = true;
- }
- systemReady(goingCallback);
+ boolean sticky) {
+ // The raw IIntentReceiver interface is called
+ // with the AM lock held, so redispatch to
+ // execute our code without the lock.
+ mHandler.post(new Runnable() {
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ mDidUpdate = true;
+ }
+ writeLastDonePreBootReceivers(doneReceivers);
+ systemReady(goingCallback);
+ }
+ });
}
};
}
@@ -8809,14 +6195,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- if (procsToKill != null) {
- synchronized(this) {
+ synchronized(this) {
+ if (procsToKill != null) {
for (int i=procsToKill.size()-1; i>=0; i--) {
ProcessRecord proc = procsToKill.get(i);
Slog.i(TAG, "Removing system update proc: " + proc);
removeProcessLocked(proc, true);
}
}
+
+ // Now that we have cleaned up any update processes, we
+ // are ready to start launching real processes and know that
+ // we won't trample on them any more.
+ mProcessesReady = true;
}
Slog.i(TAG, "System now ready");
@@ -8866,7 +6257,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
- List apps = ActivityThread.getPackageManager().
+ List apps = AppGlobals.getPackageManager().
getPersistentApplications(STOCK_PM_FLAGS);
if (apps != null) {
int N = apps.size();
@@ -8889,7 +6280,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mBooting = true;
try {
- if (ActivityThread.getPackageManager().hasSystemUidErrors()) {
+ if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
Message msg = Message.obtain();
msg.what = SHOW_UID_ERROR_MSG;
mHandler.sendMessage(msg);
@@ -8897,7 +6288,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} catch (RemoteException e) {
}
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
}
@@ -8985,12 +6376,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.info.processName, app.info.uid);
killServicesLocked(app, false);
- for (int i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.app == app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
+ r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
}
}
if (!app.persistent) {
@@ -9008,28 +6399,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return false;
}
} else {
- HistoryRecord r = topRunningActivityLocked(null);
+ ActivityRecord r = mMainStack.topRunningActivityLocked(null);
if (r.app == app) {
// If the top running activity is from this crashing
// process, then terminate it to avoid getting in a loop.
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- int index = indexOfTokenLocked(r);
- finishActivityLocked(r, index,
+ int index = mMainStack.indexOfTokenLocked(r);
+ r.stack.finishActivityLocked(r, index,
Activity.RESULT_CANCELED, null, "crashed");
// Also terminate an activities below it that aren't yet
// stopped, to avoid a situation where one will get
// re-start our crashing activity once it gets resumed again.
index--;
if (index >= 0) {
- r = (HistoryRecord)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r.state == ActivityState.RESUMED
|| r.state == ActivityState.PAUSING
|| r.state == ActivityState.PAUSED) {
if (!r.isHomeActivity) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishActivityLocked(r, index,
+ r.stack.finishActivityLocked(r, index,
Activity.RESULT_CANCELED, null, "crashed");
}
}
@@ -9041,9 +6432,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (app.services.size() != 0) {
// Any services running in the application need to be placed
// back in the pending list.
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord sr = (ServiceRecord)it.next();
+ ServiceRecord sr = it.next();
sr.crashCount++;
}
}
@@ -9056,7 +6447,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
&& (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
Iterator it = mHomeProcess.activities.iterator();
while (it.hasNext()) {
- HistoryRecord r = (HistoryRecord)it.next();
+ ActivityRecord r = (ActivityRecord)it.next();
if (r.isHomeActivity) {
Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
try {
@@ -9086,7 +6477,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// The current broadcast is waiting for this app's receiver
// to be finished. Looks like that's not going to happen, so
// let the broadcast continue.
- logBroadcastReceiverDiscard(r);
+ logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, true);
reschedule = true;
@@ -9095,7 +6486,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r != null && r.curApp == app) {
if (DEBUG_BROADCAST) Slog.v(TAG,
"skip & discard pending app " + r);
- logBroadcastReceiverDiscard(r);
+ logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, true);
reschedule = true;
@@ -9127,6 +6518,161 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
crashApplication(r, crashInfo);
}
+ public void handleApplicationStrictModeViolation(
+ IBinder app,
+ int violationMask,
+ StrictMode.ViolationInfo info) {
+ ProcessRecord r = findAppProcess(app);
+
+ if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
+ Integer stackFingerprint = info.crashInfo.stackTrace.hashCode();
+ boolean logIt = true;
+ synchronized (mAlreadyLoggedViolatedStacks) {
+ if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
+ logIt = false;
+ // TODO: sub-sample into EventLog for these, with
+ // the info.durationMillis? Then we'd get
+ // the relative pain numbers, without logging all
+ // the stack traces repeatedly. We'd want to do
+ // likewise in the client code, which also does
+ // dup suppression, before the Binder call.
+ } else {
+ if (mAlreadyLoggedViolatedStacks.size() >= MAX_DUP_SUPPRESSED_STACKS) {
+ mAlreadyLoggedViolatedStacks.clear();
+ }
+ mAlreadyLoggedViolatedStacks.add(stackFingerprint);
+ }
+ }
+ if (logIt) {
+ logStrictModeViolationToDropBox(r, info);
+ }
+ }
+
+ if ((violationMask & StrictMode.PENALTY_DIALOG) != 0) {
+ AppErrorResult result = new AppErrorResult();
+ synchronized (this) {
+ final long origId = Binder.clearCallingIdentity();
+
+ Message msg = Message.obtain();
+ msg.what = SHOW_STRICT_MODE_VIOLATION_MSG;
+ HashMap<String, Object> data = new HashMap<String, Object>();
+ data.put("result", result);
+ data.put("app", r);
+ data.put("violationMask", violationMask);
+ data.put("info", info);
+ msg.obj = data;
+ mHandler.sendMessage(msg);
+
+ Binder.restoreCallingIdentity(origId);
+ }
+ int res = result.get();
+ Slog.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+ }
+ }
+
+ // Depending on the policy in effect, there could be a bunch of
+ // these in quick succession so we try to batch these together to
+ // minimize disk writes, number of dropbox entries, and maximize
+ // compression, by having more fewer, larger records.
+ private void logStrictModeViolationToDropBox(
+ ProcessRecord process,
+ StrictMode.ViolationInfo info) {
+ if (info == null) {
+ return;
+ }
+ final boolean isSystemApp = process == null ||
+ (process.info.flags & (ApplicationInfo.FLAG_SYSTEM |
+ ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
+ final String dropboxTag = isSystemApp ? "system_app_strictmode" : "data_app_strictmode";
+ final DropBoxManager dbox = (DropBoxManager)
+ mContext.getSystemService(Context.DROPBOX_SERVICE);
+
+ // Exit early if the dropbox isn't configured to accept this report type.
+ if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
+
+ boolean bufferWasEmpty;
+ boolean needsFlush;
+ final StringBuilder sb = isSystemApp ? mStrictModeBuffer : new StringBuilder(1024);
+ synchronized (sb) {
+ bufferWasEmpty = sb.length() == 0;
+ appendDropBoxProcessHeaders(process, sb);
+ sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+ sb.append("System-App: ").append(isSystemApp).append("\n");
+ sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n");
+ if (info.violationNumThisLoop != 0) {
+ sb.append("Loop-Violation-Number: ").append(info.violationNumThisLoop).append("\n");
+ }
+ if (info != null && info.durationMillis != -1) {
+ sb.append("Duration-Millis: ").append(info.durationMillis).append("\n");
+ }
+ sb.append("\n");
+ if (info.crashInfo != null && info.crashInfo.stackTrace != null) {
+ sb.append(info.crashInfo.stackTrace);
+ }
+ sb.append("\n");
+
+ // Only buffer up to ~64k. Various logging bits truncate
+ // things at 128k.
+ needsFlush = (sb.length() > 64 * 1024);
+ }
+
+ // Flush immediately if the buffer's grown too large, or this
+ // is a non-system app. Non-system apps are isolated with a
+ // different tag & policy and not batched.
+ //
+ // Batching is useful during internal testing with
+ // StrictMode settings turned up high. Without batching,
+ // thousands of separate files could be created on boot.
+ if (!isSystemApp || needsFlush) {
+ new Thread("Error dump: " + dropboxTag) {
+ @Override
+ public void run() {
+ String report;
+ synchronized (sb) {
+ report = sb.toString();
+ sb.delete(0, sb.length());
+ sb.trimToSize();
+ }
+ if (report.length() != 0) {
+ dbox.addText(dropboxTag, report);
+ }
+ }
+ }.start();
+ return;
+ }
+
+ // System app batching:
+ if (!bufferWasEmpty) {
+ // An existing dropbox-writing thread is outstanding, so
+ // we don't need to start it up. The existing thread will
+ // catch the buffer appends we just did.
+ return;
+ }
+
+ // Worker thread to both batch writes and to avoid blocking the caller on I/O.
+ // (After this point, we shouldn't access AMS internal data structures.)
+ new Thread("Error dump: " + dropboxTag) {
+ @Override
+ public void run() {
+ // 5 second sleep to let stacks arrive and be batched together
+ try {
+ Thread.sleep(5000); // 5 seconds
+ } catch (InterruptedException e) {}
+
+ String errorReport;
+ synchronized (mStrictModeBuffer) {
+ errorReport = mStrictModeBuffer.toString();
+ if (errorReport.length() == 0) {
+ return;
+ }
+ mStrictModeBuffer.delete(0, mStrictModeBuffer.length());
+ mStrictModeBuffer.trimToSize();
+ }
+ dbox.addText(dropboxTag, errorReport);
+ }
+ }.start();
+ }
+
/**
* Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors.
* @param app object of the crashing app, null for the system server
@@ -9180,6 +6726,54 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
/**
+ * Utility function for addErrorToDropBox and handleStrictModeViolation's logging
+ * to append various headers to the dropbox log text.
+ */
+ private void appendDropBoxProcessHeaders(ProcessRecord process, StringBuilder sb) {
+ // Note: ProcessRecord 'process' is guarded by the service
+ // instance. (notably process.pkgList, which could otherwise change
+ // concurrently during execution of this method)
+ synchronized (this) {
+ if (process == null || process.pid == MY_PID) {
+ sb.append("Process: system_server\n");
+ } else {
+ sb.append("Process: ").append(process.processName).append("\n");
+ }
+ if (process == null) {
+ return;
+ }
+ int flags = process.info.flags;
+ IPackageManager pm = AppGlobals.getPackageManager();
+ sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n");
+ for (String pkg : process.pkgList) {
+ sb.append("Package: ").append(pkg);
+ try {
+ PackageInfo pi = pm.getPackageInfo(pkg, 0);
+ if (pi != null) {
+ sb.append(" v").append(pi.versionCode);
+ if (pi.versionName != null) {
+ sb.append(" (").append(pi.versionName).append(")");
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error getting package info: " + pkg, e);
+ }
+ sb.append("\n");
+ }
+ }
+ }
+
+ private static String processClass(ProcessRecord process) {
+ if (process == null || process.pid == MY_PID) {
+ return "system_server";
+ } else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return "system_app";
+ } else {
+ return "data_app";
+ }
+ }
+
+ /**
* Write a description of an error (crash, WTF, ANR) to the drop box.
* @param eventType to include in the drop box tag ("crash", "wtf", etc.)
* @param process which caused the error, null means the system server
@@ -9191,22 +6785,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* @param crashInfo giving an application stack trace, null if absent
*/
public void addErrorToDropBox(String eventType,
- ProcessRecord process, HistoryRecord activity, HistoryRecord parent, String subject,
+ ProcessRecord process, ActivityRecord activity, ActivityRecord parent, String subject,
final String report, final File logFile,
final ApplicationErrorReport.CrashInfo crashInfo) {
// NOTE -- this must never acquire the ActivityManagerService lock,
// otherwise the watchdog may be prevented from resetting the system.
- String prefix;
- if (process == null || process.pid == MY_PID) {
- prefix = "system_server_";
- } else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- prefix = "system_app_";
- } else {
- prefix = "data_app_";
- }
-
- final String dropboxTag = prefix + eventType;
+ final String dropboxTag = processClass(process) + "_" + eventType;
final DropBoxManager dbox = (DropBoxManager)
mContext.getSystemService(Context.DROPBOX_SERVICE);
@@ -9214,31 +6799,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
final StringBuilder sb = new StringBuilder(1024);
- if (process == null || process.pid == MY_PID) {
- sb.append("Process: system_server\n");
- } else {
- sb.append("Process: ").append(process.processName).append("\n");
- }
- if (process != null) {
- int flags = process.info.flags;
- IPackageManager pm = ActivityThread.getPackageManager();
- sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n");
- for (String pkg : process.pkgList) {
- sb.append("Package: ").append(pkg);
- try {
- PackageInfo pi = pm.getPackageInfo(pkg, 0);
- if (pi != null) {
- sb.append(" v").append(pi.versionCode);
- if (pi.versionName != null) {
- sb.append(" (").append(pi.versionName).append(")");
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Error getting package info: " + pkg, e);
- }
- sb.append("\n");
- }
- }
+ appendDropBoxProcessHeaders(process, sb);
if (activity != null) {
sb.append("Activity: ").append(activity.shortComponentName).append("\n");
}
@@ -9499,6 +7060,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
new ActivityManager.RunningAppProcessInfo(app.processName,
app.pid, app.getPackageList());
currApp.uid = app.info.uid;
+ if (mHeavyWeightProcess == app) {
+ currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
+ }
+ if (app.persistent) {
+ currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
+ }
int adj = app.curAdj;
if (adj >= EMPTY_APP_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY;
@@ -9510,6 +7077,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
currApp.lru = 0;
} else if (adj >= SECONDARY_SERVER_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
+ } else if (adj >= HEAVY_WEIGHT_APP_ADJ) {
+ currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
+ } else if (adj >= PERCEPTIBLE_APP_ADJ) {
+ currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
} else if (adj >= VISIBLE_APP_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
} else {
@@ -9518,8 +7089,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
currApp.importanceReasonCode = app.adjTypeCode;
if (app.adjSource instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
- } else if (app.adjSource instanceof HistoryRecord) {
- HistoryRecord r = (HistoryRecord)app.adjSource;
+ } else if (app.adjSource instanceof ActivityRecord) {
+ ActivityRecord r = (ActivityRecord)app.adjSource;
if (r.app != null) currApp.importanceReasonPid = r.app.pid;
}
if (app.adjTarget instanceof ComponentName) {
@@ -9549,7 +7120,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
}
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
for (String pkg : extList) {
try {
ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
@@ -9590,12 +7161,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pw.println("Activity manager dump options:");
pw.println(" [-a] [-h] [cmd] ...");
pw.println(" cmd may be one of:");
- pw.println(" activities: activity stack state");
- pw.println(" broadcasts: broadcast state");
- pw.println(" intents: pending intent state");
- pw.println(" processes: process state");
- pw.println(" providers: content provider state");
- pw.println(" services: service state");
+ pw.println(" a[ctivities]: activity stack state");
+ pw.println(" b[roadcasts]: broadcast state");
+ pw.println(" i[ntents]: pending intent state");
+ pw.println(" p[rocesses]: process state");
+ pw.println(" o[om]: out of memory management");
+ pw.println(" prov[iders]: content provider state");
+ pw.println(" s[ervices]: service state");
pw.println(" service [name]: service client-side state");
return;
} else {
@@ -9627,13 +7199,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
dumpProcessesLocked(fd, pw, args, opti, true);
}
return;
+ } else if ("oom".equals(cmd) || "o".equals(cmd)) {
+ synchronized (this) {
+ dumpOomLocked(fd, pw, args, opti, true);
+ }
+ return;
} else if ("providers".equals(cmd) || "prov".equals(cmd)) {
synchronized (this) {
dumpProvidersLocked(fd, pw, args, opti, true);
}
return;
} else if ("service".equals(cmd)) {
- dumpService(fd, pw, args, opti, true);
+ dumpService(fd, pw, args, opti, dumpAll);
return;
} else if ("services".equals(cmd) || "s".equals(cmd)) {
synchronized (this) {
@@ -9698,31 +7275,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (needHeader) {
pw.println(" Activity stack:");
}
- dumpHistoryList(pw, mHistory, " ", "Hist", true);
+ dumpHistoryList(pw, mMainStack.mHistory, " ", "Hist", true);
pw.println(" ");
pw.println(" Running activities (most recent first):");
- dumpHistoryList(pw, mLRUActivities, " ", "Run", false);
- if (mWaitingVisibleActivities.size() > 0) {
+ dumpHistoryList(pw, mMainStack.mLRUActivities, " ", "Run", false);
+ if (mMainStack.mWaitingVisibleActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting for another to become visible:");
- dumpHistoryList(pw, mWaitingVisibleActivities, " ", "Wait", false);
+ dumpHistoryList(pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false);
}
- if (mStoppingActivities.size() > 0) {
+ if (mMainStack.mStoppingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to stop:");
- dumpHistoryList(pw, mStoppingActivities, " ", "Stop", false);
+ dumpHistoryList(pw, mMainStack.mStoppingActivities, " ", "Stop", false);
}
- if (mFinishingActivities.size() > 0) {
+ if (mMainStack.mFinishingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to finish:");
- dumpHistoryList(pw, mFinishingActivities, " ", "Fin", false);
+ dumpHistoryList(pw, mMainStack.mFinishingActivities, " ", "Fin", false);
}
pw.println(" ");
- pw.println(" mPausingActivity: " + mPausingActivity);
- pw.println(" mResumedActivity: " + mResumedActivity);
+ pw.println(" mPausingActivity: " + mMainStack.mPausingActivity);
+ pw.println(" mResumedActivity: " + mMainStack.mResumedActivity);
pw.println(" mFocusedActivity: " + mFocusedActivity);
- pw.println(" mLastPausedActivity: " + mLastPausedActivity);
+ pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity);
if (dumpAll && mRecentTasks.size() > 0) {
pw.println(" ");
@@ -9742,7 +7319,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return true;
}
-
+
boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll) {
boolean needSep = false;
@@ -9772,8 +7349,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (needSep) pw.println(" ");
needSep = true;
pw.println(" Running processes (most recent first):");
- dumpProcessList(pw, this, mLruProcesses, " ",
- "App ", "PERS", true);
+ dumpProcessOomList(pw, this, mLruProcesses, " ",
+ "Proc", "PERS", false);
needSep = true;
}
@@ -9804,7 +7381,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
needSep = true;
pw.println(" Persisent processes that are starting:");
dumpProcessList(pw, this, mPersistentStartingProcesses, " ",
- "Starting Norm", "Restarting PERS", false);
+ "Starting Norm", "Restarting PERS");
}
if (mStartingProcesses.size() > 0) {
@@ -9812,7 +7389,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
needSep = true;
pw.println(" Processes that are starting:");
dumpProcessList(pw, this, mStartingProcesses, " ",
- "Starting Norm", "Starting PERS", false);
+ "Starting Norm", "Starting PERS");
}
if (mRemovedProcesses.size() > 0) {
@@ -9820,7 +7397,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
needSep = true;
pw.println(" Processes that are being removed:");
dumpProcessList(pw, this, mRemovedProcesses, " ",
- "Removed Norm", "Removed PERS", false);
+ "Removed Norm", "Removed PERS");
}
if (mProcessesOnHold.size() > 0) {
@@ -9828,26 +7405,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
needSep = true;
pw.println(" Processes that are on old until the system is ready:");
dumpProcessList(pw, this, mProcessesOnHold, " ",
- "OnHold Norm", "OnHold PERS", false);
+ "OnHold Norm", "OnHold PERS");
}
- if (mProcessesToGc.size() > 0) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Processes that are waiting to GC:");
- long now = SystemClock.uptimeMillis();
- for (int i=0; i<mProcessesToGc.size(); i++) {
- ProcessRecord proc = mProcessesToGc.get(i);
- pw.print(" Process "); pw.println(proc);
- pw.print(" lowMem="); pw.print(proc.reportLowMemory);
- pw.print(", last gced=");
- pw.print(now-proc.lastRequestedGc);
- pw.print(" ms ago, last lowMem=");
- pw.print(now-proc.lastLowMemory);
- pw.println(" ms ago");
-
- }
- }
+ needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll);
if (mProcessCrashTimes.getMap().size() > 0) {
if (needSep) pw.println(" ");
@@ -9887,8 +7448,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pw.println(" ");
pw.println(" mHomeProcess: " + mHomeProcess);
+ if (mHeavyWeightProcess != null) {
+ pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ }
pw.println(" mConfiguration: " + mConfiguration);
- pw.println(" mConfigWillChange: " + mConfigWillChange);
+ pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange);
pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
@@ -9903,18 +7467,94 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (dumpAll) {
pw.println(" Total persistent processes: " + numPers);
pw.println(" mStartRunning=" + mStartRunning
- + " mSystemReady=" + mSystemReady
- + " mBooting=" + mBooting
+ + " mProcessesReady=" + mProcessesReady
+ + " mSystemReady=" + mSystemReady);
+ pw.println(" mBooting=" + mBooting
+ " mBooted=" + mBooted
+ " mFactoryTest=" + mFactoryTest);
- pw.println(" mGoingToSleep=" + mGoingToSleep);
- pw.println(" mLaunchingActivity=" + mLaunchingActivity);
+ pw.print(" mLastPowerCheckRealtime=");
+ TimeUtils.formatDuration(mLastPowerCheckRealtime, pw);
+ pw.println("");
+ pw.print(" mLastPowerCheckUptime=");
+ TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
+ pw.println("");
+ pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep);
+ pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity);
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
}
return true;
}
+ boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean needSep, boolean dumpAll) {
+ if (mProcessesToGc.size() > 0) {
+ if (needSep) pw.println(" ");
+ needSep = true;
+ pw.println(" Processes that are waiting to GC:");
+ long now = SystemClock.uptimeMillis();
+ for (int i=0; i<mProcessesToGc.size(); i++) {
+ ProcessRecord proc = mProcessesToGc.get(i);
+ pw.print(" Process "); pw.println(proc);
+ pw.print(" lowMem="); pw.print(proc.reportLowMemory);
+ pw.print(", last gced=");
+ pw.print(now-proc.lastRequestedGc);
+ pw.print(" ms ago, last lowMem=");
+ pw.print(now-proc.lastLowMemory);
+ pw.println(" ms ago");
+
+ }
+ }
+ return needSep;
+ }
+
+ boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll) {
+ boolean needSep = false;
+
+ if (mLruProcesses.size() > 0) {
+ ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(mLruProcesses);
+
+ Comparator<ProcessRecord> comparator = new Comparator<ProcessRecord>() {
+ @Override
+ public int compare(ProcessRecord object1, ProcessRecord object2) {
+ if (object1.setAdj != object2.setAdj) {
+ return object1.setAdj > object2.setAdj ? -1 : 1;
+ }
+ if (object1.setSchedGroup != object2.setSchedGroup) {
+ return object1.setSchedGroup > object2.setSchedGroup ? -1 : 1;
+ }
+ if (object1.keeping != object2.keeping) {
+ return object1.keeping ? -1 : 1;
+ }
+ if (object1.pid != object2.pid) {
+ return object1.pid > object2.pid ? -1 : 1;
+ }
+ return 0;
+ }
+ };
+
+ Collections.sort(procs, comparator);
+
+ if (needSep) pw.println(" ");
+ needSep = true;
+ pw.println(" Process OOM control:");
+ dumpProcessOomList(pw, this, procs, " ",
+ "Proc", "PERS", true);
+ needSep = true;
+ }
+
+ needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll);
+
+ pw.println(" ");
+ pw.println(" mHomeProcess: " + mHomeProcess);
+ if (mHeavyWeightProcess != null) {
+ pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ }
+
+ return true;
+ }
+
/**
* There are three ways to call this:
* - no service specified: dump all the services
@@ -9936,20 +7576,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
componentNameString = args[opti];
opti++;
ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
- r = componentName != null ? mServices.get(componentName) : null;
+ synchronized (this) {
+ r = componentName != null ? mServices.get(componentName) : null;
+ }
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
}
if (r != null) {
- dumpService(fd, pw, r, newArgs);
+ dumpService(fd, pw, r, newArgs, dumpAll);
} else {
- for (ServiceRecord r1 : mServices.values()) {
- if (componentNameString == null
- || r1.name.flattenToString().contains(componentNameString)) {
- dumpService(fd, pw, r1, newArgs);
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ synchronized (this) {
+ for (ServiceRecord r1 : mServices.values()) {
+ if (componentNameString == null
+ || r1.name.flattenToString().contains(componentNameString)) {
+ services.add(r1);
+ }
}
}
+ for (int i=0; i<services.size(); i++) {
+ dumpService(fd, pw, services.get(i), newArgs, dumpAll);
+ }
}
}
@@ -9957,8 +7605,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* Invokes IApplicationThread.dumpService() on the thread of the specified service if
* there is a thread associated with the service.
*/
- private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args) {
+ private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args,
+ boolean dumpAll) {
pw.println(" Service " + r.name.flattenToString());
+ if (dumpAll) {
+ synchronized (this) {
+ pw.print(" * "); pw.println(r);
+ r.dump(pw, " ");
+ }
+ pw.println("");
+ }
if (r.app != null && r.app.thread != null) {
try {
// flush anything that is already in the PrintWriter since the thread is going
@@ -9966,6 +7622,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pw.flush();
r.app.thread.dumpService(fd, r, args);
pw.print("\n");
+ pw.flush();
} catch (RemoteException e) {
pw.println("got a RemoteException while dumping the service");
}
@@ -9990,7 +7647,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pw.println(" ");
pw.println("Receiver Resolver Table:");
- mReceiverResolver.dump(pw, null, " ", null);
+ mReceiverResolver.dump(pw, null, " ", null, false);
needSep = true;
}
@@ -10126,12 +7783,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mServiceConnections.size() > 0) {
if (needSep) pw.println(" ");
pw.println(" Connection bindings to services:");
- Iterator<ConnectionRecord> it
+ Iterator<ArrayList<ConnectionRecord>> it
= mServiceConnections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord r = it.next();
- pw.print(" * "); pw.println(r);
- r.dump(pw, " ");
+ ArrayList<ConnectionRecord> r = it.next();
+ for (int i=0; i<r.size(); i++) {
+ pw.print(" * "); pw.println(r.get(i));
+ r.get(i).dump(pw, " ");
+ }
}
needSep = true;
}
@@ -10148,10 +7807,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mProvidersByClass.size() > 0) {
if (needSep) pw.println(" ");
pw.println(" Published content providers (by class):");
- Iterator it = mProvidersByClass.entrySet().iterator();
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByClass.entrySet().iterator();
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
pw.print(" * "); pw.println(r);
r.dump(pw, " ");
}
@@ -10161,10 +7821,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mProvidersByName.size() > 0) {
pw.println(" ");
pw.println(" Authority to provider mappings:");
- Iterator it = mProvidersByName.entrySet().iterator();
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByName.entrySet().iterator();
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
pw.print(" "); pw.print(e.getKey()); pw.print(": ");
pw.println(r);
}
@@ -10231,7 +7892,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
String prefix, String label, boolean complete) {
TaskRecord lastTask = null;
for (int i=list.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)list.get(i);
+ ActivityRecord r = (ActivityRecord)list.get(i);
final boolean full = complete || !r.inHistory;
if (lastTask != r.task) {
lastTask = r.task;
@@ -10261,62 +7922,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private static final int dumpProcessList(PrintWriter pw,
ActivityManagerService service, List list,
- String prefix, String normalLabel, String persistentLabel,
- boolean inclOomAdj) {
+ String prefix, String normalLabel, String persistentLabel) {
int numPers = 0;
- for (int i=list.size()-1; i>=0; i--) {
+ final int N = list.size()-1;
+ for (int i=N; i>=0; i--) {
ProcessRecord r = (ProcessRecord)list.get(i);
- if (false) {
- pw.println(prefix + (r.persistent ? persistentLabel : normalLabel)
- + " #" + i + ":");
- r.dump(pw, prefix + " ");
- } else if (inclOomAdj) {
- String oomAdj;
- if (r.setAdj >= EMPTY_APP_ADJ) {
- oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ);
- } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) {
- oomAdj = buildOomTag("bak", " ", r.setAdj, HIDDEN_APP_MIN_ADJ);
- } else if (r.setAdj >= HOME_APP_ADJ) {
- oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ);
- } else if (r.setAdj >= SECONDARY_SERVER_ADJ) {
- oomAdj = buildOomTag("svc", " ", r.setAdj, SECONDARY_SERVER_ADJ);
- } else if (r.setAdj >= BACKUP_APP_ADJ) {
- oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ);
- } else if (r.setAdj >= VISIBLE_APP_ADJ) {
- oomAdj = buildOomTag("vis ", null, r.setAdj, VISIBLE_APP_ADJ);
- } else if (r.setAdj >= FOREGROUND_APP_ADJ) {
- oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ);
- } else if (r.setAdj >= CORE_SERVER_ADJ) {
- oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ);
- } else if (r.setAdj >= SYSTEM_ADJ) {
- oomAdj = buildOomTag("sys ", null, r.setAdj, SYSTEM_ADJ);
- } else {
- oomAdj = Integer.toString(r.setAdj);
- }
- String schedGroup;
- switch (r.setSchedGroup) {
- case Process.THREAD_GROUP_BG_NONINTERACTIVE:
- schedGroup = "B";
- break;
- case Process.THREAD_GROUP_DEFAULT:
- schedGroup = "F";
- break;
- default:
- schedGroup = Integer.toString(r.setSchedGroup);
- break;
- }
- pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
- prefix, (r.persistent ? persistentLabel : normalLabel),
- i, oomAdj, schedGroup, r.toShortString(), r.adjType));
- if (r.adjSource != null || r.adjTarget != null) {
- pw.println(prefix + " " + r.adjTarget
- + "<=" + r.adjSource);
- }
- } else {
- pw.println(String.format("%s%s #%2d: %s",
- prefix, (r.persistent ? persistentLabel : normalLabel),
- i, r.toString()));
- }
+ pw.println(String.format("%s%s #%2d: %s",
+ prefix, (r.persistent ? persistentLabel : normalLabel),
+ i, r.toString()));
if (r.persistent) {
numPers++;
}
@@ -10324,6 +7937,132 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return numPers;
}
+ private static final void dumpProcessOomList(PrintWriter pw,
+ ActivityManagerService service, List<ProcessRecord> list,
+ String prefix, String normalLabel, String persistentLabel,
+ boolean inclDetails) {
+
+ final long curRealtime = SystemClock.elapsedRealtime();
+ final long realtimeSince = curRealtime - service.mLastPowerCheckRealtime;
+ final long curUptime = SystemClock.uptimeMillis();
+ final long uptimeSince = curUptime - service.mLastPowerCheckUptime;
+
+ final int N = list.size()-1;
+ for (int i=N; i>=0; i--) {
+ ProcessRecord r = list.get(i);
+ String oomAdj;
+ if (r.setAdj >= EMPTY_APP_ADJ) {
+ oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ);
+ } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) {
+ oomAdj = buildOomTag("bak", " ", r.setAdj, HIDDEN_APP_MIN_ADJ);
+ } else if (r.setAdj >= HOME_APP_ADJ) {
+ oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ);
+ } else if (r.setAdj >= SECONDARY_SERVER_ADJ) {
+ oomAdj = buildOomTag("svc", " ", r.setAdj, SECONDARY_SERVER_ADJ);
+ } else if (r.setAdj >= BACKUP_APP_ADJ) {
+ oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ);
+ } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) {
+ oomAdj = buildOomTag("hvy ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ);
+ } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) {
+ oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ);
+ } else if (r.setAdj >= VISIBLE_APP_ADJ) {
+ oomAdj = buildOomTag("vis ", null, r.setAdj, VISIBLE_APP_ADJ);
+ } else if (r.setAdj >= FOREGROUND_APP_ADJ) {
+ oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ);
+ } else if (r.setAdj >= CORE_SERVER_ADJ) {
+ oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ);
+ } else if (r.setAdj >= SYSTEM_ADJ) {
+ oomAdj = buildOomTag("sys ", null, r.setAdj, SYSTEM_ADJ);
+ } else {
+ oomAdj = Integer.toString(r.setAdj);
+ }
+ String schedGroup;
+ switch (r.setSchedGroup) {
+ case Process.THREAD_GROUP_BG_NONINTERACTIVE:
+ schedGroup = "B";
+ break;
+ case Process.THREAD_GROUP_DEFAULT:
+ schedGroup = "F";
+ break;
+ default:
+ schedGroup = Integer.toString(r.setSchedGroup);
+ break;
+ }
+ pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
+ prefix, (r.persistent ? persistentLabel : normalLabel),
+ N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
+ if (r.adjSource != null || r.adjTarget != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ if (r.adjTarget instanceof ComponentName) {
+ pw.print(((ComponentName)r.adjTarget).flattenToShortString());
+ } else if (r.adjTarget != null) {
+ pw.print(r.adjTarget.toString());
+ } else {
+ pw.print("{null}");
+ }
+ pw.print("<=");
+ if (r.adjSource instanceof ProcessRecord) {
+ pw.print("Proc{");
+ pw.print(((ProcessRecord)r.adjSource).toShortString());
+ pw.println("}");
+ } else if (r.adjSource != null) {
+ pw.println(r.adjSource.toString());
+ } else {
+ pw.println("{null}");
+ }
+ }
+ if (inclDetails) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("oom: max="); pw.print(r.maxAdj);
+ pw.print(" hidden="); pw.print(r.hiddenAdj);
+ pw.print(" curRaw="); pw.print(r.curRawAdj);
+ pw.print(" setRaw="); pw.print(r.setRawAdj);
+ pw.print(" cur="); pw.print(r.curAdj);
+ pw.print(" set="); pw.println(r.setAdj);
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("keeping="); pw.print(r.keeping);
+ pw.print(" hidden="); pw.print(r.hidden);
+ pw.print(" empty="); pw.println(r.empty);
+
+ if (!r.keeping) {
+ if (r.lastWakeTime != 0) {
+ long wtime;
+ BatteryStatsImpl stats = service.mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ wtime = stats.getProcessWakeTime(r.info.uid,
+ r.pid, curRealtime);
+ }
+ long timeUsed = wtime - r.lastWakeTime;
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("keep awake over ");
+ TimeUtils.formatDuration(realtimeSince, pw);
+ pw.print(" used ");
+ TimeUtils.formatDuration(timeUsed, pw);
+ pw.print(" (");
+ pw.print((timeUsed*100)/realtimeSince);
+ pw.println("%)");
+ }
+ if (r.lastCpuTime != 0) {
+ long timeUsed = r.curCpuTime - r.lastCpuTime;
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("run cpu over ");
+ TimeUtils.formatDuration(uptimeSince, pw);
+ pw.print(" used ");
+ TimeUtils.formatDuration(timeUsed, pw);
+ pw.print(" (");
+ pw.print((timeUsed*100)/uptimeSince);
+ pw.println("%)");
+ }
+ }
+ }
+ }
+ }
+
static final void dumpApplicationMemoryUsage(FileDescriptor fd,
PrintWriter pw, List list, String prefix, String[] args) {
final boolean isCheckinRequest = scanArgs(args, "--checkin");
@@ -10374,22 +8113,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return false;
}
- private final int indexOfTokenLocked(IBinder token) {
- int count = mHistory.size();
-
- // convert the token to an entry in the history.
- int index = -1;
- for (int i=count-1; i>=0; i--) {
- Object o = mHistory.get(i);
- if (o == token) {
- index = i;
- break;
- }
- }
-
- return index;
- }
-
private final void killServicesLocked(ProcessRecord app,
boolean allowRestart) {
// Report disconnected services.
@@ -10397,22 +8120,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// XXX we are letting the client link to the service for
// death notifications.
if (app.services.size() > 0) {
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord r = (ServiceRecord)it.next();
+ ServiceRecord r = it.next();
if (r.connections.size() > 0) {
- Iterator<ConnectionRecord> jt
+ Iterator<ArrayList<ConnectionRecord>> jt
= r.connections.values().iterator();
while (jt.hasNext()) {
- ConnectionRecord c = jt.next();
- if (c.binding.client != app) {
- try {
- //c.conn.connected(r.className, null);
- } catch (Exception e) {
- // todo: this should be asynchronous!
- Slog.w(TAG, "Exception thrown disconnected servce "
- + r.shortName
- + " from app " + app.processName, e);
+ ArrayList<ConnectionRecord> cl = jt.next();
+ for (int i=0; i<cl.size(); i++) {
+ ConnectionRecord c = cl.get(i);
+ if (c.binding.client != app) {
+ try {
+ //c.conn.connected(r.className, null);
+ } catch (Exception e) {
+ // todo: this should be asynchronous!
+ Slog.w(TAG, "Exception thrown disconnected servce "
+ + r.shortName
+ + " from app " + app.processName, e);
+ }
}
}
}
@@ -10434,15 +8160,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (app.services.size() != 0) {
// Any services running in the application need to be placed
// back in the pending list.
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord sr = (ServiceRecord)it.next();
+ ServiceRecord sr = it.next();
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
sr.app = null;
sr.executeNesting = 0;
- mStoppingServices.remove(sr);
+ if (mStoppingServices.remove(sr)) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+ }
boolean hasClients = sr.bindings.size() > 0;
if (hasClients) {
@@ -10495,6 +8223,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ServiceRecord sr = mStoppingServices.get(i);
if (sr.app == app) {
mStoppingServices.remove(i);
+ if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
}
}
@@ -10575,9 +8304,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Remove published content providers.
if (!app.pubProviders.isEmpty()) {
- Iterator it = app.pubProviders.values().iterator();
+ Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
while (it.hasNext()) {
- ContentProviderRecord cpr = (ContentProviderRecord)it.next();
+ ContentProviderRecord cpr = it.next();
cpr.provider = null;
cpr.app = null;
@@ -10669,6 +8398,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_PROCESSES) Slog.v(TAG,
"Removing non-persistent process during cleanup: " + app);
mProcessNames.remove(app.processName, app.info.uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
@@ -10681,6 +8414,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
restart = true;
}
}
+ if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
+ "Clean-up removing on hold: " + app);
mProcessesOnHold.remove(app);
if (app == mHomeProcess) {
@@ -10710,8 +8445,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int NL = mLaunchingProviders.size();
boolean restart = false;
for (int i=0; i<NL; i++) {
- ContentProviderRecord cpr = (ContentProviderRecord)
- mLaunchingProviders.get(i);
+ ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.launchingApp == app) {
if (!alwaysBad && !app.bad) {
restart = true;
@@ -10755,11 +8489,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.app != null && r.app.persistent) {
info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
}
- for (ConnectionRecord conn : r.connections.values()) {
- if (conn.clientLabel != 0) {
- info.clientPackage = conn.binding.client.info.packageName;
- info.clientLabel = conn.clientLabel;
- break;
+
+ for (ArrayList<ConnectionRecord> connl : r.connections.values()) {
+ for (int i=0; i<connl.size(); i++) {
+ ConnectionRecord conn = connl.get(i);
+ if (conn.clientLabel != 0) {
+ info.clientPackage = conn.binding.client.info.packageName;
+ info.clientLabel = conn.clientLabel;
+ return info;
+ }
}
}
return info;
@@ -10794,9 +8532,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
ServiceRecord r = mServices.get(name);
if (r != null) {
- for (ConnectionRecord conn : r.connections.values()) {
- if (conn.clientIntent != null) {
- return conn.clientIntent;
+ for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
+ for (int i=0; i<conn.size(); i++) {
+ if (conn.get(i).clientIntent != null) {
+ return conn.get(i).clientIntent;
+ }
}
}
}
@@ -10834,7 +8574,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r == null) {
try {
ResolveInfo rInfo =
- ActivityThread.getPackageManager().resolveService(
+ AppGlobals.getPackageManager().resolveService(
service, resolvedType, 0);
ServiceInfo sInfo =
rInfo != null ? rInfo.serviceInfo : null;
@@ -10891,7 +8631,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r == null) {
try {
ResolveInfo rInfo =
- ActivityThread.getPackageManager().resolveService(
+ AppGlobals.getPackageManager().resolveService(
service, resolvedType, STOCK_PM_FLAGS);
ServiceInfo sInfo =
rInfo != null ? rInfo.serviceInfo : null;
@@ -10949,7 +8689,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return null;
}
- private final void bumpServiceExecutingLocked(ServiceRecord r) {
+ private final void bumpServiceExecutingLocked(ServiceRecord r, String why) {
+ if (DEBUG_SERVICE) Log.v(TAG, ">>> EXECUTING "
+ + why + " of " + r + " in app " + r.app);
+ else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING "
+ + why + " of " + r.shortName);
long now = SystemClock.uptimeMillis();
if (r.executeNesting == 0 && r.app != null) {
if (r.app.executingServices.size() == 0) {
@@ -10970,19 +8714,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return;
}
- int i = 0;
- while (i < N) {
+ while (r.pendingStarts.size() > 0) {
try {
- ServiceRecord.StartItem si = r.pendingStarts.get(i);
- if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: "
- + r.name + " " + r.intent + " args=" + si.intent);
- if (si.intent == null && N > 1) {
+ ServiceRecord.StartItem si = r.pendingStarts.remove(0);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: "
+ + r + " " + r.intent + " args=" + si.intent);
+ if (si.intent == null) {
// If somehow we got a dummy start at the front, then
// just drop it here.
- i++;
continue;
}
- bumpServiceExecutingLocked(r);
+ si.deliveredTime = SystemClock.uptimeMillis();
+ r.deliveredStarts.add(si);
+ si.deliveryCount++;
+ if (si.targetPermissionUid >= 0) {
+ grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
+ r.packageName, si.intent, si.getUriPermissionsLocked());
+ }
+ bumpServiceExecutingLocked(r, "start");
if (!oomAdjusted) {
oomAdjusted = true;
updateOomAdjLocked(r.app);
@@ -10995,27 +8744,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
flags |= Service.START_FLAG_REDELIVERY;
}
r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
- si.deliveredTime = SystemClock.uptimeMillis();
- r.deliveredStarts.add(si);
- si.deliveryCount++;
- i++;
} catch (RemoteException e) {
// Remote process gone... we'll let the normal cleanup take
// care of this.
+ if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r);
break;
} catch (Exception e) {
Slog.w(TAG, "Unexpected exception", e);
break;
}
}
- if (i == N) {
- r.pendingStarts.clear();
- } else {
- while (i > 0) {
- i--;
- r.pendingStarts.remove(i);
- }
- }
}
private final boolean requestServiceBindingLocked(ServiceRecord r,
@@ -11026,9 +8764,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
- bumpServiceExecutingLocked(r);
- if (DEBUG_SERVICE) Slog.v(TAG, "Connecting binding " + i
- + ": shouldUnbind=" + i.hasBound);
+ bumpServiceExecutingLocked(r, "bind");
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
if (!rebind) {
i.requested = true;
@@ -11036,6 +8772,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
i.hasBound = true;
i.doRebind = false;
} catch (RemoteException e) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
return false;
}
}
@@ -11062,13 +8799,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
app.services.add(r);
- bumpServiceExecutingLocked(r);
+ bumpServiceExecutingLocked(r, "create");
updateLruProcessLocked(app, true, true);
boolean created = false;
try {
- if (DEBUG_SERVICE) Slog.v(TAG, "Scheduling start service: "
- + r.name + " " + r.intent);
mStringBuilder.setLength(0);
r.intent.getIntent().toShortString(mStringBuilder, false, true);
EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
@@ -11098,7 +8833,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.lastStartId < 1) {
r.lastStartId = 1;
}
- r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, null));
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
}
sendServiceArgsLocked(r, true);
@@ -11118,6 +8853,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (N > 0) {
for (int i=N-1; i>=0; i--) {
ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+ si.removeUriPermissionsLocked();
if (si.intent == null) {
// We'll generate this again if needed.
} else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
@@ -11227,8 +8963,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return true;
}
- if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up service " + r.name
- + " " + r.intent);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
// We are now bringing the service up, so no longer in the
// restarting state.
@@ -11279,27 +9014,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (!force) {
// XXX should probably keep a count of the number of auto-create
// connections directly in the service.
- Iterator<ConnectionRecord> it = r.connections.values().iterator();
+ Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord cr = it.next();
- if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
- return;
+ ArrayList<ConnectionRecord> cr = it.next();
+ for (int i=0; i<cr.size(); i++) {
+ if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+ return;
+ }
}
}
}
// Report to all of the connections that the service is no longer
// available.
- Iterator<ConnectionRecord> it = r.connections.values().iterator();
+ Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord c = it.next();
- try {
- // todo: shouldn't be a synchronous call!
- c.conn.connected(r.name, null);
- } catch (Exception e) {
- Slog.w(TAG, "Failure disconnecting service " + r.name +
- " to connection " + c.conn.asBinder() +
- " (in " + c.binding.client.processName + ")", e);
+ ArrayList<ConnectionRecord> c = it.next();
+ for (int i=0; i<c.size(); i++) {
+ try {
+ c.get(i).conn.connected(r.name, null);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure disconnecting service " + r.name +
+ " to connection " + c.get(i).conn.asBinder() +
+ " (in " + c.get(i).binding.client.processName + ")", e);
+ }
}
}
}
@@ -11313,7 +9051,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ ": hasBound=" + ibr.hasBound);
if (r.app != null && r.app.thread != null && ibr.hasBound) {
try {
- bumpServiceExecutingLocked(r);
+ bumpServiceExecutingLocked(r, "bring down unbind");
updateOomAdjLocked(r.app);
ibr.hasBound = false;
r.app.thread.scheduleUnbindService(r,
@@ -11327,15 +9065,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down service " + r.name
- + " " + r.intent);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
System.identityHashCode(r), r.shortName,
(r.app != null) ? r.app.pid : -1);
mServices.remove(r.name);
mServicesByIntent.remove(r.intent);
- if (localLOGV) Slog.v(TAG, "BRING DOWN SERVICE: " + r.shortName);
r.totalRestartCount = 0;
unscheduleServiceRestartLocked(r);
@@ -11344,8 +9080,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
for (int i=0; i<N; i++) {
if (mPendingServices.get(i) == r) {
mPendingServices.remove(i);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Removed pending service: " + r.shortName);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r);
i--;
N--;
}
@@ -11357,7 +9092,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.foregroundNoti = null;
// Clear start entries.
- r.deliveredStarts.clear();
+ r.clearDeliveredStartsLocked();
r.pendingStarts.clear();
if (r.app != null) {
@@ -11367,9 +9102,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.app.services.remove(r);
if (r.app.thread != null) {
try {
- if (DEBUG_SERVICE) Slog.v(TAG,
- "Stopping service: " + r.shortName);
- bumpServiceExecutingLocked(r);
+ bumpServiceExecutingLocked(r, "stop");
mStoppingServices.add(r);
updateOomAdjLocked(r.app);
r.app.thread.scheduleStopService(r);
@@ -11381,11 +9114,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
updateServiceForegroundLocked(r.app, false);
} else {
if (DEBUG_SERVICE) Slog.v(
- TAG, "Removed service that has no process: " + r.shortName);
+ TAG, "Removed service that has no process: " + r);
}
} else {
if (DEBUG_SERVICE) Slog.v(
- TAG, "Removed service that is not running: " + r.shortName);
+ TAG, "Removed service that is not running: " + r);
}
}
@@ -11417,9 +9150,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
? res.permission : "private to package");
}
ServiceRecord r = res.record;
+ int targetPermissionUid = checkGrantUriPermissionFromIntentLocked(
+ callingUid, r.packageName, service);
if (unscheduleServiceRestartLocked(r)) {
- if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: "
- + r.shortName);
+ if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
}
r.startRequested = true;
r.callStart = false;
@@ -11427,7 +9161,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.lastStartId < 1) {
r.lastStartId = 1;
}
- r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, service));
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+ service, targetPermissionUid));
r.lastActivity = SystemClock.uptimeMillis();
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
@@ -11552,7 +9287,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
if (si != null) {
while (r.deliveredStarts.size() > 0) {
- if (r.deliveredStarts.remove(0) == si) {
+ ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
+ cur.removeUriPermissionsLocked();
+ if (cur == si) {
break;
}
}
@@ -11628,7 +9365,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
boolean anyForeground = false;
- for (ServiceRecord sr : (HashSet<ServiceRecord>)proc.services) {
+ for (ServiceRecord sr : proc.services) {
if (sr.isForeground) {
anyForeground = true;
break;
@@ -11662,14 +9399,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ ") when binding service " + service);
}
- HistoryRecord activity = null;
+ ActivityRecord activity = null;
if (token != null) {
- int aindex = indexOfTokenLocked(token);
+ int aindex = mMainStack.indexOfTokenLocked(token);
if (aindex < 0) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
- activity = (HistoryRecord)mHistory.get(aindex);
+ activity = (ActivityRecord)mMainStack.mHistory.get(aindex);
}
int clientLabel = 0;
@@ -11710,7 +9447,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (unscheduleServiceRestartLocked(s)) {
if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
- + s.shortName);
+ + s);
}
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
@@ -11718,7 +9455,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
- s.connections.put(binder, c);
+ ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+ if (clist == null) {
+ clist = new ArrayList<ConnectionRecord>();
+ s.connections.put(binder, clist);
+ }
+ clist.add(c);
b.connections.add(c);
if (activity != null) {
if (activity.connections == null) {
@@ -11727,7 +9469,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
activity.connections.add(c);
}
b.client.connections.add(c);
- mServiceConnections.put(binder, c);
+ clist = mServiceConnections.get(binder);
+ if (clist == null) {
+ clist = new ArrayList<ConnectionRecord>();
+ mServiceConnections.put(binder, clist);
+ }
+ clist.add(c);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
@@ -11773,12 +9520,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return 1;
}
- private void removeConnectionLocked(
- ConnectionRecord c, ProcessRecord skipApp, HistoryRecord skipAct) {
+ void removeConnectionLocked(
+ ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
- s.connections.remove(binder);
+ ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+ if (clist != null) {
+ clist.remove(c);
+ if (clist.size() == 0) {
+ s.connections.remove(binder);
+ }
+ }
b.connections.remove(c);
if (c.activity != null && c.activity != skipAct) {
if (c.activity.connections != null) {
@@ -11788,7 +9541,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (b.client != skipApp) {
b.client.connections.remove(c);
}
- mServiceConnections.remove(binder);
+ clist = mServiceConnections.get(binder);
+ if (clist != null) {
+ clist.remove(c);
+ if (clist.size() == 0) {
+ mServiceConnections.remove(binder);
+ }
+ }
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
@@ -11799,7 +9558,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
&& b.intent.hasBound) {
try {
- bumpServiceExecutingLocked(s);
+ bumpServiceExecutingLocked(s, "unbind");
updateOomAdjLocked(s.app);
b.intent.hasBound = false;
// Assume the client doesn't want to know about a rebind;
@@ -11821,8 +9580,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
IBinder binder = connection.asBinder();
if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder);
- ConnectionRecord r = mServiceConnections.get(binder);
- if (r == null) {
+ ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+ if (clist == null) {
Slog.w(TAG, "Unbind failed: could not find connection for "
+ connection.asBinder());
return false;
@@ -11830,11 +9589,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
- removeConnectionLocked(r, null, null);
+ while (clist.size() > 0) {
+ ConnectionRecord r = clist.get(0);
+ removeConnectionLocked(r, null, null);
- if (r.binding.service.app != null) {
- // This could have made the service less important.
- updateOomAdjLocked(r.binding.service.app);
+ if (r.binding.service.app != null) {
+ // This could have made the service less important.
+ updateOomAdjLocked(r.binding.service.app);
+ }
}
Binder.restoreCallingIdentity(origId);
@@ -11857,7 +9619,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
- if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING SERVICE " + r.name
+ if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
+ " " + intent + ": " + service);
if (r != null) {
Intent.FilterComparison filter
@@ -11868,26 +9630,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
b.requested = true;
b.received = true;
if (r.connections.size() > 0) {
- Iterator<ConnectionRecord> it
+ Iterator<ArrayList<ConnectionRecord>> it
= r.connections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord c = it.next();
- if (!filter.equals(c.binding.intent.intent)) {
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Not publishing to: " + c);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Bound intent: " + c.binding.intent.intent);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Published intent: " + intent);
- continue;
- }
- if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
- try {
- c.conn.connected(r.name, service);
- } catch (Exception e) {
- Slog.w(TAG, "Failure sending service " + r.name +
- " to connection " + c.conn.asBinder() +
- " (in " + c.binding.client.processName + ")", e);
+ ArrayList<ConnectionRecord> clist = it.next();
+ for (int i=0; i<clist.size(); i++) {
+ ConnectionRecord c = clist.get(i);
+ if (!filter.equals(c.binding.intent.intent)) {
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Not publishing to: " + c);
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Bound intent: " + c.binding.intent.intent);
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Published intent: " + intent);
+ continue;
+ }
+ if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
+ try {
+ c.conn.connected(r.name, service);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure sending service " + r.name +
+ " to connection " + c.conn.asBinder() +
+ " (in " + c.binding.client.processName + ")", e);
+ }
}
}
}
@@ -11948,9 +9713,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ServiceRecord r = (ServiceRecord)token;
boolean inStopping = mStoppingServices.contains(token);
if (r != null) {
- if (DEBUG_SERVICE) Slog.v(TAG, "DONE EXECUTING SERVICE " + r.name
- + ": nesting=" + r.executeNesting
- + ", inStopping=" + inStopping);
if (r != token) {
Slog.w(TAG, "Done executing service " + r.name
+ " with incorrect token: given " + token
@@ -12007,20 +9769,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
} else {
- Slog.w(TAG, "Done executing unknown service " + r.name
- + " with token " + token);
+ Slog.w(TAG, "Done executing unknown service from pid "
+ + Binder.getCallingPid());
}
}
}
public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
+ + ": nesting=" + r.executeNesting
+ + ", inStopping=" + inStopping + ", app=" + r.app);
+ else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName);
r.executeNesting--;
if (r.executeNesting <= 0 && r.app != null) {
+ if (DEBUG_SERVICE) Slog.v(TAG,
+ "Nesting at 0 of " + r.shortName);
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
+ if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
+ "No more executingServices of " + r.shortName);
mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
}
if (inStopping) {
+ if (DEBUG_SERVICE) Slog.v(TAG,
+ "doneExecuting remove stopping " + r);
mStoppingServices.remove(r);
}
updateOomAdjLocked(r.app);
@@ -12130,20 +9902,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
return;
}
+ }
- long oldIdent = Binder.clearCallingIdentity();
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentConnected(agentPackageName, agent);
- } catch (RemoteException e) {
- // can't happen; the backup manager service is local
- } catch (Exception e) {
- Slog.w(TAG, "Exception trying to deliver BackupAgent binding: ");
- e.printStackTrace();
- } finally {
- Binder.restoreCallingIdentity(oldIdent);
- }
+ long oldIdent = Binder.clearCallingIdentity();
+ try {
+ IBackupManager bm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ bm.agentConnected(agentPackageName, agent);
+ } catch (RemoteException e) {
+ // can't happen; the backup manager service is local
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception trying to deliver BackupAgent binding: ");
+ e.printStackTrace();
+ } finally {
+ Binder.restoreCallingIdentity(oldIdent);
}
}
@@ -12455,7 +10227,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Always okay.
} else if (callerApp == null || !callerApp.persistent) {
try {
- if (ActivityThread.getPackageManager().isProtectedBroadcast(
+ if (AppGlobals.getPackageManager().isProtectedBroadcast(
intent.getAction())) {
String msg = "Permission Denial: not allowed to send broadcast "
+ intent.getAction() + " from pid="
@@ -12514,7 +10286,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
try {
if (intent.getComponent() != null) {
// Broadcast is going to one specific receiver class...
- ActivityInfo ai = ActivityThread.getPackageManager().
+ ActivityInfo ai = AppGlobals.getPackageManager().
getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS);
if (ai != null) {
receivers = new ArrayList();
@@ -12527,7 +10299,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
receivers =
- ActivityThread.getPackageManager().queryIntentReceivers(
+ AppGlobals.getPackageManager().queryIntentReceivers(
intent, resolvedType, STOCK_PM_FLAGS);
}
registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);
@@ -12661,7 +10433,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
boolean replaced = false;
if (replacePending) {
- for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) {
+ for (int i=mOrderedBroadcasts.size()-1; i>0; i--) {
if (intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
if (DEBUG_BROADCAST) Slog.v(TAG,
"***** DROPPING ORDERED: " + intent);
@@ -12680,35 +10452,41 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return BROADCAST_SUCCESS;
}
- public final int broadcastIntent(IApplicationThread caller,
- Intent intent, String resolvedType, IIntentReceiver resultTo,
- int resultCode, String resultData, Bundle map,
- String requiredPermission, boolean serialized, boolean sticky) {
+ final Intent verifyBroadcastLocked(Intent intent) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- synchronized(this) {
- int flags = intent.getFlags();
-
- if (!mSystemReady) {
- // if the caller really truly claims to know what they're doing, go
- // ahead and allow the broadcast without launching any receivers
- if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
- intent = new Intent(intent);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0){
- Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
- + " before boot completion");
- throw new IllegalStateException("Cannot broadcast before boot completed");
- }
- }
-
- if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
- throw new IllegalArgumentException(
- "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ int flags = intent.getFlags();
+
+ if (!mProcessesReady) {
+ // if the caller really truly claims to know what they're doing, go
+ // ahead and allow the broadcast without launching any receivers
+ if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
+ intent = new Intent(intent);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+ Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
+ + " before boot completion");
+ throw new IllegalStateException("Cannot broadcast before boot completed");
}
+ }
+
+ if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ throw new IllegalArgumentException(
+ "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ }
+
+ return intent;
+ }
+
+ public final int broadcastIntent(IApplicationThread caller,
+ Intent intent, String resolvedType, IIntentReceiver resultTo,
+ int resultCode, String resultData, Bundle map,
+ String requiredPermission, boolean serialized, boolean sticky) {
+ synchronized(this) {
+ intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
@@ -12729,6 +10507,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int resultCode, String resultData, Bundle map,
String requiredPermission, boolean serialized, boolean sticky) {
synchronized(this) {
+ intent = verifyBroadcastLocked(intent);
+
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
resultTo, resultCode, resultData, map, requiredPermission,
@@ -12848,7 +10628,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Binder.restoreCallingIdentity(origId);
}
- private final void logBroadcastReceiverDiscard(BroadcastRecord r) {
+ private final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
if (r.nextReceiver > 0) {
Object curReceiver = r.receivers.get(r.nextReceiver-1);
if (curReceiver instanceof BroadcastFilter) {
@@ -12876,72 +10656,115 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void broadcastTimeout() {
- ProcessRecord app = null;
- String anrMessage = null;
+ private final void setBroadcastTimeoutLocked(long timeoutTime) {
+ if (! mPendingBroadcastTimeoutMessage) {
+ Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
+ mHandler.sendMessageAtTime(msg, timeoutTime);
+ mPendingBroadcastTimeoutMessage = true;
+ }
+ }
- synchronized (this) {
- if (mOrderedBroadcasts.size() == 0) {
+ private final void cancelBroadcastTimeoutLocked() {
+ if (mPendingBroadcastTimeoutMessage) {
+ mHandler.removeMessages(BROADCAST_TIMEOUT_MSG);
+ mPendingBroadcastTimeoutMessage = false;
+ }
+ }
+
+ private final void broadcastTimeoutLocked(boolean fromMsg) {
+ if (fromMsg) {
+ mPendingBroadcastTimeoutMessage = false;
+ }
+
+ if (mOrderedBroadcasts.size() == 0) {
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ BroadcastRecord r = mOrderedBroadcasts.get(0);
+ if (fromMsg) {
+ if (mDidDexOpt) {
+ // Delay timeouts until dexopt finishes.
+ mDidDexOpt = false;
+ long timeoutTime = SystemClock.uptimeMillis() + BROADCAST_TIMEOUT;
+ setBroadcastTimeoutLocked(timeoutTime);
return;
}
- long now = SystemClock.uptimeMillis();
- BroadcastRecord r = mOrderedBroadcasts.get(0);
- if ((r.receiverTime+BROADCAST_TIMEOUT) > now) {
+ if (! mProcessesReady) {
+ // Only process broadcast timeouts if the system is ready. That way
+ // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
+ // to do heavy lifting for system up.
+ return;
+ }
+
+ long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
+ if (timeoutTime > now) {
+ // We can observe premature timeouts because we do not cancel and reset the
+ // broadcast timeout message after each receiver finishes. Instead, we set up
+ // an initial timeout then kick it down the road a little further as needed
+ // when it expires.
if (DEBUG_BROADCAST) Slog.v(TAG,
"Premature timeout @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
- + (r.receiverTime + BROADCAST_TIMEOUT));
- Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
- mHandler.sendMessageAtTime(msg, r.receiverTime+BROADCAST_TIMEOUT);
+ + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
return;
}
+ }
- Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver);
- r.receiverTime = now;
- r.anrCount++;
+ Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+ + ", started " + (now - r.receiverTime) + "ms ago");
+ r.receiverTime = now;
+ r.anrCount++;
- // Current receiver has passed its expiration date.
- if (r.nextReceiver <= 0) {
- Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
- return;
- }
+ // Current receiver has passed its expiration date.
+ if (r.nextReceiver <= 0) {
+ Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
+ return;
+ }
- Object curReceiver = r.receivers.get(r.nextReceiver-1);
- Slog.w(TAG, "Receiver during timeout: " + curReceiver);
- logBroadcastReceiverDiscard(r);
- if (curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter)curReceiver;
- if (bf.receiverList.pid != 0
- && bf.receiverList.pid != MY_PID) {
- synchronized (this.mPidsSelfLocked) {
- app = this.mPidsSelfLocked.get(
- bf.receiverList.pid);
- }
- }
- } else {
- app = r.curApp;
- }
-
- if (app != null) {
- anrMessage = "Broadcast of " + r.intent.toString();
- }
+ ProcessRecord app = null;
+ String anrMessage = null;
- if (mPendingBroadcast == r) {
- mPendingBroadcast = null;
+ Object curReceiver = r.receivers.get(r.nextReceiver-1);
+ Slog.w(TAG, "Receiver during timeout: " + curReceiver);
+ logBroadcastReceiverDiscardLocked(r);
+ if (curReceiver instanceof BroadcastFilter) {
+ BroadcastFilter bf = (BroadcastFilter)curReceiver;
+ if (bf.receiverList.pid != 0
+ && bf.receiverList.pid != MY_PID) {
+ synchronized (this.mPidsSelfLocked) {
+ app = this.mPidsSelfLocked.get(
+ bf.receiverList.pid);
+ }
}
-
- // Move on to the next receiver.
- finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, true);
- scheduleBroadcastsLocked();
+ } else {
+ app = r.curApp;
}
+ if (app != null) {
+ anrMessage = "Broadcast of " + r.intent.toString();
+ }
+
+ if (mPendingBroadcast == r) {
+ mPendingBroadcast = null;
+ }
+
+ // Move on to the next receiver.
+ finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, true);
+ scheduleBroadcastsLocked();
+
if (anrMessage != null) {
- appNotResponding(app, null, null, anrMessage);
+ // Post the ANR to the handler since we do not want to process ANRs while
+ // potentially holding our lock.
+ mHandler.post(new AppNotResponding(app, anrMessage));
}
}
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Process cur broadcast " + r + " for app " + app);
if (app.thread == null) {
throw new RemoteException();
}
@@ -12961,9 +10784,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ensurePackageDexOpt(r.intent.getComponent().getPackageName());
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
r.resultCode, r.resultData, r.resultExtras, r.ordered);
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
} finally {
if (!started) {
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Process cur broadcast " + r + ": NOT STARTED!");
r.receiver = null;
r.curApp = null;
app.curReceiver = null;
@@ -12972,9 +10799,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
- static void performReceive(ProcessRecord app, IIntentReceiver receiver,
+ static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky) throws RemoteException {
+ // Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null && app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
@@ -12985,7 +10813,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void deliverToRegisteredReceiver(BroadcastRecord r,
+ private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered) {
boolean skip = false;
if (filter.requiredPermission != null) {
@@ -13043,7 +10871,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Slog.i(TAG, "Delivering to " + filter
+ " (seq=" + seq + "): " + r);
}
- performReceive(filter.receiverList.app, filter.receiverList.receiver,
+ performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, r.ordered, r.initialSticky);
if (ordered) {
@@ -13100,7 +10928,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_BROADCAST) Slog.v(TAG,
"Delivering non-ordered to registered "
+ target + ": " + r);
- deliverToRegisteredReceiver(r, (BroadcastFilter)target, false);
+ deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast "
@@ -13128,6 +10956,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else {
Slog.w(TAG, "pending app " + mPendingBroadcast.curApp
+ " died before responding to broadcast");
+ mPendingBroadcast.state = BroadcastRecord.IDLE;
+ mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
@@ -13154,11 +10984,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// and continue to make progress.
//
// This is only done if the system is ready so that PRE_BOOT_COMPLETED
- // receivers don't get executed with with timeouts. They're intended for
+ // receivers don't get executed with timeouts. They're intended for
// one time heavy lifting after system upgrades and can take
// significant amounts of time.
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
- if (mSystemReady && r.dispatchTime > 0) {
+ if (mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*BROADCAST_TIMEOUT*numReceivers))) {
@@ -13170,7 +11000,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ " numReceivers=" + numReceivers
+ " nextReceiver=" + r.nextReceiver
+ " state=" + r.state);
- broadcastTimeout(); // forcibly finish this broadcast
+ broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
@@ -13194,7 +11024,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Slog.i(TAG, "Finishing broadcast " + r.intent.getAction()
+ " seq=" + seq + " app=" + r.callerApp);
}
- performReceive(r.callerApp, r.resultTo,
+ performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false);
} catch (RemoteException e) {
@@ -13203,7 +11033,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
- mHandler.removeMessages(BROADCAST_TIMEOUT_MSG);
+ cancelBroadcastTimeoutLocked();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
+ r);
@@ -13228,11 +11058,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast "
+ r);
+ }
+ if (! mPendingBroadcastTimeoutMessage) {
+ long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG,
- "Submitting BROADCAST_TIMEOUT_MSG for "
- + (r.receiverTime + BROADCAST_TIMEOUT));
- Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
- mHandler.sendMessageAtTime(msg, r.receiverTime+BROADCAST_TIMEOUT);
+ "Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
}
Object nextReceiver = r.receivers.get(recIdx);
@@ -13243,7 +11074,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_BROADCAST) Slog.v(TAG,
"Delivering ordered to registered "
+ filter + ": " + r);
- deliverToRegisteredReceiver(r, filter, r.ordered);
+ deliverToRegisteredReceiverLocked(r, filter, r.ordered);
if (r.receiver == null || !r.ordered) {
// The receiver has already finished, so schedule to
// process the next one.
@@ -13279,7 +11110,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.callingUid != Process.SYSTEM_UID &&
r.requiredPermission != null) {
try {
- perm = ActivityThread.getPackageManager().
+ perm = AppGlobals.getPackageManager().
checkPermission(r.requiredPermission,
info.activityInfo.applicationInfo.packageName);
} catch (RemoteException e) {
@@ -13297,10 +11128,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (r.curApp != null && r.curApp.crashing) {
// If the target process is crashing, just skip it.
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Skipping deliver ordered " + r + " to " + r.curApp
+ + ": process crashing");
skip = true;
}
if (skip) {
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Skipping delivery of ordered " + r + " for whatever reason");
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
@@ -13332,6 +11168,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// Not running -- get it started, to be executed when the app comes up.
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Need to start app " + targetProcess + " for broadcast " + r);
if ((r.curApp=startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
@@ -13344,7 +11182,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ info.activityInfo.applicationInfo.packageName + "/"
+ info.activityInfo.applicationInfo.uid + " for broadcast "
+ r.intent + ": process is bad");
- logBroadcastReceiverDiscard(r);
+ logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, true);
scheduleBroadcastsLocked();
@@ -13353,6 +11191,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
mPendingBroadcast = r;
+ mPendingBroadcastRecvIndex = recIdx;
}
}
@@ -13536,7 +11375,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* configuration.
*/
public boolean updateConfigurationLocked(Configuration values,
- HistoryRecord starting) {
+ ActivityRecord starting) {
int changes = 0;
boolean kept = true;
@@ -13605,18 +11444,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// If the configuration changed, and the caller is not already
// in the process of starting an activity, then find the top
// activity to check if its configuration needs to change.
- starting = topRunningActivityLocked(null);
+ starting = mMainStack.topRunningActivityLocked(null);
}
if (starting != null) {
- kept = ensureActivityConfigurationLocked(starting, changes);
+ kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
if (kept) {
// If this didn't result in the starting activity being
// destroyed, then we need to make sure at this point that all
// other activities are made visible.
if (DEBUG_SWITCH) Slog.i(TAG, "Config didn't destroy " + starting
+ ", ensuring others are correct.");
- ensureActivitiesVisibleLocked(starting, changes);
+ mMainStack.ensureActivitiesVisibleLocked(starting, changes);
}
}
@@ -13626,160 +11465,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return kept;
}
-
- private final boolean relaunchActivityLocked(HistoryRecord r,
- int changes, boolean andResume) {
- List<ResultInfo> results = null;
- List<Intent> newIntents = null;
- if (andResume) {
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
- + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
- EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
- : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
-
- r.startFreezingScreenLocked(r.app, 0);
-
- try {
- if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
- r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
- changes, !andResume, mConfiguration);
- // Note: don't need to call pauseIfSleepingLocked() here, because
- // the caller will only pass in 'andResume' if this activity is
- // currently resumed, which implies we aren't sleeping.
- } catch (RemoteException e) {
- return false;
- }
-
- if (andResume) {
- r.results = null;
- r.newIntents = null;
- reportResumedActivityLocked(r);
- }
-
- return true;
- }
-
- /**
- * Make sure the given activity matches the current configuration. Returns
- * false if the activity had to be destroyed. Returns true if the
- * configuration is the same, or the activity will remain running as-is
- * for whatever reason. Ensures the HistoryRecord is updated with the
- * correct configuration and all other bookkeeping is handled.
- */
- private final boolean ensureActivityConfigurationLocked(HistoryRecord r,
- int globalChanges) {
- if (mConfigWillChange) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Skipping config check (will change): " + r);
- return true;
- }
-
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Ensuring correct configuration: " + r);
-
- // Short circuit: if the two configurations are the exact same
- // object (the common case), then there is nothing to do.
- Configuration newConfig = mConfiguration;
- if (r.configuration == newConfig) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration unchanged in " + r);
- return true;
- }
-
- // We don't worry about activities that are finishing.
- if (r.finishing) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration doesn't matter in finishing " + r);
- r.stopFreezingScreenLocked(false);
- return true;
- }
-
- // Okay we now are going to make this activity have the new config.
- // But then we need to figure out how it needs to deal with that.
- Configuration oldConfig = r.configuration;
- r.configuration = newConfig;
-
- // If the activity isn't currently running, just leave the new
- // configuration and it will pick that up next time it starts.
- if (r.app == null || r.app.thread == null) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration doesn't matter not running " + r);
- r.stopFreezingScreenLocked(false);
- return true;
- }
-
- // If the activity isn't persistent, there is a chance we will
- // need to restart it.
- if (!r.persistent) {
-
- // Figure out what has changed between the two configurations.
- int changes = oldConfig.diff(newConfig);
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
- + Integer.toHexString(changes) + ", handles=0x"
- + Integer.toHexString(r.info.configChanges)
- + ", newConfig=" + newConfig);
- }
- if ((changes&(~r.info.configChanges)) != 0) {
- // Aha, the activity isn't handling the change, so DIE DIE DIE.
- r.configChangeFlags |= changes;
- r.startFreezingScreenLocked(r.app, globalChanges);
- if (r.app == null || r.app.thread == null) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is destroying non-running " + r);
- destroyActivityLocked(r, true);
- } else if (r.state == ActivityState.PAUSING) {
- // A little annoying: we are waiting for this activity to
- // finish pausing. Let's not do anything now, but just
- // flag that it needs to be restarted when done pausing.
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is skipping already pausing " + r);
- r.configDestroy = true;
- return true;
- } else if (r.state == ActivityState.RESUMED) {
- // Try to optimize this case: the configuration is changing
- // and we need to restart the top, resumed activity.
- // Instead of doing the normal handshaking, just say
- // "restart!".
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is restarting resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, true);
- r.configChangeFlags = 0;
- } else {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is restarting non-resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, false);
- r.configChangeFlags = 0;
- }
-
- // All done... tell the caller we weren't able to keep this
- // activity around.
- return false;
- }
- }
-
- // Default case: the activity can handle this new configuration, so
- // hand it over. Note that we don't need to give it the new
- // configuration, since we always send configuration changes to all
- // process when they happen so it can just use whatever configuration
- // it last got.
- if (r.app != null && r.app.thread != null) {
- try {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
- r.app.thread.scheduleActivityConfigurationChanged(r);
- } catch (RemoteException e) {
- // If process died, whatever.
- }
- }
- r.stopFreezingScreenLocked(false);
-
- return true;
- }
/**
* Save the locale. You must be inside a synchronized (this) block.
@@ -13826,6 +11511,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
app.adjType = "fixed";
app.adjSeq = mAdjSeq;
app.curRawAdj = app.maxAdj;
+ app.keeping = true;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
return (app.curAdj=app.maxAdj);
}
@@ -13833,6 +11519,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
app.adjSource = null;
app.adjTarget = null;
+ app.keeping = false;
app.empty = false;
app.hidden = false;
@@ -13851,11 +11538,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
adj = FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "instrumentation";
- } else if (app.persistentActivities > 0) {
- // Special persistent activities... shouldn't be used these days.
- adj = FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- app.adjType = "persistent";
} else if (app.curReceiver != null ||
(mPendingBroadcast != null && mPendingBroadcast.curApp == app)) {
// An app that is currently receiving a broadcast also
@@ -13871,15 +11553,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
app.adjType = "exec-service";
} else if (app.foregroundServices) {
// The user is aware of this app, so make it visible.
- adj = VISIBLE_APP_ADJ;
+ adj = PERCEPTIBLE_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "foreground-service";
} else if (app.forcingToForeground != null) {
// The user is aware of this app, so make it visible.
- adj = VISIBLE_APP_ADJ;
+ adj = PERCEPTIBLE_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "force-foreground";
app.adjSource = app.forcingToForeground;
+ } else if (app == mHeavyWeightProcess) {
+ // We don't want to kill the current heavy-weight process.
+ adj = HEAVY_WEIGHT_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.adjType = "heavy";
} else if (app == mHomeProcess) {
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
@@ -13894,7 +11581,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
app.adjType = "bg-activities";
N = app.activities.size();
for (int j=0; j<N; j++) {
- if (((HistoryRecord)app.activities.get(j)).visible) {
+ if (app.activities.get(j).visible) {
// This app has a visible activity!
app.hidden = false;
adj = VISIBLE_APP_ADJ;
@@ -13937,9 +11624,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long now = SystemClock.uptimeMillis();
// This process is more important if the top activity is
// bound to the service.
- Iterator jt = app.services.iterator();
+ Iterator<ServiceRecord> jt = app.services.iterator();
while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) {
- ServiceRecord s = (ServiceRecord)jt.next();
+ ServiceRecord s = jt.next();
if (s.startRequested) {
if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
@@ -13957,70 +11644,79 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (adj > SECONDARY_SERVER_ADJ) {
app.adjType = "started-bg-services";
}
+ // Don't kill this process because it is doing work; it
+ // has said it is doing work.
+ app.keeping = true;
}
if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- Iterator<ConnectionRecord> kt
+ Iterator<ArrayList<ConnectionRecord>> kt
= s.connections.values().iterator();
while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
- // XXX should compute this based on the max of
- // all connected clients.
- ConnectionRecord cr = kt.next();
- if (cr.binding.client == app) {
- // Binding to ourself is not interesting.
- continue;
- }
- if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
- ProcessRecord client = cr.binding.client;
- int myHiddenAdj = hiddenAdj;
- if (myHiddenAdj > client.hiddenAdj) {
- if (client.hiddenAdj > VISIBLE_APP_ADJ) {
- myHiddenAdj = client.hiddenAdj;
- } else {
- myHiddenAdj = VISIBLE_APP_ADJ;
- }
+ ArrayList<ConnectionRecord> clist = kt.next();
+ for (int i=0; i<clist.size() && adj > FOREGROUND_APP_ADJ; i++) {
+ // XXX should compute this based on the max of
+ // all connected clients.
+ ConnectionRecord cr = clist.get(i);
+ if (cr.binding.client == app) {
+ // Binding to ourself is not interesting.
+ continue;
}
- int clientAdj = computeOomAdjLocked(
- client, myHiddenAdj, TOP_APP, true);
- if (adj > clientAdj) {
- adj = clientAdj > VISIBLE_APP_ADJ
- ? clientAdj : VISIBLE_APP_ADJ;
- if (!client.hidden) {
- app.hidden = false;
+ if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+ ProcessRecord client = cr.binding.client;
+ int myHiddenAdj = hiddenAdj;
+ if (myHiddenAdj > client.hiddenAdj) {
+ if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
+ myHiddenAdj = client.hiddenAdj;
+ } else {
+ myHiddenAdj = VISIBLE_APP_ADJ;
+ }
+ }
+ int clientAdj = computeOomAdjLocked(
+ client, myHiddenAdj, TOP_APP, true);
+ if (adj > clientAdj) {
+ adj = clientAdj >= VISIBLE_APP_ADJ
+ ? clientAdj : VISIBLE_APP_ADJ;
+ if (!client.hidden) {
+ app.hidden = false;
+ }
+ if (client.keeping) {
+ app.keeping = true;
+ }
+ app.adjType = "service";
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE;
+ app.adjSource = cr.binding.client;
+ app.adjTarget = s.name;
}
+ if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+ if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ }
+ }
+ }
+ ActivityRecord a = cr.activity;
+ //if (a != null) {
+ // Slog.i(TAG, "Connection to " + a ": state=" + a.state);
+ //}
+ if (a != null && adj > FOREGROUND_APP_ADJ &&
+ (a.state == ActivityState.RESUMED
+ || a.state == ActivityState.PAUSING)) {
+ adj = FOREGROUND_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.hidden = false;
app.adjType = "service";
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE;
- app.adjSource = cr.binding.client;
- app.adjTarget = s.serviceInfo.name;
- }
- if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
- if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- }
+ app.adjSource = a;
+ app.adjTarget = s.name;
}
}
- HistoryRecord a = cr.activity;
- //if (a != null) {
- // Slog.i(TAG, "Connection to " + a ": state=" + a.state);
- //}
- if (a != null && adj > FOREGROUND_APP_ADJ &&
- (a.state == ActivityState.RESUMED
- || a.state == ActivityState.PAUSING)) {
- adj = FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- app.hidden = false;
- app.adjType = "service";
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo
- .REASON_SERVICE_IN_USE;
- app.adjSource = a;
- app.adjTarget = s.serviceInfo.name;
- }
}
}
}
- // Finally, f this process has active services running in it, we
+ // Finally, if this process has active services running in it, we
// would like to avoid killing it unless it would prevent the current
// application from running. By default we put the process in
// with the rest of the background processes; as we scan through
@@ -14034,10 +11730,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (app.pubProviders.size() != 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- Iterator jt = app.pubProviders.values().iterator();
+ Iterator<ContentProviderRecord> jt = app.pubProviders.values().iterator();
while (jt.hasNext() && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- ContentProviderRecord cpr = (ContentProviderRecord)jt.next();
+ ContentProviderRecord cpr = jt.next();
if (cpr.clients.size() != 0) {
Iterator<ProcessRecord> kt = cpr.clients.iterator();
while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
@@ -14062,11 +11758,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (!client.hidden) {
app.hidden = false;
}
+ if (client.keeping) {
+ app.keeping = true;
+ }
app.adjType = "provider";
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_PROVIDER_IN_USE;
app.adjSource = client;
- app.adjTarget = cpr.info.name;
+ app.adjTarget = cpr.name;
}
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -14081,8 +11780,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
adj = FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.hidden = false;
+ app.keeping = true;
app.adjType = "provider";
- app.adjTarget = cpr.info.name;
+ app.adjTarget = cpr.name;
}
}
}
@@ -14094,10 +11794,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
if (adj > app.maxAdj) {
adj = app.maxAdj;
- if (app.maxAdj <= VISIBLE_APP_ADJ) {
+ if (app.maxAdj <= PERCEPTIBLE_APP_ADJ) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
}
}
+ if (adj < HIDDEN_APP_MIN_ADJ) {
+ app.keeping = true;
+ }
app.curAdj = adj;
app.curSchedGroup = schedGroup;
@@ -14130,8 +11833,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private final boolean canGcNowLocked() {
return mParallelBroadcasts.size() == 0
&& mOrderedBroadcasts.size() == 0
- && (mSleeping || (mResumedActivity != null &&
- mResumedActivity.idle));
+ && (mSleeping || (mMainStack.mResumedActivity != null &&
+ mMainStack.mResumedActivity.idle));
}
/**
@@ -14146,7 +11849,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (canGcNowLocked()) {
while (mProcessesToGc.size() > 0) {
ProcessRecord proc = mProcessesToGc.remove(0);
- if (proc.curRawAdj > VISIBLE_APP_ADJ || proc.reportLowMemory) {
+ if (proc.curRawAdj > PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
<= SystemClock.uptimeMillis()) {
// To avoid spamming the system, we will GC processes one
@@ -14235,6 +11938,104 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ final void checkExcessivePowerUsageLocked(boolean doKills) {
+ updateCpuStatsNow();
+
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ boolean doWakeKills = doKills;
+ boolean doCpuKills = doKills;
+ if (mLastPowerCheckRealtime == 0) {
+ doWakeKills = false;
+ }
+ if (mLastPowerCheckUptime == 0) {
+ doCpuKills = false;
+ }
+ if (stats.isScreenOn()) {
+ doWakeKills = false;
+ }
+ final long curRealtime = SystemClock.elapsedRealtime();
+ final long realtimeSince = curRealtime - mLastPowerCheckRealtime;
+ final long curUptime = SystemClock.uptimeMillis();
+ final long uptimeSince = curUptime - mLastPowerCheckUptime;
+ mLastPowerCheckRealtime = curRealtime;
+ mLastPowerCheckUptime = curUptime;
+ if (realtimeSince < WAKE_LOCK_MIN_CHECK_DURATION) {
+ doWakeKills = false;
+ }
+ if (uptimeSince < CPU_MIN_CHECK_DURATION) {
+ doCpuKills = false;
+ }
+ int i = mLruProcesses.size();
+ while (i > 0) {
+ i--;
+ ProcessRecord app = mLruProcesses.get(i);
+ if (!app.keeping) {
+ long wtime;
+ synchronized (stats) {
+ wtime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, curRealtime);
+ }
+ long wtimeUsed = wtime - app.lastWakeTime;
+ long cputimeUsed = app.curCpuTime - app.lastCpuTime;
+ if (DEBUG_POWER) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Wake for ");
+ app.toShortString(sb);
+ sb.append(": over ");
+ TimeUtils.formatDuration(realtimeSince, sb);
+ sb.append(" used ");
+ TimeUtils.formatDuration(wtimeUsed, sb);
+ sb.append(" (");
+ sb.append((wtimeUsed*100)/realtimeSince);
+ sb.append("%)");
+ Slog.i(TAG, sb.toString());
+ sb.setLength(0);
+ sb.append("CPU for ");
+ app.toShortString(sb);
+ sb.append(": over ");
+ TimeUtils.formatDuration(uptimeSince, sb);
+ sb.append(" used ");
+ TimeUtils.formatDuration(cputimeUsed, sb);
+ sb.append(" (");
+ sb.append((cputimeUsed*100)/uptimeSince);
+ sb.append("%)");
+ Slog.i(TAG, sb.toString());
+ }
+ // If a process has held a wake lock for more
+ // than 50% of the time during this period,
+ // that sounds pad. Kill!
+ if (doWakeKills && realtimeSince > 0
+ && ((wtimeUsed*100)/realtimeSince) >= 50) {
+ synchronized (stats) {
+ stats.reportExcessiveWakeLocked(app.info.uid, app.processName,
+ realtimeSince, wtimeUsed);
+ }
+ Slog.w(TAG, "Excessive wake lock in " + app.processName
+ + " (pid " + app.pid + "): held " + wtimeUsed
+ + " during " + realtimeSince);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "excessive wake lock");
+ Process.killProcessQuiet(app.pid);
+ } else if (doCpuKills && uptimeSince > 0
+ && ((cputimeUsed*100)/uptimeSince) >= 50) {
+ synchronized (stats) {
+ stats.reportExcessiveCpuLocked(app.info.uid, app.processName,
+ uptimeSince, cputimeUsed);
+ }
+ Slog.w(TAG, "Excessive CPU in " + app.processName
+ + " (pid " + app.pid + "): used " + cputimeUsed
+ + " during " + uptimeSince);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "excessive cpu");
+ Process.killProcessQuiet(app.pid);
+ } else {
+ app.lastWakeTime = wtime;
+ app.lastCpuTime = app.curCpuTime;
+ }
+ }
+ }
+ }
+
private final boolean updateOomAdjLocked(
ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
app.hiddenAdj = hiddenAdj;
@@ -14243,6 +12044,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return true;
}
+ final boolean wasKeeping = app.keeping;
+
int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) {
@@ -14258,6 +12061,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// background (such as a service stopping).
scheduleAppGcLocked(app);
}
+
+ if (wasKeeping && !app.keeping) {
+ // This app is no longer something we want to keep. Note
+ // its current wake lock time to later know to kill it if
+ // it is not behaving well.
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, SystemClock.elapsedRealtime());
+ }
+ app.lastCpuTime = app.curCpuTime;
+ }
+
app.setRawAdj = app.curRawAdj;
}
if (adj != app.setAdj) {
@@ -14301,19 +12117,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return true;
}
- private final HistoryRecord resumedAppLocked() {
- HistoryRecord resumedActivity = mResumedActivity;
+ private final ActivityRecord resumedAppLocked() {
+ ActivityRecord resumedActivity = mMainStack.mResumedActivity;
if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = mPausingActivity;
+ resumedActivity = mMainStack.mPausingActivity;
if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = topRunningActivityLocked(null);
+ resumedActivity = mMainStack.topRunningActivityLocked(null);
}
}
return resumedActivity;
}
private final boolean updateOomAdjLocked(ProcessRecord app) {
- final HistoryRecord TOP_ACT = resumedAppLocked();
+ final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
int curAdj = app.curAdj;
final boolean wasHidden = app.curAdj >= HIDDEN_APP_MIN_ADJ
@@ -14334,9 +12150,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return res;
}
- private final boolean updateOomAdjLocked() {
+ final boolean updateOomAdjLocked() {
boolean didOomAdj = true;
- final HistoryRecord TOP_ACT = resumedAppLocked();
+ final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
if (false) {
@@ -14398,7 +12214,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj;
}
- private final void trimApplications() {
+ final void trimApplications() {
synchronized (this) {
int i;
@@ -14445,8 +12261,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final ProcessRecord app = mLruProcesses.get(i);
if (app.persistent || app.services.size() != 0
- || app.curReceiver != null
- || app.persistentActivities > 0) {
+ || app.curReceiver != null) {
// Don't count processes holding services against our
// maximum process count.
if (localLOGV) Slog.v(
@@ -14511,14 +12326,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Quit the application only if we have a state saved for
// all of its activities.
boolean canQuit = !app.persistent && app.curReceiver == null
- && app.services.size() == 0
- && app.persistentActivities == 0;
+ && app.services.size() == 0;
int NUMA = app.activities.size();
int j;
if (Config.LOGV) Slog.v(
TAG, "Looking to quit " + app.processName);
for (j=0; j<NUMA && canQuit; j++) {
- HistoryRecord r = (HistoryRecord)app.activities.get(j);
+ ActivityRecord r = app.activities.get(j);
if (Config.LOGV) Slog.v(
TAG, " " + r.intent.getComponent().flattenToShortString()
+ ": frozen=" + r.haveState + ", visible=" + r.visible);
@@ -14528,9 +12342,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (canQuit) {
// Finish all of the activities, and then the app itself.
for (j=0; j<NUMA; j++) {
- HistoryRecord r = (HistoryRecord)app.activities.get(j);
+ ActivityRecord r = app.activities.get(j);
if (!r.finishing) {
- destroyActivityLocked(r, false);
+ r.stack.destroyActivityLocked(r, false);
}
r.resultTo = null;
}
@@ -14567,25 +12381,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Finally, if there are too many activities now running, try to
// finish as many as we can to get back down to the limit.
for ( i=0;
- i<mLRUActivities.size()
- && mLRUActivities.size() > curMaxActivities;
+ i<mMainStack.mLRUActivities.size()
+ && mMainStack.mLRUActivities.size() > curMaxActivities;
i++) {
- final HistoryRecord r
- = (HistoryRecord)mLRUActivities.get(i);
+ final ActivityRecord r
+ = (ActivityRecord)mMainStack.mLRUActivities.get(i);
// We can finish this one if we have its icicle saved and
// it is not persistent.
if ((r.haveState || !r.stateNotNeeded) && !r.visible
- && r.stopped && !r.persistent && !r.finishing) {
- final int origSize = mLRUActivities.size();
- destroyActivityLocked(r, true);
+ && r.stopped && !r.finishing) {
+ final int origSize = mMainStack.mLRUActivities.size();
+ r.stack.destroyActivityLocked(r, true);
// This will remove it from the LRU list, so keep
// our index at the same value. Note that this check to
// see if the size changes is just paranoia -- if
// something unexpected happens, we don't want to end up
// in an infinite loop.
- if (origSize > mLRUActivities.size()) {
+ if (origSize > mMainStack.mLRUActivities.size()) {
i--;
}
}
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index dca7a99..4d773e4 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,7 @@
package com.android.server.am;
import com.android.server.AttributeCache;
-import com.android.server.am.ActivityManagerService.ActivityState;
+import com.android.server.am.ActivityStack.ActivityState;
import android.app.Activity;
import android.content.ComponentName;
@@ -29,9 +29,12 @@ import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Message;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
+import android.util.TimeUtils;
import android.view.IApplicationToken;
import java.io.PrintWriter;
@@ -42,8 +45,9 @@ import java.util.HashSet;
/**
* An entry in the history stack, representing an activity.
*/
-class HistoryRecord extends IApplicationToken.Stub {
+class ActivityRecord extends IApplicationToken.Stub {
final ActivityManagerService service; // owner
+ final ActivityStack stack; // owner
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid who started the activity.
final Intent intent; // the original intent that generated us
@@ -65,22 +69,22 @@ class HistoryRecord extends IApplicationToken.Stub {
int icon; // resource identifier of activity's icon.
int theme; // resource identifier of activity's theme.
TaskRecord task; // the task this is in.
- long startTime; // when we starting launching this activity
+ long launchTime; // when we starting launching this activity
+ long startTime; // last time this activity was started
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
Configuration configuration; // configuration activity was last running in
- HistoryRecord resultTo; // who started this entry, so will get our reply
+ ActivityRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
final int requestCode; // code given by requester (resultTo)
ArrayList results; // pending ActivityResult objs we have received
HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
ArrayList newIntents; // any pending new intents for single-top mode
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
- HashSet<UriPermission> readUriPermissions; // special access to reading uris.
- HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+ UriPermissionOwner uriPermissions; // current special URI access perms.
ProcessRecord app; // if non-null, hosting application
Bitmap thumbnail; // icon representation of paused screen
CharSequence description; // textual description of paused screen
- ActivityManagerService.ActivityState state; // current state we are in
+ ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
boolean frontOfTask; // is this the root activity of its task?
boolean launchFailed; // set if a launched failed, to abort on 2nd try
@@ -92,7 +96,6 @@ class HistoryRecord extends IApplicationToken.Stub {
int configChangeFlags; // which config values have changed
boolean keysPaused; // has key dispatching been paused for it?
boolean inHistory; // are we in the history stack?
- boolean persistent; // requested to be persistent?
int launchMode; // the launch mode activity attribute.
boolean visible; // does this activity's window need to be shown?
boolean waitingVisible; // true if waiting for a new act to become vis
@@ -137,11 +140,15 @@ class HistoryRecord extends IApplicationToken.Stub {
if (pendingResults != null) {
pw.print(prefix); pw.print("pendingResults="); pw.println(pendingResults);
}
- if (readUriPermissions != null) {
- pw.print(prefix); pw.print("readUriPermissions="); pw.println(readUriPermissions);
- }
- if (writeUriPermissions != null) {
- pw.print(prefix); pw.print("writeUriPermissions="); pw.println(writeUriPermissions);
+ if (uriPermissions != null) {
+ if (uriPermissions.readUriPermissions != null) {
+ pw.print(prefix); pw.print("readUriPermissions=");
+ pw.println(uriPermissions.readUriPermissions);
+ }
+ if (uriPermissions.writeUriPermissions != null) {
+ pw.print(prefix); pw.print("writeUriPermissions=");
+ pw.println(uriPermissions.writeUriPermissions);
+ }
}
pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed);
pw.print(" haveState="); pw.print(haveState);
@@ -152,13 +159,17 @@ class HistoryRecord extends IApplicationToken.Stub {
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
- pw.print(" persistent="); pw.print(persistent);
pw.print(" launchMode="); pw.println(launchMode);
pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
pw.print(" visible="); pw.print(visible);
pw.print(" frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
pw.print(" idle="); pw.println(idle);
+ if (launchTime != 0 || startTime != 0) {
+ pw.print(prefix); pw.print("launchTime=");
+ TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime=");
+ TimeUtils.formatDuration(startTime, pw); pw.println("");
+ }
if (waitingVisible || nowVisible) {
pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
pw.print(" nowVisible="); pw.println(nowVisible);
@@ -173,12 +184,13 @@ class HistoryRecord extends IApplicationToken.Stub {
}
}
- HistoryRecord(ActivityManagerService _service, ProcessRecord _caller,
+ ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller,
int _launchedFromUid, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
- HistoryRecord _resultTo, String _resultWho, int _reqCode,
+ ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified) {
service = _service;
+ stack = _stack;
info = aInfo;
launchedFromUid = _launchedFromUid;
intent = _intent;
@@ -189,7 +201,7 @@ class HistoryRecord extends IApplicationToken.Stub {
resultTo = _resultTo;
resultWho = _resultWho;
requestCode = _reqCode;
- state = ActivityManagerService.ActivityState.INITIALIZING;
+ state = ActivityState.INITIALIZING;
frontOfTask = false;
launchFailed = false;
haveState = false;
@@ -199,7 +211,6 @@ class HistoryRecord extends IApplicationToken.Stub {
configDestroy = false;
keysPaused = false;
inHistory = false;
- persistent = false;
visible = true;
waitingVisible = false;
nowVisible = false;
@@ -292,7 +303,14 @@ class HistoryRecord extends IApplicationToken.Stub {
}
}
- void addResultLocked(HistoryRecord from, String resultWho,
+ UriPermissionOwner getUriPermissionsLocked() {
+ if (uriPermissions == null) {
+ uriPermissions = new UriPermissionOwner(service, this);
+ }
+ return uriPermissions;
+ }
+
+ void addResultLocked(ActivityRecord from, String resultWho,
int requestCode, int resultCode,
Intent resultData) {
ActivityResult r = new ActivityResult(from, resultWho,
@@ -303,7 +321,7 @@ class HistoryRecord extends IApplicationToken.Stub {
results.add(r);
}
- void removeResultsLocked(HistoryRecord from, String resultWho,
+ void removeResultsLocked(ActivityRecord from, String resultWho,
int requestCode) {
if (results != null) {
for (int i=results.size()-1; i>=0; i--) {
@@ -327,6 +345,42 @@ class HistoryRecord extends IApplicationToken.Stub {
}
newIntents.add(intent);
}
+
+ /**
+ * Deliver a new Intent to an existing activity, so that its onNewIntent()
+ * method will be called at the proper time.
+ */
+ final void deliverNewIntentLocked(int callingUid, Intent intent) {
+ boolean sent = false;
+ if (state == ActivityState.RESUMED
+ && app != null && app.thread != null) {
+ try {
+ ArrayList<Intent> ar = new ArrayList<Intent>();
+ intent = new Intent(intent);
+ ar.add(intent);
+ service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+ intent, getUriPermissionsLocked());
+ app.thread.scheduleNewIntent(ar, this);
+ sent = true;
+ } catch (RemoteException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Exception thrown sending new intent to " + this, e);
+ } catch (NullPointerException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Exception thrown sending new intent to " + this, e);
+ }
+ }
+ if (!sent) {
+ addNewIntentLocked(new Intent(intent));
+ }
+ }
+
+ void removeUriPermissionsLocked() {
+ if (uriPermissions != null) {
+ uriPermissions.removeUriPermissionsLocked();
+ uriPermissions = null;
+ }
+ }
void pauseKeyDispatchingLocked() {
if (!keysPaused) {
@@ -367,34 +421,37 @@ class HistoryRecord extends IApplicationToken.Stub {
public void windowsVisible() {
synchronized(service) {
- if (startTime != 0) {
+ if (launchTime != 0) {
final long curTime = SystemClock.uptimeMillis();
- final long thisTime = curTime - startTime;
- final long totalTime = service.mInitialStartTime != 0
- ? (curTime - service.mInitialStartTime) : thisTime;
+ final long thisTime = curTime - launchTime;
+ final long totalTime = stack.mInitialStartTime != 0
+ ? (curTime - stack.mInitialStartTime) : thisTime;
if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
EventLog.writeEvent(EventLogTags.ACTIVITY_LAUNCH_TIME,
System.identityHashCode(this), shortComponentName,
thisTime, totalTime);
StringBuilder sb = service.mStringBuilder;
sb.setLength(0);
- sb.append("Displayed activity ");
+ sb.append("Displayed ");
sb.append(shortComponentName);
sb.append(": ");
- sb.append(thisTime);
- sb.append(" ms (total ");
- sb.append(totalTime);
- sb.append(" ms)");
+ TimeUtils.formatDuration(thisTime, sb);
+ if (thisTime != totalTime) {
+ sb.append(" (total ");
+ TimeUtils.formatDuration(totalTime, sb);
+ sb.append(")");
+ }
Log.i(ActivityManagerService.TAG, sb.toString());
}
- service.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
+ stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
if (totalTime > 0) {
service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
}
- startTime = 0;
- service.mInitialStartTime = 0;
+ launchTime = 0;
+ stack.mInitialStartTime = 0;
}
- service.reportActivityVisibleLocked(this);
+ startTime = 0;
+ stack.reportActivityVisibleLocked(this);
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG, "windowsVisible(): " + this);
if (!nowVisible) {
@@ -403,27 +460,27 @@ class HistoryRecord extends IApplicationToken.Stub {
// Instead of doing the full stop routine here, let's just
// hide any activities we now can, and let them stop when
// the normal idle happens.
- service.processStoppingActivitiesLocked(false);
+ stack.processStoppingActivitiesLocked(false);
} else {
// If this activity was already idle, then we now need to
// make sure we perform the full stop of any activities
// that are waiting to do so. This is because we won't
// do that while they are still waiting for this one to
// become visible.
- final int N = service.mWaitingVisibleActivities.size();
+ final int N = stack.mWaitingVisibleActivities.size();
if (N > 0) {
for (int i=0; i<N; i++) {
- HistoryRecord r = (HistoryRecord)
- service.mWaitingVisibleActivities.get(i);
+ ActivityRecord r = (ActivityRecord)
+ stack.mWaitingVisibleActivities.get(i);
r.waitingVisible = false;
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG,
"Was waiting for visible: " + r);
}
- service.mWaitingVisibleActivities.clear();
+ stack.mWaitingVisibleActivities.clear();
Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- service.mHandler.sendMessage(msg);
+ msg.what = ActivityStack.IDLE_NOW_MSG;
+ stack.mHandler.sendMessage(msg);
}
}
service.scheduleAppGcsLocked();
@@ -437,16 +494,16 @@ class HistoryRecord extends IApplicationToken.Stub {
nowVisible = false;
}
- private HistoryRecord getWaitingHistoryRecordLocked() {
+ private ActivityRecord getWaitingHistoryRecordLocked() {
// First find the real culprit... if we are waiting
// for another app to start, then we have paused dispatching
// for this activity.
- HistoryRecord r = this;
+ ActivityRecord r = this;
if (r.waitingVisible) {
// Hmmm, who might we be waiting for?
- r = service.mResumedActivity;
+ r = stack.mResumedActivity;
if (r == null) {
- r = service.mPausingActivity;
+ r = stack.mPausingActivity;
}
// Both of those null? Fall back to 'this' again
if (r == null) {
@@ -458,7 +515,7 @@ class HistoryRecord extends IApplicationToken.Stub {
}
public boolean keyDispatchingTimedOut() {
- HistoryRecord r;
+ ActivityRecord r;
ProcessRecord anrApp = null;
synchronized(service) {
r = getWaitingHistoryRecordLocked();
@@ -496,7 +553,7 @@ class HistoryRecord extends IApplicationToken.Stub {
/** Returns the key dispatching timeout for this application token. */
public long getKeyDispatchingTimeout() {
synchronized(service) {
- HistoryRecord r = getWaitingHistoryRecordLocked();
+ ActivityRecord r = getWaitingHistoryRecordLocked();
if (r == null || r.app == null
|| r.app.instrumentationClass == null) {
return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
@@ -515,7 +572,6 @@ class HistoryRecord extends IApplicationToken.Stub {
state == ActivityState.RESUMED;
}
-
public String toString() {
if (stringName != null) {
return stringName;
diff --git a/services/java/com/android/server/am/ActivityResult.java b/services/java/com/android/server/am/ActivityResult.java
index 3cc2725..12eba34 100644
--- a/services/java/com/android/server/am/ActivityResult.java
+++ b/services/java/com/android/server/am/ActivityResult.java
@@ -24,9 +24,9 @@ import android.os.Bundle;
* Pending result information to send back to an activity.
*/
class ActivityResult extends ResultInfo {
- final HistoryRecord mFrom;
+ final ActivityRecord mFrom;
- public ActivityResult(HistoryRecord from, String resultWho,
+ public ActivityResult(ActivityRecord from, String resultWho,
int requestCode, int resultCode, Intent data) {
super(resultWho, requestCode, resultCode, data);
mFrom = from;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
new file mode 100644
index 0000000..463493b
--- /dev/null
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -0,0 +1,3557 @@
+/*
+ * Copyright (C) 2010 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.am;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
+
+import android.app.Activity;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import static android.app.IActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.IActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+import static android.app.IActivityManager.START_INTENT_NOT_RESOLVED;
+import static android.app.IActivityManager.START_PERMISSION_DENIED;
+import static android.app.IActivityManager.START_RETURN_INTENT_TO_CALLER;
+import static android.app.IActivityManager.START_SUCCESS;
+import static android.app.IActivityManager.START_SWITCHES_CANCELED;
+import static android.app.IActivityManager.START_TASK_TO_FRONT;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
+import android.app.ResultInfo;
+import android.app.IActivityManager.WaitResult;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.view.WindowManagerPolicy;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * State and management of a single stack of activities.
+ */
+public class ActivityStack {
+ static final String TAG = ActivityManagerService.TAG;
+ static final boolean localLOGV = ActivityManagerService.localLOGV;
+ static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
+ static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE;
+ static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY;
+ static final boolean DEBUG_USER_LEAVING = ActivityManagerService.DEBUG_USER_LEAVING;
+ static final boolean DEBUG_TRANSITION = ActivityManagerService.DEBUG_TRANSITION;
+ static final boolean DEBUG_RESULTS = ActivityManagerService.DEBUG_RESULTS;
+ static final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
+ static final boolean DEBUG_TASKS = ActivityManagerService.DEBUG_TASKS;
+
+ static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS;
+
+ // How long we wait until giving up on the last activity telling us it
+ // is idle.
+ static final int IDLE_TIMEOUT = 10*1000;
+
+ // How long we wait until giving up on the last activity to pause. This
+ // is short because it directly impacts the responsiveness of starting the
+ // next activity.
+ static final int PAUSE_TIMEOUT = 500;
+
+ // How long we can hold the launch wake lock before giving up.
+ static final int LAUNCH_TIMEOUT = 10*1000;
+
+ // How long we wait until giving up on an activity telling us it has
+ // finished destroying itself.
+ static final int DESTROY_TIMEOUT = 10*1000;
+
+ // How long until we reset a task when the user returns to it. Currently
+ // 30 minutes.
+ static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+
+ // How long between activity launches that we consider safe to not warn
+ // the user about an unexpected activity being launched on top.
+ static final long START_WARN_TIME = 5*1000;
+
+ // Set to false to disable the preview that is shown while a new activity
+ // is being started.
+ static final boolean SHOW_APP_STARTING_PREVIEW = true;
+
+ enum ActivityState {
+ INITIALIZING,
+ RESUMED,
+ PAUSING,
+ PAUSED,
+ STOPPING,
+ STOPPED,
+ FINISHING,
+ DESTROYING,
+ DESTROYED
+ }
+
+ final ActivityManagerService mService;
+ final boolean mMainStack;
+
+ final Context mContext;
+
+ /**
+ * The back history of all previous (and possibly still
+ * running) activities. It contains HistoryRecord objects.
+ */
+ final ArrayList mHistory = new ArrayList();
+
+ /**
+ * List of running activities, sorted by recent usage.
+ * The first entry in the list is the least recently used.
+ * It contains HistoryRecord objects.
+ */
+ final ArrayList mLRUActivities = new ArrayList();
+
+ /**
+ * List of activities that are waiting for a new activity
+ * to become visible before completing whatever operation they are
+ * supposed to do.
+ */
+ final ArrayList<ActivityRecord> mWaitingVisibleActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * List of activities that are ready to be stopped, but waiting
+ * for the next activity to settle down before doing so. It contains
+ * HistoryRecord objects.
+ */
+ final ArrayList<ActivityRecord> mStoppingActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * Animations that for the current transition have requested not to
+ * be considered for the transition animation.
+ */
+ final ArrayList<ActivityRecord> mNoAnimActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * List of activities that are ready to be finished, but waiting
+ * for the previous activity to settle down before doing so. It contains
+ * HistoryRecord objects.
+ */
+ final ArrayList<ActivityRecord> mFinishingActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * List of people waiting to find out about the next launched activity.
+ */
+ final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched
+ = new ArrayList<IActivityManager.WaitResult>();
+
+ /**
+ * List of people waiting to find out about the next visible activity.
+ */
+ final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
+ = new ArrayList<IActivityManager.WaitResult>();
+
+ /**
+ * Set when the system is going to sleep, until we have
+ * successfully paused the current activity and released our wake lock.
+ * At that point the system is allowed to actually sleep.
+ */
+ final PowerManager.WakeLock mGoingToSleep;
+
+ /**
+ * We don't want to allow the device to go to sleep while in the process
+ * of launching an activity. This is primarily to allow alarm intent
+ * receivers to launch an activity and get that to run before the device
+ * goes back to sleep.
+ */
+ final PowerManager.WakeLock mLaunchingActivity;
+
+ /**
+ * When we are in the process of pausing an activity, before starting the
+ * next one, this variable holds the activity that is currently being paused.
+ */
+ ActivityRecord mPausingActivity = null;
+
+ /**
+ * This is the last activity that we put into the paused state. This is
+ * used to determine if we need to do an activity transition while sleeping,
+ * when we normally hold the top activity paused.
+ */
+ ActivityRecord mLastPausedActivity = null;
+
+ /**
+ * Current activity that is resumed, or null if there is none.
+ */
+ ActivityRecord mResumedActivity = null;
+
+ /**
+ * This is the last activity that has been started. It is only used to
+ * identify when multiple activities are started at once so that the user
+ * can be warned they may not be in the activity they think they are.
+ */
+ ActivityRecord mLastStartedActivity = null;
+
+ /**
+ * Set when we know we are going to be calling updateConfiguration()
+ * soon, so want to skip intermediate config checks.
+ */
+ boolean mConfigWillChange;
+
+ /**
+ * Set to indicate whether to issue an onUserLeaving callback when a
+ * newly launched activity is being brought in front of us.
+ */
+ boolean mUserLeaving = false;
+
+ long mInitialStartTime = 0;
+
+ static final int PAUSE_TIMEOUT_MSG = 9;
+ static final int IDLE_TIMEOUT_MSG = 10;
+ static final int IDLE_NOW_MSG = 11;
+ static final int LAUNCH_TIMEOUT_MSG = 16;
+ static final int DESTROY_TIMEOUT_MSG = 17;
+ static final int RESUME_TOP_ACTIVITY_MSG = 19;
+
+ final Handler mHandler = new Handler() {
+ //public Handler() {
+ // if (localLOGV) Slog.v(TAG, "Handler started!");
+ //}
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case PAUSE_TIMEOUT_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ // We don't at this point know if the activity is fullscreen,
+ // so we need to be conservative and assume it isn't.
+ Slog.w(TAG, "Activity pause timeout for " + token);
+ activityPaused(token, null, true);
+ } break;
+ case IDLE_TIMEOUT_MSG: {
+ if (mService.mDidDexOpt) {
+ mService.mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+ nmsg.obj = msg.obj;
+ mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
+ return;
+ }
+ // We don't at this point know if the activity is fullscreen,
+ // so we need to be conservative and assume it isn't.
+ IBinder token = (IBinder)msg.obj;
+ Slog.w(TAG, "Activity idle timeout for " + token);
+ activityIdleInternal(token, true, null);
+ } break;
+ case DESTROY_TIMEOUT_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ // We don't at this point know if the activity is fullscreen,
+ // so we need to be conservative and assume it isn't.
+ Slog.w(TAG, "Activity destroy timeout for " + token);
+ activityDestroyed(token);
+ } break;
+ case IDLE_NOW_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ activityIdleInternal(token, false, null);
+ } break;
+ case LAUNCH_TIMEOUT_MSG: {
+ if (mService.mDidDexOpt) {
+ mService.mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
+ return;
+ }
+ synchronized (mService) {
+ if (mLaunchingActivity.isHeld()) {
+ Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+ mLaunchingActivity.release();
+ }
+ }
+ } break;
+ case RESUME_TOP_ACTIVITY_MSG: {
+ synchronized (mService) {
+ resumeTopActivityLocked(null);
+ }
+ } break;
+ }
+ }
+ };
+
+ ActivityStack(ActivityManagerService service, Context context, boolean mainStack) {
+ mService = service;
+ mContext = context;
+ mMainStack = mainStack;
+ PowerManager pm =
+ (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
+ mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
+ mLaunchingActivity.setReferenceCounted(false);
+ }
+
+ final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && r != notTop) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && !r.delayedResume && r != notTop) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ /**
+ * This is a simplified version of topRunningActivityLocked that provides a number of
+ * optional skip-over modes. It is intended for use with the ActivityController hook only.
+ *
+ * @param token If non-null, any history records matching this token will be skipped.
+ * @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
+ *
+ * @return Returns the HistoryRecord of the next activity on the stack.
+ */
+ final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ // Note: the taskId check depends on real taskId fields being non-zero
+ if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ final int indexOfTokenLocked(IBinder token) {
+ int count = mHistory.size();
+
+ // convert the token to an entry in the history.
+ int index = -1;
+ for (int i=count-1; i>=0; i--) {
+ Object o = mHistory.get(i);
+ if (o == token) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ private final boolean updateLRUListLocked(ActivityRecord r) {
+ final boolean hadit = mLRUActivities.remove(r);
+ mLRUActivities.add(r);
+ return hadit;
+ }
+
+ /**
+ * Returns the top activity in any existing task matching the given
+ * Intent. Returns null if no such task is found.
+ */
+ private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+ ComponentName cls = intent.getComponent();
+ if (info.targetActivity != null) {
+ cls = new ComponentName(info.packageName, info.targetActivity);
+ }
+
+ TaskRecord cp = null;
+
+ final int N = mHistory.size();
+ for (int i=(N-1); i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && r.task != cp
+ && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ cp = r.task;
+ //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
+ // + "/aff=" + r.task.affinity + " to new cls="
+ // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
+ if (r.task.affinity != null) {
+ if (r.task.affinity.equals(info.taskAffinity)) {
+ //Slog.i(TAG, "Found matching affinity!");
+ return r;
+ }
+ } else if (r.task.intent != null
+ && r.task.intent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
+ } else if (r.task.affinityIntent != null
+ && r.task.affinityIntent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the first activity (starting from the top of the stack) that
+ * is the same as the given activity. Returns null if no such activity
+ * is found.
+ */
+ private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
+ ComponentName cls = intent.getComponent();
+ if (info.targetActivity != null) {
+ cls = new ComponentName(info.packageName, info.targetActivity);
+ }
+
+ final int N = mHistory.size();
+ for (int i=(N-1); i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing) {
+ if (r.intent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ final boolean realStartActivityLocked(ActivityRecord r,
+ ProcessRecord app, boolean andResume, boolean checkConfig)
+ throws RemoteException {
+
+ r.startFreezingScreenLocked(app, 0);
+ mService.mWindowManager.setAppVisibility(r, true);
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order. Note that
+ // as a result of this, it can call back into the activity
+ // manager with a new orientation. We don't care about that,
+ // because the activity is not currently running so we are
+ // just restarting it anyway.
+ if (checkConfig) {
+ Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
+ mService.mConfiguration,
+ r.mayFreezeScreenLocked(app) ? r : null);
+ mService.updateConfigurationLocked(config, r);
+ }
+
+ r.app = app;
+
+ if (localLOGV) Slog.v(TAG, "Launching: " + r);
+
+ int idx = app.activities.indexOf(r);
+ if (idx < 0) {
+ app.activities.add(r);
+ }
+ mService.updateLruProcessLocked(app, true, true);
+
+ try {
+ if (app.thread == null) {
+ throw new RemoteException();
+ }
+ List<ResultInfo> results = null;
+ List<Intent> newIntents = null;
+ if (andResume) {
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
+ + " icicle=" + r.icicle
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
+ if (andResume) {
+ EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+ }
+ if (r.isHomeActivity) {
+ mService.mHomeProcess = app;
+ }
+ mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
+ System.identityHashCode(r),
+ r.info, r.icicle, results, newIntents, !andResume,
+ mService.isNextTransitionForward());
+
+ if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+ // This may be a heavy-weight process! Note that the package
+ // manager will ensure that only activity can run in the main
+ // process of the .apk, which is the only thing that will be
+ // considered heavy-weight.
+ if (app.processName.equals(app.info.packageName)) {
+ if (mService.mHeavyWeightProcess != null
+ && mService.mHeavyWeightProcess != app) {
+ Log.w(TAG, "Starting new heavy weight process " + app
+ + " when already running "
+ + mService.mHeavyWeightProcess);
+ }
+ mService.mHeavyWeightProcess = app;
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
+ msg.obj = r;
+ mService.mHandler.sendMessage(msg);
+ }
+ }
+
+ } catch (RemoteException e) {
+ if (r.launchFailed) {
+ // This is the second time we failed -- finish activity
+ // and give up.
+ Slog.e(TAG, "Second failure launching "
+ + r.intent.getComponent().flattenToShortString()
+ + ", giving up", e);
+ mService.appDiedLocked(app, app.pid, app.thread);
+ requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+ "2nd-crash");
+ return false;
+ }
+
+ // This is the first time we failed -- restart process and
+ // retry.
+ app.activities.remove(r);
+ throw e;
+ }
+
+ r.launchFailed = false;
+ if (updateLRUListLocked(r)) {
+ Slog.w(TAG, "Activity " + r
+ + " being launched, but already in LRU list");
+ }
+
+ if (andResume) {
+ // As part of the process of launching, ActivityThread also performs
+ // a resume.
+ r.state = ActivityState.RESUMED;
+ r.icicle = null;
+ r.haveState = false;
+ r.stopped = false;
+ mResumedActivity = r;
+ r.task.touchActiveTime();
+ completeResumeLocked(r);
+ pauseIfSleepingLocked();
+ } else {
+ // This activity is not starting in the resumed state... which
+ // should look like we asked it to pause+stop (but remain visible),
+ // and it has done so and reported back the current icicle and
+ // other state.
+ r.state = ActivityState.STOPPED;
+ r.stopped = true;
+ }
+
+ // Launch the new version setup screen if needed. We do this -after-
+ // launching the initial activity (that is, home), so that it can have
+ // a chance to initialize itself while in the background, making the
+ // switch back to it faster and look better.
+ if (mMainStack) {
+ mService.startSetupActivityLocked();
+ }
+
+ return true;
+ }
+
+ private final void startSpecificActivityLocked(ActivityRecord r,
+ boolean andResume, boolean checkConfig) {
+ // Is this activity's application already running?
+ ProcessRecord app = mService.getProcessRecordLocked(r.processName,
+ r.info.applicationInfo.uid);
+
+ if (r.launchTime == 0) {
+ r.launchTime = SystemClock.uptimeMillis();
+ if (mInitialStartTime == 0) {
+ mInitialStartTime = r.launchTime;
+ }
+ } else if (mInitialStartTime == 0) {
+ mInitialStartTime = SystemClock.uptimeMillis();
+ }
+
+ if (app != null && app.thread != null) {
+ try {
+ realStartActivityLocked(r, app, andResume, checkConfig);
+ return;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when starting activity "
+ + r.intent.getComponent().flattenToShortString(), e);
+ }
+
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
+
+ mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
+ "activity", r.intent.getComponent(), false);
+ }
+
+ void pauseIfSleepingLocked() {
+ if (mService.mSleeping || mService.mShuttingDown) {
+ if (!mGoingToSleep.isHeld()) {
+ mGoingToSleep.acquire();
+ if (mLaunchingActivity.isHeld()) {
+ mLaunchingActivity.release();
+ mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ }
+ }
+
+ // If we are not currently pausing an activity, get the current
+ // one to pause. If we are pausing one, we will just let that stuff
+ // run and release the wake lock when all done.
+ if (mPausingActivity == null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
+ startPausingLocked(false, true);
+ }
+ }
+ }
+
+ private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+ if (mPausingActivity != null) {
+ RuntimeException e = new RuntimeException();
+ Slog.e(TAG, "Trying to pause when pause is already pending for "
+ + mPausingActivity, e);
+ }
+ ActivityRecord prev = mResumedActivity;
+ if (prev == null) {
+ RuntimeException e = new RuntimeException();
+ Slog.e(TAG, "Trying to pause when nothing is resumed", e);
+ resumeTopActivityLocked(null);
+ return;
+ }
+ if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
+ mResumedActivity = null;
+ mPausingActivity = prev;
+ mLastPausedActivity = prev;
+ prev.state = ActivityState.PAUSING;
+ prev.task.touchActiveTime();
+
+ mService.updateCpuStats();
+
+ if (prev.app != null && prev.app.thread != null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
+ try {
+ EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
+ System.identityHashCode(prev),
+ prev.shortComponentName);
+ prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
+ prev.configChangeFlags);
+ if (mMainStack) {
+ mService.updateUsageStats(prev, false);
+ }
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ }
+ } else {
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ }
+
+ // If we are not going to sleep, we want to ensure the device is
+ // awake until the next activity is started.
+ if (!mService.mSleeping && !mService.mShuttingDown) {
+ mLaunchingActivity.acquire();
+ if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
+ // To be safe, don't allow the wake lock to be held for too long.
+ Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
+ }
+ }
+
+
+ if (mPausingActivity != null) {
+ // Have the window manager pause its key dispatching until the new
+ // activity has started. If we're pausing the activity just because
+ // the screen is being turned off and the UI is sleeping, don't interrupt
+ // key dispatch; the same activity will pick it up again on wakeup.
+ if (!uiSleeping) {
+ prev.pauseKeyDispatchingLocked();
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
+ }
+
+ // Schedule a pause timeout in case the app doesn't respond.
+ // We don't give it much time because this directly impacts the
+ // responsiveness seen by the user.
+ Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+ msg.obj = prev;
+ mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+ if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ } else {
+ // This activity failed to schedule the
+ // pause, so just treat it as being paused now.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
+ resumeTopActivityLocked(null);
+ }
+ }
+
+ final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
+ if (DEBUG_PAUSE) Slog.v(
+ TAG, "Activity paused: token=" + token + ", icicle=" + icicle
+ + ", timeout=" + timeout);
+
+ ActivityRecord r = null;
+
+ synchronized (mService) {
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ r = (ActivityRecord)mHistory.get(index);
+ if (!timeout) {
+ r.icicle = icicle;
+ r.haveState = true;
+ }
+ mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ if (mPausingActivity == r) {
+ r.state = ActivityState.PAUSED;
+ completePauseLocked();
+ } else {
+ EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
+ System.identityHashCode(r), r.shortComponentName,
+ mPausingActivity != null
+ ? mPausingActivity.shortComponentName : "(none)");
+ }
+ }
+ }
+ }
+
+ private final void completePauseLocked() {
+ ActivityRecord prev = mPausingActivity;
+ if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
+
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
+ prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
+ } else if (prev.app != null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
+ if (prev.waitingVisible) {
+ prev.waitingVisible = false;
+ mWaitingVisibleActivities.remove(prev);
+ if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
+ TAG, "Complete pause, no longer waiting: " + prev);
+ }
+ if (prev.configDestroy) {
+ // The previous is being paused because the configuration
+ // is changing, which means it is actually stopping...
+ // To juggle the fact that we are also starting a new
+ // instance right now, we need to first completely stop
+ // the current instance before starting the new one.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
+ destroyActivityLocked(prev, true);
+ } else {
+ mStoppingActivities.add(prev);
+ if (mStoppingActivities.size() > 3) {
+ // If we already have a few activities waiting to stop,
+ // then give up on things going idle and start clearing
+ // them out.
+ if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
+ Message msg = Message.obtain();
+ msg.what = IDLE_NOW_MSG;
+ mHandler.sendMessage(msg);
+ }
+ }
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
+ prev = null;
+ }
+ mPausingActivity = null;
+ }
+
+ if (!mService.mSleeping && !mService.mShuttingDown) {
+ resumeTopActivityLocked(prev);
+ } else {
+ if (mGoingToSleep.isHeld()) {
+ mGoingToSleep.release();
+ }
+ if (mService.mShuttingDown) {
+ mService.notifyAll();
+ }
+ }
+
+ if (prev != null) {
+ prev.resumeKeyDispatchingLocked();
+ }
+
+ if (prev.app != null && prev.cpuTimeAtResume > 0
+ && mService.mBatteryStatsService.isOnBattery()) {
+ long diff = 0;
+ synchronized (mService.mProcessStatsThread) {
+ diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
+ - prev.cpuTimeAtResume;
+ }
+ if (diff > 0) {
+ BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
+ synchronized (bsi) {
+ BatteryStatsImpl.Uid.Proc ps =
+ bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
+ prev.info.packageName);
+ if (ps != null) {
+ ps.addForegroundTimeLocked(diff);
+ }
+ }
+ }
+ }
+ prev.cpuTimeAtResume = 0; // reset it
+ }
+
+ /**
+ * Once we know that we have asked an application to put an activity in
+ * the resumed state (either by launching it or explicitly telling it),
+ * this function updates the rest of our state to match that fact.
+ */
+ private final void completeResumeLocked(ActivityRecord next) {
+ next.idle = false;
+ next.results = null;
+ next.newIntents = null;
+
+ // schedule an idle timeout in case the app doesn't do it for us.
+ Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+ msg.obj = next;
+ mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
+
+ if (false) {
+ // The activity was never told to pause, so just keep
+ // things going as-is. To maintain our own state,
+ // we need to emulate it coming back and saying it is
+ // idle.
+ msg = mHandler.obtainMessage(IDLE_NOW_MSG);
+ msg.obj = next;
+ mHandler.sendMessage(msg);
+ }
+
+ if (mMainStack) {
+ mService.reportResumedActivityLocked(next);
+ }
+
+ next.thumbnail = null;
+ if (mMainStack) {
+ mService.setFocusedActivityLocked(next);
+ }
+ next.resumeKeyDispatchingLocked();
+ ensureActivitiesVisibleLocked(null, 0);
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+
+ // Mark the point when the activity is resuming
+ // TODO: To be more accurate, the mark should be before the onCreate,
+ // not after the onResume. But for subsequent starts, onResume is fine.
+ if (next.app != null) {
+ synchronized (mService.mProcessStatsThread) {
+ next.cpuTimeAtResume = mService.mProcessStats.getCpuTimeForPid(next.app.pid);
+ }
+ } else {
+ next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
+ }
+ }
+
+ /**
+ * Make sure that all activities that need to be visible (that is, they
+ * currently can be seen by the user) actually are.
+ */
+ final void ensureActivitiesVisibleLocked(ActivityRecord top,
+ ActivityRecord starting, String onlyThisProcess, int configChanges) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "ensureActivitiesVisible behind " + top
+ + " configChanges=0x" + Integer.toHexString(configChanges));
+
+ // If the top activity is not fullscreen, then we need to
+ // make sure any activities under it are now visible.
+ final int count = mHistory.size();
+ int i = count-1;
+ while (mHistory.get(i) != top) {
+ i--;
+ }
+ ActivityRecord r;
+ boolean behindFullscreen = false;
+ for (; i>=0; i--) {
+ r = (ActivityRecord)mHistory.get(i);
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Make visible? " + r + " finishing=" + r.finishing
+ + " state=" + r.state);
+ if (r.finishing) {
+ continue;
+ }
+
+ final boolean doThisProcess = onlyThisProcess == null
+ || onlyThisProcess.equals(r.processName);
+
+ // First: if this is not the current activity being started, make
+ // sure it matches the current configuration.
+ if (r != starting && doThisProcess) {
+ ensureActivityConfigurationLocked(r, 0);
+ }
+
+ if (r.app == null || r.app.thread == null) {
+ if (onlyThisProcess == null
+ || onlyThisProcess.equals(r.processName)) {
+ // This activity needs to be visible, but isn't even
+ // running... get it started, but don't resume it
+ // at this point.
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Start and freeze screen for " + r);
+ if (r != starting) {
+ r.startFreezingScreenLocked(r.app, configChanges);
+ }
+ if (!r.visible) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Starting and making visible: " + r);
+ mService.mWindowManager.setAppVisibility(r, true);
+ }
+ if (r != starting) {
+ startSpecificActivityLocked(r, false, false);
+ }
+ }
+
+ } else if (r.visible) {
+ // If this activity is already visible, then there is nothing
+ // else to do here.
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Skipping: already visible at " + r);
+ r.stopFreezingScreenLocked(false);
+
+ } else if (onlyThisProcess == null) {
+ // This activity is not currently visible, but is running.
+ // Tell it to become visible.
+ r.visible = true;
+ if (r.state != ActivityState.RESUMED && r != starting) {
+ // If this activity is paused, tell it
+ // to now show its window.
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Making visible and scheduling visibility: " + r);
+ try {
+ mService.mWindowManager.setAppVisibility(r, true);
+ r.app.thread.scheduleWindowVisibility(r, true);
+ r.stopFreezingScreenLocked(false);
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making visibile: "
+ + r.intent.getComponent(), e);
+ }
+ }
+ }
+
+ // Aggregate current change flags.
+ configChanges |= r.configChangeFlags;
+
+ if (r.fullscreen) {
+ // At this point, nothing else needs to be shown
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Stopping: fullscreen at " + r);
+ behindFullscreen = true;
+ i--;
+ break;
+ }
+ }
+
+ // Now for any activities that aren't visible to the user, make
+ // sure they no longer are keeping the screen frozen.
+ while (i >= 0) {
+ r = (ActivityRecord)mHistory.get(i);
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Make invisible? " + r + " finishing=" + r.finishing
+ + " state=" + r.state
+ + " behindFullscreen=" + behindFullscreen);
+ if (!r.finishing) {
+ if (behindFullscreen) {
+ if (r.visible) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Making invisible: " + r);
+ r.visible = false;
+ try {
+ mService.mWindowManager.setAppVisibility(r, false);
+ if ((r.state == ActivityState.STOPPING
+ || r.state == ActivityState.STOPPED)
+ && r.app != null && r.app.thread != null) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Scheduling invisibility: " + r);
+ r.app.thread.scheduleWindowVisibility(r, false);
+ }
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making hidden: "
+ + r.intent.getComponent(), e);
+ }
+ } else {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Already invisible: " + r);
+ }
+ } else if (r.fullscreen) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Now behindFullscreen: " + r);
+ behindFullscreen = true;
+ }
+ }
+ i--;
+ }
+ }
+
+ /**
+ * Version of ensureActivitiesVisible that can easily be called anywhere.
+ */
+ final void ensureActivitiesVisibleLocked(ActivityRecord starting,
+ int configChanges) {
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ ensureActivitiesVisibleLocked(r, starting, null, configChanges);
+ }
+ }
+
+ /**
+ * Ensure that the top activity in the stack is resumed.
+ *
+ * @param prev The previously resumed activity, for when in the process
+ * of pausing; can be null to call from elsewhere.
+ *
+ * @return Returns true if something is being resumed, or false if
+ * nothing happened.
+ */
+ final boolean resumeTopActivityLocked(ActivityRecord prev) {
+ // Find the first activity that is not finishing.
+ ActivityRecord next = topRunningActivityLocked(null);
+
+ // Remember how we'll process this pause/resume situation, and ensure
+ // that the state is reset however we wind up proceeding.
+ final boolean userLeaving = mUserLeaving;
+ mUserLeaving = false;
+
+ if (next == null) {
+ // There are no more activities! Let's just start up the
+ // Launcher...
+ if (mMainStack) {
+ return mService.startHomeActivityLocked();
+ }
+ }
+
+ next.delayedResume = false;
+
+ // If the top activity is the resumed one, nothing to do.
+ if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return false;
+ }
+
+ // If we are sleeping, and there is no resumed activity, and the top
+ // activity is paused, well that is the state we want.
+ if ((mService.mSleeping || mService.mShuttingDown)
+ && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return false;
+ }
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mStoppingActivities.remove(next);
+ mWaitingVisibleActivities.remove(next);
+
+ if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
+
+ // If we are currently pausing an activity, then don't do anything
+ // until that is done.
+ if (mPausingActivity != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
+ return false;
+ }
+
+ // Okay we are now going to start a switch, to 'next'. We may first
+ // have to pause the current activity, but this is an important point
+ // where we have decided to go to 'next' so keep track of that.
+ // XXX "App Redirected" dialog is getting too many false positives
+ // at this point, so turn off for now.
+ if (false) {
+ if (mLastStartedActivity != null && !mLastStartedActivity.finishing) {
+ long now = SystemClock.uptimeMillis();
+ final boolean inTime = mLastStartedActivity.startTime != 0
+ && (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
+ final int lastUid = mLastStartedActivity.info.applicationInfo.uid;
+ final int nextUid = next.info.applicationInfo.uid;
+ if (inTime && lastUid != nextUid
+ && lastUid != next.launchedFromUid
+ && mService.checkPermission(
+ android.Manifest.permission.STOP_APP_SWITCHES,
+ -1, next.launchedFromUid)
+ != PackageManager.PERMISSION_GRANTED) {
+ mService.showLaunchWarningLocked(mLastStartedActivity, next);
+ } else {
+ next.startTime = now;
+ mLastStartedActivity = next;
+ }
+ } else {
+ next.startTime = SystemClock.uptimeMillis();
+ mLastStartedActivity = next;
+ }
+ }
+
+ // We need to start pausing the current activity so the top one
+ // can be resumed...
+ if (mResumedActivity != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
+ startPausingLocked(userLeaving, false);
+ return true;
+ }
+
+ if (prev != null && prev != next) {
+ if (!prev.waitingVisible && next != null && !next.nowVisible) {
+ prev.waitingVisible = true;
+ mWaitingVisibleActivities.add(prev);
+ if (DEBUG_SWITCH) Slog.v(
+ TAG, "Resuming top, waiting visible to hide: " + prev);
+ } else {
+ // The next activity is already visible, so hide the previous
+ // activity's windows right now so we can show the new one ASAP.
+ // We only do this if the previous is finishing, which should mean
+ // it is on top of the one being resumed so hiding it quickly
+ // is good. Otherwise, we want to do the normal route of allowing
+ // the resumed activity to be shown so we can decide if the
+ // previous should actually be hidden depending on whether the
+ // new one is found to be full-screen or not.
+ if (prev.finishing) {
+ mService.mWindowManager.setAppVisibility(prev, false);
+ if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
+ + prev + ", waitingVisible="
+ + (prev != null ? prev.waitingVisible : null)
+ + ", nowVisible=" + next.nowVisible);
+ } else {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
+ + prev + ", waitingVisible="
+ + (prev != null ? prev.waitingVisible : null)
+ + ", nowVisible=" + next.nowVisible);
+ }
+ }
+ }
+
+ // We are starting up the next activity, so tell the window manager
+ // that the previous one will be hidden soon. This way it can know
+ // to ignore it when computing the desired screen orientation.
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare close transition: prev=" + prev);
+ if (mNoAnimActivities.contains(prev)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(prev.task == next.task
+ ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
+ : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
+ }
+ mService.mWindowManager.setAppWillBeHidden(prev);
+ mService.mWindowManager.setAppVisibility(prev, false);
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: prev=" + prev);
+ if (mNoAnimActivities.contains(next)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(prev.task == next.task
+ ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
+ : WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ }
+ }
+ if (false) {
+ mService.mWindowManager.setAppWillBeHidden(prev);
+ mService.mWindowManager.setAppVisibility(prev, false);
+ }
+ } else if (mHistory.size() > 1) {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: no previous");
+ if (mNoAnimActivities.contains(next)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ }
+ }
+
+ if (next.app != null && next.app.thread != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
+
+ // This activity is now becoming visible.
+ mService.mWindowManager.setAppVisibility(next, true);
+
+ ActivityRecord lastResumedActivity = mResumedActivity;
+ ActivityState lastState = next.state;
+
+ mService.updateCpuStats();
+
+ next.state = ActivityState.RESUMED;
+ mResumedActivity = next;
+ next.task.touchActiveTime();
+ mService.updateLruProcessLocked(next.app, true, true);
+ updateLRUListLocked(next);
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean updated = false;
+ if (mMainStack) {
+ synchronized (mService) {
+ Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
+ mService.mConfiguration,
+ next.mayFreezeScreenLocked(next.app) ? next : null);
+ if (config != null) {
+ next.frozenBeforeDestroy = true;
+ }
+ updated = mService.updateConfigurationLocked(config, next);
+ }
+ }
+ if (!updated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivityLocked(null);
+ if (DEBUG_SWITCH) Slog.i(TAG,
+ "Activity config changed during resume: " + next
+ + ", new next: " + nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+ }
+ if (mMainStack) {
+ mService.setFocusedActivityLocked(next);
+ }
+ ensureActivitiesVisibleLocked(null, 0);
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return true;
+ }
+
+ try {
+ // Deliver all pending results.
+ ArrayList a = next.results;
+ if (a != null) {
+ final int N = a.size();
+ if (!next.finishing && N > 0) {
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Delivering results to " + next
+ + ": " + a);
+ next.app.thread.scheduleSendResult(next, a);
+ }
+ }
+
+ if (next.newIntents != null) {
+ next.app.thread.scheduleNewIntent(next.newIntents, next);
+ }
+
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
+ System.identityHashCode(next),
+ next.task.taskId, next.shortComponentName);
+
+ next.app.thread.scheduleResumeActivity(next,
+ mService.isNextTransitionForward());
+
+ pauseIfSleepingLocked();
+
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ next.state = lastState;
+ mResumedActivity = lastResumedActivity;
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
+ mService.mWindowManager.setAppStartingWindow(
+ next, next.packageName, next.theme,
+ next.nonLocalizedLabel,
+ next.labelRes, next.icon, null, true);
+ }
+ }
+ startSpecificActivityLocked(next, true, false);
+ return true;
+ }
+
+ // From this point on, if something goes wrong there is no way
+ // to recover the activity.
+ try {
+ next.visible = true;
+ completeResumeLocked(next);
+ } catch (Exception e) {
+ // If any exception gets thrown, toss away this
+ // activity and try the next one.
+ Slog.w(TAG, "Exception thrown during resume of " + next, e);
+ requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
+ "resume-exception");
+ return true;
+ }
+
+ // Didn't need to use the icicle, and it is now out of date.
+ next.icicle = null;
+ next.haveState = false;
+ next.stopped = false;
+
+ } else {
+ // Whoops, need to restart this activity!
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW) {
+ mService.mWindowManager.setAppStartingWindow(
+ next, next.packageName, next.theme,
+ next.nonLocalizedLabel,
+ next.labelRes, next.icon, null, true);
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
+ }
+ startSpecificActivityLocked(next, true, true);
+ }
+
+ return true;
+ }
+
+ private final void startActivityLocked(ActivityRecord r, boolean newTask,
+ boolean doResume) {
+ final int NH = mHistory.size();
+
+ int addPos = -1;
+
+ if (!newTask) {
+ // If starting in an existing task, find where that is...
+ boolean startIt = true;
+ for (int i = NH-1; i >= 0; i--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ if (p.finishing) {
+ continue;
+ }
+ if (p.task == r.task) {
+ // Here it is! Now, if this is not yet visible to the
+ // user, then just add it without starting; it will
+ // get started when the user navigates back to it.
+ addPos = i+1;
+ if (!startIt) {
+ mHistory.add(addPos, r);
+ r.inHistory = true;
+ r.task.numActivities++;
+ mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+ r.info.screenOrientation, r.fullscreen);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ return;
+ }
+ break;
+ }
+ if (p.fullscreen) {
+ startIt = false;
+ }
+ }
+ }
+
+ // Place a new activity at top of stack, so it is next to interact
+ // with the user.
+ if (addPos < 0) {
+ addPos = NH;
+ }
+
+ // If we are not placing the new activity frontmost, we do not want
+ // to deliver the onUserLeaving callback to the actual frontmost
+ // activity
+ if (addPos < NH) {
+ mUserLeaving = false;
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false");
+ }
+
+ // Slot the activity into the history stack and proceed
+ mHistory.add(addPos, r);
+ r.inHistory = true;
+ r.frontOfTask = newTask;
+ r.task.numActivities++;
+ if (NH > 0) {
+ // We want to show the starting preview window if we are
+ // switching to a new task, or the next activity's process is
+ // not currently running.
+ boolean showStartingIcon = newTask;
+ ProcessRecord proc = r.app;
+ if (proc == null) {
+ proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
+ }
+ if (proc == null || proc.thread == null) {
+ showStartingIcon = true;
+ }
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: starting " + r);
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mNoAnimActivities.add(r);
+ } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ mNoAnimActivities.remove(r);
+ } else {
+ mService.mWindowManager.prepareAppTransition(newTask
+ ? WindowManagerPolicy.TRANSIT_TASK_OPEN
+ : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ mNoAnimActivities.remove(r);
+ }
+ mService.mWindowManager.addAppToken(
+ addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
+ boolean doShow = true;
+ if (newTask) {
+ // Even though this activity is starting fresh, we still need
+ // to reset it to make sure we apply affinities to move any
+ // existing activities from other tasks in to it.
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((r.intent.getFlags()
+ &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ resetTaskIfNeededLocked(r, r);
+ doShow = topRunningNonDelayedActivityLocked(null) == r;
+ }
+ }
+ if (SHOW_APP_STARTING_PREVIEW && doShow) {
+ // Figure out if we are transitioning from another activity that is
+ // "has the same starting icon" as the next one. This allows the
+ // window manager to keep the previous window it had previously
+ // created, if it still had one.
+ ActivityRecord prev = mResumedActivity;
+ if (prev != null) {
+ // We don't want to reuse the previous starting preview if:
+ // (1) The current activity is in a different task.
+ if (prev.task != r.task) prev = null;
+ // (2) The current activity is already displayed.
+ else if (prev.nowVisible) prev = null;
+ }
+ mService.mWindowManager.setAppStartingWindow(
+ r, r.packageName, r.theme, r.nonLocalizedLabel,
+ r.labelRes, r.icon, prev, showStartingIcon);
+ }
+ } else {
+ // If this is the first activity, don't do any fancy animations,
+ // because there is nothing for it to animate on top of.
+ mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+ r.info.screenOrientation, r.fullscreen);
+ }
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ }
+
+ /**
+ * Perform a reset of the given task, if needed as part of launching it.
+ * Returns the new HistoryRecord at the top of the task.
+ */
+ private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
+ ActivityRecord newActivity) {
+ boolean forceReset = (newActivity.info.flags
+ &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
+ if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+ if ((newActivity.info.flags
+ &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
+ forceReset = true;
+ }
+ }
+
+ final TaskRecord task = taskTop.task;
+
+ // We are going to move through the history list so that we can look
+ // at each activity 'target' with 'below' either the interesting
+ // activity immediately below it in the stack or null.
+ ActivityRecord target = null;
+ int targetI = 0;
+ int taskTopI = -1;
+ int replyChainEnd = -1;
+ int lastReparentPos = -1;
+ for (int i=mHistory.size()-1; i>=-1; i--) {
+ ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
+
+ if (below != null && below.finishing) {
+ continue;
+ }
+ if (target == null) {
+ target = below;
+ targetI = i;
+ // If we were in the middle of a reply chain before this
+ // task, it doesn't appear like the root of the chain wants
+ // anything interesting, so drop it.
+ replyChainEnd = -1;
+ continue;
+ }
+
+ final int flags = target.info.flags;
+
+ final boolean finishOnTaskLaunch =
+ (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
+ final boolean allowTaskReparenting =
+ (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
+
+ if (target.task == task) {
+ // We are inside of the task being reset... we'll either
+ // finish this activity, push it out for another task,
+ // or leave it as-is. We only do this
+ // for activities that are not the root of the task (since
+ // if we finish the root, we may no longer have the task!).
+ if (taskTopI < 0) {
+ taskTopI = targetI;
+ }
+ if (below != null && below.task == task) {
+ final boolean clearWhenTaskReset =
+ (target.intent.getFlags()
+ &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
+ if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) {
+ // If this activity is sending a reply to a previous
+ // activity, we can't do anything with it now until
+ // we reach the start of the reply chain.
+ // XXX note that we are assuming the result is always
+ // to the previous activity, which is almost always
+ // the case but we really shouldn't count on.
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting
+ && target.taskAffinity != null
+ && !target.taskAffinity.equals(task.affinity)) {
+ // If this activity has an affinity for another
+ // task, then we need to move it out of here. We will
+ // move it as far out of the way as possible, to the
+ // bottom of the activity stack. This also keeps it
+ // correctly ordered with any activities we previously
+ // moved.
+ ActivityRecord p = (ActivityRecord)mHistory.get(0);
+ if (target.taskAffinity != null
+ && target.taskAffinity.equals(p.task.affinity)) {
+ // If the activity currently at the bottom has the
+ // same task affinity as the one we are moving,
+ // then merge it into the same task.
+ target.task = p.task;
+ if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ + " out to bottom task " + p.task);
+ } else {
+ mService.mCurTask++;
+ if (mService.mCurTask <= 0) {
+ mService.mCurTask = 1;
+ }
+ target.task = new TaskRecord(mService.mCurTask, target.info, null,
+ (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ target.task.affinityIntent = target.intent;
+ if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ + " out to new task " + target.task);
+ }
+ mService.mWindowManager.setAppGroupId(target, task.taskId);
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ int dstPos = 0;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+ + " out to target's task " + target.task);
+ task.numActivities--;
+ p.task = target.task;
+ target.task.numActivities++;
+ mHistory.remove(srcPos);
+ mHistory.add(dstPos, p);
+ mService.mWindowManager.moveAppToken(dstPos, p);
+ mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+ dstPos++;
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ i++;
+ }
+ if (taskTop == p) {
+ taskTop = below;
+ }
+ if (taskTopI == replyChainEnd) {
+ taskTopI = -1;
+ }
+ replyChainEnd = -1;
+ if (mMainStack) {
+ mService.addRecentTaskLocked(target.task);
+ }
+ } else if (forceReset || finishOnTaskLaunch
+ || clearWhenTaskReset) {
+ // If the activity should just be removed -- either
+ // because it asks for it, or the task should be
+ // cleared -- then finish it and anything that is
+ // part of its reply chain.
+ if (clearWhenTaskReset) {
+ // In this case, we want to finish this activity
+ // and everything above it, so be sneaky and pretend
+ // like these are all in the reply chain.
+ replyChainEnd = targetI+1;
+ while (replyChainEnd < mHistory.size() &&
+ ((ActivityRecord)mHistory.get(
+ replyChainEnd)).task == task) {
+ replyChainEnd++;
+ }
+ replyChainEnd--;
+ } else if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ ActivityRecord p = null;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(p, srcPos,
+ Activity.RESULT_CANCELED, null, "reset")) {
+ replyChainEnd--;
+ srcPos--;
+ }
+ }
+ if (taskTop == p) {
+ taskTop = below;
+ }
+ if (taskTopI == replyChainEnd) {
+ taskTopI = -1;
+ }
+ replyChainEnd = -1;
+ } else {
+ // If we were in the middle of a chain, well the
+ // activity that started it all doesn't want anything
+ // special, so leave it all as-is.
+ replyChainEnd = -1;
+ }
+ } else {
+ // Reached the bottom of the task -- any reply chain
+ // should be left as-is.
+ replyChainEnd = -1;
+ }
+
+ } else if (target.resultTo != null) {
+ // If this activity is sending a reply to a previous
+ // activity, we can't do anything with it now until
+ // we reach the start of the reply chain.
+ // XXX note that we are assuming the result is always
+ // to the previous activity, which is almost always
+ // the case but we really shouldn't count on.
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+
+ } else if (taskTopI >= 0 && allowTaskReparenting
+ && task.affinity != null
+ && task.affinity.equals(target.taskAffinity)) {
+ // We are inside of another task... if this activity has
+ // an affinity for our task, then either remove it if we are
+ // clearing or move it over to our task. Note that
+ // we currently punt on the case where we are resetting a
+ // task that is not at the top but who has activities above
+ // with an affinity to it... this is really not a normal
+ // case, and we will need to later pull that task to the front
+ // and usually at that point we will do the reset and pick
+ // up those remaining activities. (This only happens if
+ // someone starts an activity in a new task from an activity
+ // in a task that is not currently on top.)
+ if (forceReset || finishOnTaskLaunch) {
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ ActivityRecord p = null;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(p, srcPos,
+ Activity.RESULT_CANCELED, null, "reset")) {
+ taskTopI--;
+ lastReparentPos--;
+ replyChainEnd--;
+ srcPos--;
+ }
+ }
+ replyChainEnd = -1;
+ } else {
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (lastReparentPos < 0) {
+ lastReparentPos = taskTopI;
+ taskTop = p;
+ } else {
+ lastReparentPos--;
+ }
+ mHistory.remove(srcPos);
+ p.task.numActivities--;
+ p.task = task;
+ mHistory.add(lastReparentPos, p);
+ if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+ + " in to resetting task " + task);
+ task.numActivities++;
+ mService.mWindowManager.moveAppToken(lastReparentPos, p);
+ mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ }
+ replyChainEnd = -1;
+
+ // Now we've moved it in to place... but what if this is
+ // a singleTop activity and we have put it on top of another
+ // instance of the same activity? Then we drop the instance
+ // below so it remains singleTop.
+ if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
+ for (int j=lastReparentPos-1; j>=0; j--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(j);
+ if (p.finishing) {
+ continue;
+ }
+ if (p.intent.getComponent().equals(target.intent.getComponent())) {
+ if (finishActivityLocked(p, j,
+ Activity.RESULT_CANCELED, null, "replace")) {
+ taskTopI--;
+ lastReparentPos--;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ target = below;
+ targetI = i;
+ }
+
+ return taskTop;
+ }
+
+ /**
+ * Perform clear operation as requested by
+ * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
+ * stack to the given task, then look for
+ * an instance of that activity in the stack and, if found, finish all
+ * activities on top of it and return the instance.
+ *
+ * @param newR Description of the new activity being started.
+ * @return Returns the old activity that should be continue to be used,
+ * or null if none was found.
+ */
+ private final ActivityRecord performClearTaskLocked(int taskId,
+ ActivityRecord newR, int launchFlags, boolean doClear) {
+ int i = mHistory.size();
+
+ // First find the requested task.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId == taskId) {
+ i++;
+ break;
+ }
+ }
+
+ // Now clear it.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.task.taskId != taskId) {
+ return null;
+ }
+ if (r.realActivity.equals(newR.realActivity)) {
+ // Here it is! Now finish everything in front...
+ ActivityRecord ret = r;
+ if (doClear) {
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i--;
+ }
+ }
+ }
+
+ // Finally, if this is a normal launch mode (that is, not
+ // expecting onNewIntent()), then we will finish the current
+ // instance of the activity so a new fresh one can be started.
+ if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+ && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
+ if (!ret.finishing) {
+ int index = indexOfTokenLocked(ret);
+ if (index >= 0) {
+ finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
+ null, "clear");
+ }
+ return null;
+ }
+ }
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Find the activity in the history stack within the given task. Returns
+ * the index within the history at which it's found, or < 0 if not found.
+ */
+ private final int findActivityInHistoryLocked(ActivityRecord r, int task) {
+ int i = mHistory.size();
+ while (i > 0) {
+ i--;
+ ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
+ if (candidate.task.taskId != task) {
+ break;
+ }
+ if (candidate.realActivity.equals(r.realActivity)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Reorder the history stack so that the activity at the given index is
+ * brought to the front.
+ */
+ private final ActivityRecord moveActivityToFrontLocked(int where) {
+ ActivityRecord newTop = (ActivityRecord)mHistory.remove(where);
+ int top = mHistory.size();
+ ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
+ mHistory.add(top, newTop);
+ oldTop.frontOfTask = false;
+ newTop.frontOfTask = true;
+ return newTop;
+ }
+
+ final int startActivityLocked(IApplicationThread caller,
+ Intent intent, String resolvedType,
+ Uri[] grantedUriPermissions,
+ int grantedMode, ActivityInfo aInfo, IBinder resultTo,
+ String resultWho, int requestCode,
+ int callingPid, int callingUid, boolean onlyIfNeeded,
+ boolean componentSpecified) {
+
+ int err = START_SUCCESS;
+
+ ProcessRecord callerApp = null;
+ if (caller != null) {
+ callerApp = mService.getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ callingPid = callerApp.pid;
+ callingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + callingPid + ") when starting: "
+ + intent.toString());
+ err = START_PERMISSION_DENIED;
+ }
+ }
+
+ if (err == START_SUCCESS) {
+ Slog.i(TAG, "Starting: " + intent + " from pid "
+ + (callerApp != null ? callerApp.pid : callingPid));
+ }
+
+ ActivityRecord sourceRecord = null;
+ ActivityRecord resultRecord = null;
+ if (resultTo != null) {
+ int index = indexOfTokenLocked(resultTo);
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Sending result to " + resultTo + " (index " + index + ")");
+ if (index >= 0) {
+ sourceRecord = (ActivityRecord)mHistory.get(index);
+ if (requestCode >= 0 && !sourceRecord.finishing) {
+ resultRecord = sourceRecord;
+ }
+ }
+ }
+
+ int launchFlags = intent.getFlags();
+
+ if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
+ && sourceRecord != null) {
+ // Transfer the result target from the source activity to the new
+ // one being started, including any failures.
+ if (requestCode >= 0) {
+ return START_FORWARD_AND_REQUEST_CONFLICT;
+ }
+ resultRecord = sourceRecord.resultTo;
+ resultWho = sourceRecord.resultWho;
+ requestCode = sourceRecord.requestCode;
+ sourceRecord.resultTo = null;
+ if (resultRecord != null) {
+ resultRecord.removeResultsLocked(
+ sourceRecord, resultWho, requestCode);
+ }
+ }
+
+ if (err == START_SUCCESS && intent.getComponent() == null) {
+ // We couldn't find a class that can handle the given Intent.
+ // That's the end of that!
+ err = START_INTENT_NOT_RESOLVED;
+ }
+
+ if (err == START_SUCCESS && aInfo == null) {
+ // We couldn't find the specific class specified in the Intent.
+ // Also the end of the line.
+ err = START_CLASS_NOT_FOUND;
+ }
+
+ if (err != START_SUCCESS) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ return err;
+ }
+
+ final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,
+ callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + aInfo.permission;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ if (mMainStack) {
+ if (mService.mController != null) {
+ boolean abort = false;
+ try {
+ // The Intent we give to the watcher has the extra data
+ // stripped off, since it can contain private information.
+ Intent watchIntent = intent.cloneFilter();
+ abort = !mService.mController.activityStarting(watchIntent,
+ aInfo.applicationInfo.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ }
+
+ if (abort) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ // We pretend to the caller that it was really started, but
+ // they will just get a cancel result.
+ return START_SUCCESS;
+ }
+ }
+ }
+
+ ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
+ intent, resolvedType, aInfo, mService.mConfiguration,
+ resultRecord, resultWho, requestCode, componentSpecified);
+
+ if (mMainStack) {
+ if (mResumedActivity == null
+ || mResumedActivity.info.applicationInfo.uid != callingUid) {
+ if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+ PendingActivityLaunch pal = new PendingActivityLaunch();
+ pal.r = r;
+ pal.sourceRecord = sourceRecord;
+ pal.grantedUriPermissions = grantedUriPermissions;
+ pal.grantedMode = grantedMode;
+ pal.onlyIfNeeded = onlyIfNeeded;
+ mService.mPendingActivityLaunches.add(pal);
+ return START_SWITCHES_CANCELED;
+ }
+ }
+
+ if (mService.mDidAppSwitch) {
+ // This is the second allowed switch since we stopped switches,
+ // so now just generally allow switches. Use case: user presses
+ // home (switches disabled, switch to home, mDidAppSwitch now true);
+ // user taps a home icon (coming from home so allowed, we hit here
+ // and now allow anyone to switch again).
+ mService.mAppSwitchesAllowedTime = 0;
+ } else {
+ mService.mDidAppSwitch = true;
+ }
+
+ mService.doPendingActivityLaunchesLocked(false);
+ }
+
+ return startActivityUncheckedLocked(r, sourceRecord,
+ grantedUriPermissions, grantedMode, onlyIfNeeded, true);
+ }
+
+ final int startActivityUncheckedLocked(ActivityRecord r,
+ ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
+ int grantedMode, boolean onlyIfNeeded, boolean doResume) {
+ final Intent intent = r.intent;
+ final int callingUid = r.launchedFromUid;
+
+ int launchFlags = intent.getFlags();
+
+ // We'll invoke onUserLeaving before onPause only if the launching
+ // activity did not explicitly state that this is an automated launch.
+ mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+ if (DEBUG_USER_LEAVING) Slog.v(TAG,
+ "startActivity() => mUserLeaving=" + mUserLeaving);
+
+ // If the caller has asked not to resume at this point, we make note
+ // of this in the record so that we can skip it when trying to find
+ // the top running activity.
+ if (!doResume) {
+ r.delayedResume = true;
+ }
+
+ ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
+ != 0 ? r : null;
+
+ // If the onlyIfNeeded flag is set, then we can do this if the activity
+ // being launched is the same as the one making the call... or, as
+ // a special case, if we do not know the caller then we count the
+ // current top activity as the caller.
+ if (onlyIfNeeded) {
+ ActivityRecord checkedCaller = sourceRecord;
+ if (checkedCaller == null) {
+ checkedCaller = topRunningNonDelayedActivityLocked(notTop);
+ }
+ if (!checkedCaller.realActivity.equals(r.realActivity)) {
+ // Caller is not the same as launcher, so always needed.
+ onlyIfNeeded = false;
+ }
+ }
+
+ if (sourceRecord == null) {
+ // This activity is not being started from another... in this
+ // case we -always- start a new task.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
+ + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // The original activity who is starting us is running as a single
+ // instance... this new activity it is starting must go on its
+ // own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+ // The activity being started is a single instance... it always
+ // gets launched into its own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+
+ if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ // For whatever reason this activity is being launched into a new
+ // task... yet the caller has requested a result back. Well, that
+ // is pretty messed up, so instead immediately send back a cancel
+ // and let the new task continue launched as normal without a
+ // dependency on its originator.
+ Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+ sendActivityResultLocked(-1,
+ r.resultTo, r.resultWho, r.requestCode,
+ Activity.RESULT_CANCELED, null);
+ r.resultTo = null;
+ }
+
+ boolean addingToTask = false;
+ if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
+ (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // If bring to front is requested, and no result is requested, and
+ // we can find a task that was started with this same
+ // component, then instead of launching bring that one to the front.
+ if (r.resultTo == null) {
+ // See if there is a task to bring to the front. If this is
+ // a SINGLE_INSTANCE activity, there can be one and only one
+ // instance of it in the history, and it is always in its own
+ // unique task, so we do a special search.
+ ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
+ ? findTaskLocked(intent, r.info)
+ : findActivityLocked(intent, r.info);
+ if (taskTop != null) {
+ if (taskTop.task.intent == null) {
+ // This task was started because of movement of
+ // the activity based on affinity... now that we
+ // are actually launching it, we can assign the
+ // base intent.
+ taskTop.task.setIntent(intent, r.info);
+ }
+ // If the target task is not in the front, then we need
+ // to bring it to the front... except... well, with
+ // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
+ // to have the same behavior as if a new instance was
+ // being started, which means not bringing it to the front
+ // if the caller is not itself in the front.
+ ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
+ if (curTop != null && curTop.task != taskTop.task) {
+ r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+ boolean callerAtFront = sourceRecord == null
+ || curTop.task == sourceRecord.task;
+ if (callerAtFront) {
+ // We really do want to push this one into the
+ // user's face, right now.
+ moveTaskToFrontLocked(taskTop.task, r);
+ }
+ }
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ taskTop = resetTaskIfNeededLocked(taskTop, r);
+ }
+ if (onlyIfNeeded) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it! And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return START_RETURN_INTENT_TO_CALLER;
+ }
+ if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // In this situation we want to remove all activities
+ // from the task up to the one being started. In most
+ // cases this means we are resetting the task to its
+ // initial state.
+ ActivityRecord top = performClearTaskLocked(
+ taskTop.task.taskId, r, launchFlags, true);
+ if (top != null) {
+ if (top.frontOfTask) {
+ // Activity aliases may mean we use different
+ // intents for the top activity, so make sure
+ // the task now has the identity of the new
+ // intent.
+ top.task.setIntent(r.intent, r.info);
+ }
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ } else {
+ // A special case: we need to
+ // start the activity because it is not currently
+ // running, and the caller has asked to clear the
+ // current task to have this activity at the top.
+ addingToTask = true;
+ // Now pretend like this activity is being started
+ // by the top of its task, so it is put in the
+ // right place.
+ sourceRecord = taskTop;
+ }
+ } else if (r.realActivity.equals(taskTop.task.realActivity)) {
+ // In this case the top activity on the task is the
+ // same as the one being launched, so we take that
+ // as a request to bring the task to the foreground.
+ // If the top activity in the task is the root
+ // activity, deliver this new intent to it if it
+ // desires.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ && taskTop.realActivity.equals(r.realActivity)) {
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
+ if (taskTop.frontOfTask) {
+ taskTop.task.setIntent(r.intent, r.info);
+ }
+ taskTop.deliverNewIntentLocked(callingUid, r.intent);
+ } else if (!r.intent.filterEquals(taskTop.task.intent)) {
+ // In this case we are launching the root activity
+ // of the task, but with a different intent. We
+ // should start a new instance on top.
+ addingToTask = true;
+ sourceRecord = taskTop;
+ }
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+ // In this case an activity is being launched in to an
+ // existing task, without resetting that task. This
+ // is typically the situation of launching an activity
+ // from a notification or shortcut. We want to place
+ // the new activity on top of the current task.
+ addingToTask = true;
+ sourceRecord = taskTop;
+ } else if (!taskTop.task.rootWasReset) {
+ // In this case we are launching in to an existing task
+ // that has not yet been started from its front door.
+ // The current task has been brought to the front.
+ // Ideally, we'd probably like to place this new task
+ // at the bottom of its stack, but that's a little hard
+ // to do with the current organization of the code so
+ // for now we'll just drop it.
+ taskTop.task.setIntent(r.intent, r.info);
+ }
+ if (!addingToTask) {
+ // We didn't do anything... but it was needed (a.k.a., client
+ // don't use that intent!) And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return START_TASK_TO_FRONT;
+ }
+ }
+ }
+ }
+
+ //String uri = r.intent.toURI();
+ //Intent intent2 = new Intent(uri);
+ //Slog.i(TAG, "Given intent: " + r.intent);
+ //Slog.i(TAG, "URI is: " + uri);
+ //Slog.i(TAG, "To intent: " + intent2);
+
+ if (r.packageName != null) {
+ // If the activity being launched is the same as the one currently
+ // at the top, then we need to check if it should only be launched
+ // once.
+ ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
+ if (top != null && r.resultTo == null) {
+ if (top.realActivity.equals(r.realActivity)) {
+ if (top.app != null && top.app.thread != null) {
+ if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+ logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ if (onlyIfNeeded) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it!
+ return START_RETURN_INTENT_TO_CALLER;
+ }
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ return START_DELIVERED_TO_TOP;
+ }
+ }
+ }
+ }
+
+ } else {
+ if (r.resultTo != null) {
+ sendActivityResultLocked(-1,
+ r.resultTo, r.resultWho, r.requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ return START_CLASS_NOT_FOUND;
+ }
+
+ boolean newTask = false;
+
+ // Should this be considered a new task?
+ if (r.resultTo == null && !addingToTask
+ && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ // todo: should do better management of integers.
+ mService.mCurTask++;
+ if (mService.mCurTask <= 0) {
+ mService.mCurTask = 1;
+ }
+ r.task = new TaskRecord(mService.mCurTask, r.info, intent,
+ (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new task " + r.task);
+ newTask = true;
+ if (mMainStack) {
+ mService.addRecentTaskLocked(r.task);
+ }
+
+ } else if (sourceRecord != null) {
+ if (!addingToTask &&
+ (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+ // In this case, we are adding the activity to an existing
+ // task, but the caller has asked to clear that task if the
+ // activity is already running.
+ ActivityRecord top = performClearTaskLocked(
+ sourceRecord.task.taskId, r, launchFlags, true);
+ if (top != null) {
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return START_DELIVERED_TO_TOP;
+ }
+ } else if (!addingToTask &&
+ (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+ // In this case, we are launching an activity in our own task
+ // that may already be running somewhere in the history, and
+ // we want to shuffle it to the front of the stack if so.
+ int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
+ if (where >= 0) {
+ ActivityRecord top = moveActivityToFrontLocked(where);
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return START_DELIVERED_TO_TOP;
+ }
+ }
+ // An existing activity is starting this new activity, so we want
+ // to keep the new one in the same task as the one that is starting
+ // it.
+ r.task = sourceRecord.task;
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in existing task " + r.task);
+
+ } else {
+ // This not being started from an existing activity, and not part
+ // of a new task... just put it in the top task, though these days
+ // this case should never happen.
+ final int N = mHistory.size();
+ ActivityRecord prev =
+ N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
+ r.task = prev != null
+ ? prev.task
+ : new TaskRecord(mService.mCurTask, r.info, intent,
+ (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new guessed " + r.task);
+ }
+
+ if (grantedUriPermissions != null && callingUid > 0) {
+ for (int i=0; i<grantedUriPermissions.length; i++) {
+ mService.grantUriPermissionLocked(callingUid, r.packageName,
+ grantedUriPermissions[i], grantedMode, r.getUriPermissionsLocked());
+ }
+ }
+
+ mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ intent, r.getUriPermissionsLocked());
+
+ if (newTask) {
+ EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
+ }
+ logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
+ startActivityLocked(r, newTask, doResume);
+ return START_SUCCESS;
+ }
+
+ final int startActivityMayWait(IApplicationThread caller,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded,
+ boolean debug, WaitResult outResult, Configuration config) {
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, resolvedType,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+
+ if (aInfo != null) {
+ // Store the found target back into the intent, because now that
+ // we have it we never want to do this again. For example, if the
+ // user navigates back to this point in the history, we should
+ // always restart the exact same activity.
+ intent.setComponent(new ComponentName(
+ aInfo.applicationInfo.packageName, aInfo.name));
+
+ // Don't debug things in the system process
+ if (debug) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setDebugApp(aInfo.processName, true, false);
+ }
+ }
+ }
+
+ synchronized (mService) {
+ int callingPid;
+ int callingUid;
+ if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+
+ mConfigWillChange = config != null
+ && mService.mConfiguration.diff(config) != 0;
+ if (DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Starting activity when config will change = " + mConfigWillChange);
+
+ final long origId = Binder.clearCallingIdentity();
+
+ if (mMainStack && aInfo != null &&
+ (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+ // This may be a heavy-weight process! Check to see if we already
+ // have another, different heavy-weight process running.
+ if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+ if (mService.mHeavyWeightProcess != null &&
+ (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
+ !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
+ int realCallingPid = callingPid;
+ int realCallingUid = callingUid;
+ if (caller != null) {
+ ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ realCallingPid = callerApp.pid;
+ realCallingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + realCallingPid + ") when starting: "
+ + intent.toString());
+ return START_PERMISSION_DENIED;
+ }
+ }
+
+ IIntentSender target = mService.getIntentSenderLocked(
+ IActivityManager.INTENT_SENDER_ACTIVITY, "android",
+ realCallingUid, null, null, 0, intent,
+ resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT);
+
+ Intent newIntent = new Intent();
+ if (requestCode >= 0) {
+ // Caller is requesting a result.
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+ new IntentSender(target));
+ if (mService.mHeavyWeightProcess.activities.size() > 0) {
+ ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
+ hist.packageName);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
+ hist.task.taskId);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+ aInfo.packageName);
+ newIntent.setFlags(intent.getFlags());
+ newIntent.setClassName("android",
+ HeavyWeightSwitcherActivity.class.getName());
+ intent = newIntent;
+ resolvedType = null;
+ caller = null;
+ callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
+ componentSpecified = true;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, null,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+ }
+ }
+ }
+
+ int res = startActivityLocked(caller, intent, resolvedType,
+ grantedUriPermissions, grantedMode, aInfo,
+ resultTo, resultWho, requestCode, callingPid, callingUid,
+ onlyIfNeeded, componentSpecified);
+
+ if (mConfigWillChange && mMainStack) {
+ // If the caller also wants to switch to a new configuration,
+ // do so now. This allows a clean switch, as we are waiting
+ // for the current activity to pause (so we will not destroy
+ // it), and have not yet started the next activity.
+ mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+ "updateConfiguration()");
+ mConfigWillChange = false;
+ if (DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Updating to new configuration after starting activity.");
+ mService.updateConfigurationLocked(config, null);
+ }
+
+ Binder.restoreCallingIdentity(origId);
+
+ if (outResult != null) {
+ outResult.result = res;
+ if (res == IActivityManager.START_SUCCESS) {
+ mWaitingActivityLaunched.add(outResult);
+ do {
+ try {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ } else if (res == IActivityManager.START_TASK_TO_FRONT) {
+ ActivityRecord r = this.topRunningActivityLocked(null);
+ if (r.nowVisible) {
+ outResult.timeout = false;
+ outResult.who = new ComponentName(r.info.packageName, r.info.name);
+ outResult.totalTime = 0;
+ outResult.thisTime = 0;
+ } else {
+ outResult.thisTime = SystemClock.uptimeMillis();
+ mWaitingActivityVisible.add(outResult);
+ do {
+ try {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ }
+ }
+ }
+
+ return res;
+ }
+ }
+
+ void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
+ long thisTime, long totalTime) {
+ for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
+ WaitResult w = mWaitingActivityLaunched.get(i);
+ w.timeout = timeout;
+ if (r != null) {
+ w.who = new ComponentName(r.info.packageName, r.info.name);
+ }
+ w.thisTime = thisTime;
+ w.totalTime = totalTime;
+ }
+ mService.notifyAll();
+ }
+
+ void reportActivityVisibleLocked(ActivityRecord r) {
+ for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
+ WaitResult w = mWaitingActivityVisible.get(i);
+ w.timeout = false;
+ if (r != null) {
+ w.who = new ComponentName(r.info.packageName, r.info.name);
+ }
+ w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
+ w.thisTime = w.totalTime;
+ }
+ mService.notifyAll();
+ }
+
+ void sendActivityResultLocked(int callingUid, ActivityRecord r,
+ String resultWho, int requestCode, int resultCode, Intent data) {
+
+ if (callingUid > 0) {
+ mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ data, r.getUriPermissionsLocked());
+ }
+
+ if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+ + " : who=" + resultWho + " req=" + requestCode
+ + " res=" + resultCode + " data=" + data);
+ if (mResumedActivity == r && r.app != null && r.app.thread != null) {
+ try {
+ ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+ list.add(new ResultInfo(resultWho, requestCode,
+ resultCode, data));
+ r.app.thread.scheduleSendResult(r, list);
+ return;
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception thrown sending result to " + r, e);
+ }
+ }
+
+ r.addResultLocked(null, resultWho, requestCode, resultCode, data);
+ }
+
+ private final void stopActivityLocked(ActivityRecord r) {
+ if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+ || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
+ if (!r.finishing) {
+ requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+ "no-history");
+ }
+ } else if (r.app != null && r.app.thread != null) {
+ if (mMainStack) {
+ if (mService.mFocusedActivity == r) {
+ mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+ }
+ }
+ r.resumeKeyDispatchingLocked();
+ try {
+ r.stopped = false;
+ r.state = ActivityState.STOPPING;
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Stopping visible=" + r.visible + " for " + r);
+ if (!r.visible) {
+ mService.mWindowManager.setAppVisibility(r, false);
+ }
+ r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
+ } catch (Exception e) {
+ // Maybe just ignore exceptions here... if the process
+ // has crashed, our death notification will clean things
+ // up.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ // Just in case, assume it to be stopped.
+ r.stopped = true;
+ r.state = ActivityState.STOPPED;
+ if (r.configDestroy) {
+ destroyActivityLocked(r, true);
+ }
+ }
+ }
+ }
+
+ final ArrayList<ActivityRecord> processStoppingActivitiesLocked(
+ boolean remove) {
+ int N = mStoppingActivities.size();
+ if (N <= 0) return null;
+
+ ArrayList<ActivityRecord> stops = null;
+
+ final boolean nowVisible = mResumedActivity != null
+ && mResumedActivity.nowVisible
+ && !mResumedActivity.waitingVisible;
+ for (int i=0; i<N; i++) {
+ ActivityRecord s = mStoppingActivities.get(i);
+ if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
+ + nowVisible + " waitingVisible=" + s.waitingVisible
+ + " finishing=" + s.finishing);
+ if (s.waitingVisible && nowVisible) {
+ mWaitingVisibleActivities.remove(s);
+ s.waitingVisible = false;
+ if (s.finishing) {
+ // If this activity is finishing, it is sitting on top of
+ // everyone else but we now know it is no longer needed...
+ // so get rid of it. Otherwise, we need to go through the
+ // normal flow and hide it once we determine that it is
+ // hidden by the activities in front of it.
+ if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
+ mService.mWindowManager.setAppVisibility(s, false);
+ }
+ }
+ if (!s.waitingVisible && remove) {
+ if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
+ if (stops == null) {
+ stops = new ArrayList<ActivityRecord>();
+ }
+ stops.add(s);
+ mStoppingActivities.remove(i);
+ N--;
+ i--;
+ }
+ }
+
+ return stops;
+ }
+
+ final void activityIdleInternal(IBinder token, boolean fromTimeout,
+ Configuration config) {
+ if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
+
+ ArrayList<ActivityRecord> stops = null;
+ ArrayList<ActivityRecord> finishes = null;
+ ArrayList<ActivityRecord> thumbnails = null;
+ int NS = 0;
+ int NF = 0;
+ int NT = 0;
+ IApplicationThread sendThumbnail = null;
+ boolean booting = false;
+ boolean enableScreen = false;
+
+ synchronized (mService) {
+ if (token != null) {
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
+ }
+
+ // Get the activity record.
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+
+ if (fromTimeout) {
+ reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
+ }
+
+ // This is a hack to semi-deal with a race condition
+ // in the client where it can be constructed with a
+ // newer configuration from when we asked it to launch.
+ // We'll update with whatever configuration it now says
+ // it used to launch.
+ if (config != null) {
+ r.configuration = config;
+ }
+
+ // No longer need to keep the device awake.
+ if (mResumedActivity == r && mLaunchingActivity.isHeld()) {
+ mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ mLaunchingActivity.release();
+ }
+
+ // We are now idle. If someone is waiting for a thumbnail from
+ // us, we can now deliver.
+ r.idle = true;
+ mService.scheduleAppGcsLocked();
+ if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
+ sendThumbnail = r.app.thread;
+ r.thumbnailNeeded = false;
+ }
+
+ // If this activity is fullscreen, set up to hide those under it.
+
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
+ ensureActivitiesVisibleLocked(null, 0);
+
+ //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
+ if (mMainStack) {
+ if (!mService.mBooted && !fromTimeout) {
+ mService.mBooted = true;
+ enableScreen = true;
+ }
+ }
+
+ } else if (fromTimeout) {
+ reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
+ }
+
+ // Atomically retrieve all of the other things to do.
+ stops = processStoppingActivitiesLocked(true);
+ NS = stops != null ? stops.size() : 0;
+ if ((NF=mFinishingActivities.size()) > 0) {
+ finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
+ mFinishingActivities.clear();
+ }
+ if ((NT=mService.mCancelledThumbnails.size()) > 0) {
+ thumbnails = new ArrayList<ActivityRecord>(mService.mCancelledThumbnails);
+ mService.mCancelledThumbnails.clear();
+ }
+
+ if (mMainStack) {
+ booting = mService.mBooting;
+ mService.mBooting = false;
+ }
+ }
+
+ int i;
+
+ // Send thumbnail if requested.
+ if (sendThumbnail != null) {
+ try {
+ sendThumbnail.requestThumbnail(token);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
+ mService.sendPendingThumbnail(null, token, null, null, true);
+ }
+ }
+
+ // Stop any activities that are scheduled to do so but have been
+ // waiting for the next one to start.
+ for (i=0; i<NS; i++) {
+ ActivityRecord r = (ActivityRecord)stops.get(i);
+ synchronized (mService) {
+ if (r.finishing) {
+ finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
+ } else {
+ stopActivityLocked(r);
+ }
+ }
+ }
+
+ // Finish any activities that are scheduled to do so but have been
+ // waiting for the next one to start.
+ for (i=0; i<NF; i++) {
+ ActivityRecord r = (ActivityRecord)finishes.get(i);
+ synchronized (mService) {
+ destroyActivityLocked(r, true);
+ }
+ }
+
+ // Report back to any thumbnail receivers.
+ for (i=0; i<NT; i++) {
+ ActivityRecord r = (ActivityRecord)thumbnails.get(i);
+ mService.sendPendingThumbnail(r, null, null, null, true);
+ }
+
+ if (booting) {
+ mService.finishBooting();
+ }
+
+ mService.trimApplications();
+ //dump();
+ //mWindowManager.dump();
+
+ if (enableScreen) {
+ mService.enableScreenAfterBoot();
+ }
+ }
+
+ /**
+ * @return Returns true if the activity is being finished, false if for
+ * some reason it is being left as-is.
+ */
+ final boolean requestFinishActivityLocked(IBinder token, int resultCode,
+ Intent resultData, String reason) {
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Finishing activity: token=" + token
+ + ", result=" + resultCode + ", data=" + resultData);
+
+ int index = indexOfTokenLocked(token);
+ if (index < 0) {
+ return false;
+ }
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+
+ // Is this the last activity left?
+ boolean lastActivity = true;
+ for (int i=mHistory.size()-1; i>=0; i--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ if (!p.finishing && p != r) {
+ lastActivity = false;
+ break;
+ }
+ }
+
+ // If this is the last activity, but it is the home activity, then
+ // just don't finish it.
+ if (lastActivity) {
+ if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
+ return false;
+ }
+ }
+
+ finishActivityLocked(r, index, resultCode, resultData, reason);
+ return true;
+ }
+
+ /**
+ * @return Returns true if this activity has been removed from the history
+ * list, or false if it is still in the list and will be removed later.
+ */
+ final boolean finishActivityLocked(ActivityRecord r, int index,
+ int resultCode, Intent resultData, String reason) {
+ if (r.finishing) {
+ Slog.w(TAG, "Duplicate finish request for " + r);
+ return false;
+ }
+
+ r.finishing = true;
+ EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName, reason);
+ r.task.numActivities--;
+ if (index < (mHistory.size()-1)) {
+ ActivityRecord next = (ActivityRecord)mHistory.get(index+1);
+ if (next.task == r.task) {
+ if (r.frontOfTask) {
+ // The next activity is now the front of the task.
+ next.frontOfTask = true;
+ }
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // If the caller asked that this activity (and all above it)
+ // be cleared when the task is reset, don't lose that information,
+ // but propagate it up to the next activity.
+ next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ }
+ }
+ }
+
+ r.pauseKeyDispatchingLocked();
+ if (mMainStack) {
+ if (mService.mFocusedActivity == r) {
+ mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+ }
+ }
+
+ // send the result
+ ActivityRecord resultTo = r.resultTo;
+ if (resultTo != null) {
+ if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
+ + " who=" + r.resultWho + " req=" + r.requestCode
+ + " res=" + resultCode + " data=" + resultData);
+ if (r.info.applicationInfo.uid > 0) {
+ mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
+ resultTo.packageName, resultData,
+ resultTo.getUriPermissionsLocked());
+ }
+ resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
+ resultData);
+ r.resultTo = null;
+ }
+ else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
+
+ // Make sure this HistoryRecord is not holding on to other resources,
+ // because clients have remote IPC references to this object so we
+ // can't assume that will go away and want to avoid circular IPC refs.
+ r.results = null;
+ r.pendingResults = null;
+ r.newIntents = null;
+ r.icicle = null;
+
+ if (mService.mPendingThumbnails.size() > 0) {
+ // There are clients waiting to receive thumbnails so, in case
+ // this is an activity that someone is waiting for, add it
+ // to the pending list so we can correctly update the clients.
+ mService.mCancelledThumbnails.add(r);
+ }
+
+ if (mResumedActivity == r) {
+ boolean endTask = index <= 0
+ || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare close transition: finishing " + r);
+ mService.mWindowManager.prepareAppTransition(endTask
+ ? WindowManagerPolicy.TRANSIT_TASK_CLOSE
+ : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
+
+ // Tell window manager to prepare for this one to be removed.
+ mService.mWindowManager.setAppVisibility(r, false);
+
+ if (mPausingActivity == null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
+ startPausingLocked(false, false);
+ }
+
+ } else if (r.state != ActivityState.PAUSING) {
+ // If the activity is PAUSING, we will complete the finish once
+ // it is done pausing; else we can just directly finish it here.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
+ return finishCurrentActivityLocked(r, index,
+ FINISH_AFTER_PAUSE) == null;
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
+ }
+
+ return false;
+ }
+
+ private static final int FINISH_IMMEDIATELY = 0;
+ private static final int FINISH_AFTER_PAUSE = 1;
+ private static final int FINISH_AFTER_VISIBLE = 2;
+
+ private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
+ int mode) {
+ final int index = indexOfTokenLocked(r);
+ if (index < 0) {
+ return null;
+ }
+
+ return finishCurrentActivityLocked(r, index, mode);
+ }
+
+ private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
+ int index, int mode) {
+ // First things first: if this activity is currently visible,
+ // and the resumed activity is not yet visible, then hold off on
+ // finishing until the resumed one becomes visible.
+ if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
+ if (!mStoppingActivities.contains(r)) {
+ mStoppingActivities.add(r);
+ if (mStoppingActivities.size() > 3) {
+ // If we already have a few activities waiting to stop,
+ // then give up on things going idle and start clearing
+ // them out.
+ Message msg = Message.obtain();
+ msg.what = IDLE_NOW_MSG;
+ mHandler.sendMessage(msg);
+ }
+ }
+ r.state = ActivityState.STOPPING;
+ mService.updateOomAdjLocked();
+ return r;
+ }
+
+ // make sure the record is cleaned out of other places.
+ mStoppingActivities.remove(r);
+ mWaitingVisibleActivities.remove(r);
+ if (mResumedActivity == r) {
+ mResumedActivity = null;
+ }
+ final ActivityState prevState = r.state;
+ r.state = ActivityState.FINISHING;
+
+ if (mode == FINISH_IMMEDIATELY
+ || prevState == ActivityState.STOPPED
+ || prevState == ActivityState.INITIALIZING) {
+ // If this activity is already stopped, we can just finish
+ // it right now.
+ return destroyActivityLocked(r, true) ? null : r;
+ } else {
+ // Need to go through the full pause cycle to get this
+ // activity into the stopped state and then finish it.
+ if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
+ mFinishingActivities.add(r);
+ resumeTopActivityLocked(null);
+ }
+ return r;
+ }
+
+ /**
+ * Perform the common clean-up of an activity record. This is called both
+ * as part of destroyActivityLocked() (when destroying the client-side
+ * representation) and cleaning things up as a result of its hosting
+ * processing going away, in which case there is no remaining client-side
+ * state to destroy so only the cleanup here is needed.
+ */
+ final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) {
+ if (mResumedActivity == r) {
+ mResumedActivity = null;
+ }
+ if (mService.mFocusedActivity == r) {
+ mService.mFocusedActivity = null;
+ }
+
+ r.configDestroy = false;
+ r.frozenBeforeDestroy = false;
+
+ // Make sure this record is no longer in the pending finishes list.
+ // This could happen, for example, if we are trimming activities
+ // down to the max limit while they are still waiting to finish.
+ mFinishingActivities.remove(r);
+ mWaitingVisibleActivities.remove(r);
+
+ // Remove any pending results.
+ if (r.finishing && r.pendingResults != null) {
+ for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
+ PendingIntentRecord rec = apr.get();
+ if (rec != null) {
+ mService.cancelIntentSenderLocked(rec, false);
+ }
+ }
+ r.pendingResults = null;
+ }
+
+ if (cleanServices) {
+ cleanUpActivityServicesLocked(r);
+ }
+
+ if (mService.mPendingThumbnails.size() > 0) {
+ // There are clients waiting to receive thumbnails so, in case
+ // this is an activity that someone is waiting for, add it
+ // to the pending list so we can correctly update the clients.
+ mService.mCancelledThumbnails.add(r);
+ }
+
+ // Get rid of any pending idle timeouts.
+ mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+ }
+
+ private final void removeActivityFromHistoryLocked(ActivityRecord r) {
+ if (r.state != ActivityState.DESTROYED) {
+ mHistory.remove(r);
+ r.inHistory = false;
+ r.state = ActivityState.DESTROYED;
+ mService.mWindowManager.removeAppToken(r);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ cleanUpActivityServicesLocked(r);
+ r.removeUriPermissionsLocked();
+ }
+ }
+
+ /**
+ * Perform clean-up of service connections in an activity record.
+ */
+ final void cleanUpActivityServicesLocked(ActivityRecord r) {
+ // Throw away any services that have been bound by this activity.
+ if (r.connections != null) {
+ Iterator<ConnectionRecord> it = r.connections.iterator();
+ while (it.hasNext()) {
+ ConnectionRecord c = it.next();
+ mService.removeConnectionLocked(c, null, r);
+ }
+ r.connections = null;
+ }
+ }
+
+ /**
+ * Destroy the current CLIENT SIDE instance of an activity. This may be
+ * called both when actually finishing an activity, or when performing
+ * a configuration switch where we destroy the current client-side object
+ * but then create a new client-side object for this same HistoryRecord.
+ */
+ final boolean destroyActivityLocked(ActivityRecord r,
+ boolean removeFromApp) {
+ if (DEBUG_SWITCH) Slog.v(
+ TAG, "Removing activity: token=" + r
+ + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+ EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+
+ boolean removedFromHistory = false;
+
+ cleanUpActivityLocked(r, false);
+
+ final boolean hadApp = r.app != null;
+
+ if (hadApp) {
+ if (removeFromApp) {
+ int idx = r.app.activities.indexOf(r);
+ if (idx >= 0) {
+ r.app.activities.remove(idx);
+ }
+ if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
+ mService.mHeavyWeightProcess = null;
+ mService.mHandler.sendEmptyMessage(
+ ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
+ if (r.app.activities.size() == 0) {
+ // No longer have activities, so update location in
+ // LRU list.
+ mService.updateLruProcessLocked(r.app, true, false);
+ }
+ }
+
+ boolean skipDestroy = false;
+
+ try {
+ if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
+ r.app.thread.scheduleDestroyActivity(r, r.finishing,
+ r.configChangeFlags);
+ } catch (Exception e) {
+ // We can just ignore exceptions here... if the process
+ // has crashed, our death notification will clean things
+ // up.
+ //Slog.w(TAG, "Exception thrown during finish", e);
+ if (r.finishing) {
+ removeActivityFromHistoryLocked(r);
+ removedFromHistory = true;
+ skipDestroy = true;
+ }
+ }
+
+ r.app = null;
+ r.nowVisible = false;
+
+ if (r.finishing && !skipDestroy) {
+ r.state = ActivityState.DESTROYING;
+ Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
+ msg.obj = r;
+ mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
+ } else {
+ r.state = ActivityState.DESTROYED;
+ }
+ } else {
+ // remove this record from the history.
+ if (r.finishing) {
+ removeActivityFromHistoryLocked(r);
+ removedFromHistory = true;
+ } else {
+ r.state = ActivityState.DESTROYED;
+ }
+ }
+
+ r.configChangeFlags = 0;
+
+ if (!mLRUActivities.remove(r) && hadApp) {
+ Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
+ }
+
+ return removedFromHistory;
+ }
+
+ final void activityDestroyed(IBinder token) {
+ synchronized (mService) {
+ mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
+
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ if (r.state == ActivityState.DESTROYING) {
+ final long origId = Binder.clearCallingIdentity();
+ removeActivityFromHistoryLocked(r);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+ }
+
+ private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) {
+ int i = list.size();
+ if (localLOGV) Slog.v(
+ TAG, "Removing app " + app + " from list " + list
+ + " with " + i + " entries");
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)list.get(i);
+ if (localLOGV) Slog.v(
+ TAG, "Record #" + i + " " + r + ": app=" + r.app);
+ if (r.app == app) {
+ if (localLOGV) Slog.v(TAG, "Removing this entry!");
+ list.remove(i);
+ }
+ }
+ }
+
+ void removeHistoryRecordsForAppLocked(ProcessRecord app) {
+ removeHistoryRecordsForAppLocked(mLRUActivities, app);
+ removeHistoryRecordsForAppLocked(mStoppingActivities, app);
+ removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
+ removeHistoryRecordsForAppLocked(mFinishingActivities, app);
+ }
+
+ final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
+
+ final int task = tr.taskId;
+ int top = mHistory.size()-1;
+
+ if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
+ // nothing to do!
+ return;
+ }
+
+ ArrayList moved = new ArrayList();
+
+ // Applying the affinities may have removed entries from the history,
+ // so get the size again.
+ top = mHistory.size()-1;
+ int pos = top;
+
+ // Shift all activities with this task up to the top
+ // of the stack, keeping them in the same internal order.
+ while (pos >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+ if (localLOGV) Slog.v(
+ TAG, "At " + pos + " ckp " + r.task + ": " + r);
+ boolean first = true;
+ if (r.task.taskId == task) {
+ if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
+ mHistory.remove(pos);
+ mHistory.add(top, r);
+ moved.add(0, r);
+ top--;
+ if (first && mMainStack) {
+ mService.addRecentTaskLocked(r.task);
+ first = false;
+ }
+ }
+ pos--;
+ }
+
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare to front transition: task=" + tr);
+ if (reason != null &&
+ (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ mNoAnimActivities.add(r);
+ }
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
+ }
+
+ mService.mWindowManager.moveAppTokensToTop(moved);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ finishTaskMoveLocked(task);
+ EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task);
+ }
+
+ private final void finishTaskMoveLocked(int task) {
+ resumeTopActivityLocked(null);
+ }
+
+ /**
+ * Worker method for rearranging history stack. Implements the function of moving all
+ * activities for a specific task (gathering them if disjoint) into a single group at the
+ * bottom of the stack.
+ *
+ * If a watcher is installed, the action is preflighted and the watcher has an opportunity
+ * to premeptively cancel the move.
+ *
+ * @param task The taskId to collect and move to the bottom.
+ * @return Returns true if the move completed, false if not.
+ */
+ final boolean moveTaskToBackLocked(int task, ActivityRecord reason) {
+ Slog.i(TAG, "moveTaskToBack: " + task);
+
+ // If we have a watcher, preflight the move before committing to it. First check
+ // for *other* available tasks, but if none are available, then try again allowing the
+ // current task to be selected.
+ if (mMainStack && mService.mController != null) {
+ ActivityRecord next = topRunningActivityLocked(null, task);
+ if (next == null) {
+ next = topRunningActivityLocked(null, 0);
+ }
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean moveOK = true;
+ try {
+ moveOK = mService.mController.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ }
+ if (!moveOK) {
+ return false;
+ }
+ }
+ }
+
+ ArrayList moved = new ArrayList();
+
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare to back transition: task=" + task);
+
+ final int N = mHistory.size();
+ int bottom = 0;
+ int pos = 0;
+
+ // Shift all activities with this task down to the bottom
+ // of the stack, keeping them in the same internal order.
+ while (pos < N) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+ if (localLOGV) Slog.v(
+ TAG, "At " + pos + " ckp " + r.task + ": " + r);
+ if (r.task.taskId == task) {
+ if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
+ mHistory.remove(pos);
+ mHistory.add(bottom, r);
+ moved.add(r);
+ bottom++;
+ }
+ pos++;
+ }
+
+ if (reason != null &&
+ (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ mNoAnimActivities.add(r);
+ }
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
+ }
+ mService.mWindowManager.moveAppTokensToBottom(moved);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ finishTaskMoveLocked(task);
+ return true;
+ }
+
+ private final void logStartActivity(int tag, ActivityRecord r,
+ TaskRecord task) {
+ EventLog.writeEvent(tag,
+ System.identityHashCode(r), task.taskId,
+ r.shortComponentName, r.intent.getAction(),
+ r.intent.getType(), r.intent.getDataString(),
+ r.intent.getFlags());
+ }
+
+ /**
+ * Make sure the given activity matches the current configuration. Returns
+ * false if the activity had to be destroyed. Returns true if the
+ * configuration is the same, or the activity will remain running as-is
+ * for whatever reason. Ensures the HistoryRecord is updated with the
+ * correct configuration and all other bookkeeping is handled.
+ */
+ final boolean ensureActivityConfigurationLocked(ActivityRecord r,
+ int globalChanges) {
+ if (mConfigWillChange) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Skipping config check (will change): " + r);
+ return true;
+ }
+
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Ensuring correct configuration: " + r);
+
+ // Short circuit: if the two configurations are the exact same
+ // object (the common case), then there is nothing to do.
+ Configuration newConfig = mService.mConfiguration;
+ if (r.configuration == newConfig) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration unchanged in " + r);
+ return true;
+ }
+
+ // We don't worry about activities that are finishing.
+ if (r.finishing) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration doesn't matter in finishing " + r);
+ r.stopFreezingScreenLocked(false);
+ return true;
+ }
+
+ // Okay we now are going to make this activity have the new config.
+ // But then we need to figure out how it needs to deal with that.
+ Configuration oldConfig = r.configuration;
+ r.configuration = newConfig;
+
+ // If the activity isn't currently running, just leave the new
+ // configuration and it will pick that up next time it starts.
+ if (r.app == null || r.app.thread == null) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration doesn't matter not running " + r);
+ r.stopFreezingScreenLocked(false);
+ return true;
+ }
+
+ // Figure out what has changed between the two configurations.
+ int changes = oldConfig.diff(newConfig);
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
+ + Integer.toHexString(changes) + ", handles=0x"
+ + Integer.toHexString(r.info.configChanges)
+ + ", newConfig=" + newConfig);
+ }
+ if ((changes&(~r.info.configChanges)) != 0) {
+ // Aha, the activity isn't handling the change, so DIE DIE DIE.
+ r.configChangeFlags |= changes;
+ r.startFreezingScreenLocked(r.app, globalChanges);
+ if (r.app == null || r.app.thread == null) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is destroying non-running " + r);
+ destroyActivityLocked(r, true);
+ } else if (r.state == ActivityState.PAUSING) {
+ // A little annoying: we are waiting for this activity to
+ // finish pausing. Let's not do anything now, but just
+ // flag that it needs to be restarted when done pausing.
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is skipping already pausing " + r);
+ r.configDestroy = true;
+ return true;
+ } else if (r.state == ActivityState.RESUMED) {
+ // Try to optimize this case: the configuration is changing
+ // and we need to restart the top, resumed activity.
+ // Instead of doing the normal handshaking, just say
+ // "restart!".
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is restarting resumed " + r);
+ relaunchActivityLocked(r, r.configChangeFlags, true);
+ r.configChangeFlags = 0;
+ } else {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is restarting non-resumed " + r);
+ relaunchActivityLocked(r, r.configChangeFlags, false);
+ r.configChangeFlags = 0;
+ }
+
+ // All done... tell the caller we weren't able to keep this
+ // activity around.
+ return false;
+ }
+
+ // Default case: the activity can handle this new configuration, so
+ // hand it over. Note that we don't need to give it the new
+ // configuration, since we always send configuration changes to all
+ // process when they happen so it can just use whatever configuration
+ // it last got.
+ if (r.app != null && r.app.thread != null) {
+ try {
+ if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
+ r.app.thread.scheduleActivityConfigurationChanged(r);
+ } catch (RemoteException e) {
+ // If process died, whatever.
+ }
+ }
+ r.stopFreezingScreenLocked(false);
+
+ return true;
+ }
+
+ private final boolean relaunchActivityLocked(ActivityRecord r,
+ int changes, boolean andResume) {
+ List<ResultInfo> results = null;
+ List<Intent> newIntents = null;
+ if (andResume) {
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
+ EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
+ : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+
+ r.startFreezingScreenLocked(r.app, 0);
+
+ try {
+ if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
+ r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
+ changes, !andResume, mService.mConfiguration);
+ // Note: don't need to call pauseIfSleepingLocked() here, because
+ // the caller will only pass in 'andResume' if this activity is
+ // currently resumed, which implies we aren't sleeping.
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ if (andResume) {
+ r.results = null;
+ r.newIntents = null;
+ if (mMainStack) {
+ mService.reportResumedActivityLocked(r);
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index 3a1aad6..a769c05 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -80,9 +80,6 @@ class AppErrorDialog extends BaseErrorDialog {
DISMISS_TIMEOUT);
}
- public void onStop() {
- }
-
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
synchronized (mProc) {
diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java
index 9702f91..b2737dc 100644
--- a/services/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/java/com/android/server/am/AppNotRespondingDialog.java
@@ -40,7 +40,7 @@ class AppNotRespondingDialog extends BaseErrorDialog {
private final ProcessRecord mProc;
public AppNotRespondingDialog(ActivityManagerService service, Context context,
- ProcessRecord app, HistoryRecord activity) {
+ ProcessRecord app, ActivityRecord activity) {
super(context);
mService = service;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 33bbc13..73a5435 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -23,6 +23,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Process;
import android.os.ServiceManager;
+import android.os.WorkSource;
import android.telephony.SignalStrength;
import android.util.Slog;
@@ -59,7 +60,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
public void shutdown() {
Slog.w("BatteryStats", "Writing battery stats before shutdown...");
synchronized (mStats) {
- mStats.writeLocked();
+ mStats.shutdownLocked();
}
}
@@ -93,45 +94,59 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
return data;
}
- public void noteStartWakelock(int uid, String name, int type) {
+ public void noteStartWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
+ mStats.noteStartWakeLocked(uid, pid, name, type);
}
}
- public void noteStopWakelock(int uid, String name, int type) {
+ public void noteStopWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
+ mStats.noteStopWakeLocked(uid, pid, name, type);
+ }
+ }
+
+ public void noteStartWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteStartWakeFromSourceLocked(ws, pid, name, type);
+ }
+ }
+
+ public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteStopWakeFromSourceLocked(ws, pid, name, type);
}
}
public void noteStartSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartSensor(sensor);
+ mStats.noteStartSensorLocked(uid, sensor);
}
}
public void noteStopSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopSensor(sensor);
+ mStats.noteStopSensorLocked(uid, sensor);
}
}
public void noteStartGps(int uid) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteStartGps(uid);
+ mStats.noteStartGpsLocked(uid);
}
}
public void noteStopGps(int uid) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteStopGps(uid);
+ mStats.noteStopGpsLocked(uid);
}
}
@@ -203,17 +218,17 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
}
}
- public void noteWifiOn(int uid) {
+ public void noteWifiOn() {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteWifiOnLocked(uid);
+ mStats.noteWifiOnLocked();
}
}
- public void noteWifiOff(int uid) {
+ public void noteWifiOff() {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteWifiOffLocked(uid);
+ mStats.noteWifiOffLocked();
}
}
@@ -245,17 +260,24 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
}
}
- public void noteWifiRunning() {
+ public void noteWifiRunning(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiRunningLocked(ws);
+ }
+ }
+
+ public void noteWifiRunningChanged(WorkSource oldWs, WorkSource newWs) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteWifiRunningLocked();
+ mStats.noteWifiRunningChangedLocked(oldWs, newWs);
}
}
- public void noteWifiStopped() {
+ public void noteWifiStopped(WorkSource ws) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteWifiStoppedLocked();
+ mStats.noteWifiStoppedLocked(ws);
}
}
@@ -317,18 +339,56 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
}
}
- public boolean isOnBattery() {
- return mStats.isOnBattery();
+ public void noteFullWifiLockAcquiredFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteFullWifiLockAcquiredFromSourceLocked(ws);
+ }
}
-
- public void setOnBattery(boolean onBattery, int level) {
+
+ public void noteFullWifiLockReleasedFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteFullWifiLockReleasedFromSourceLocked(ws);
+ }
+ }
+
+ public void noteScanWifiLockAcquiredFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteScanWifiLockAcquiredFromSourceLocked(ws);
+ }
+ }
+
+ public void noteScanWifiLockReleasedFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteScanWifiLockReleasedFromSourceLocked(ws);
+ }
+ }
+
+ public void noteWifiMulticastEnabledFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiMulticastEnabledFromSourceLocked(ws);
+ }
+ }
+
+ public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
enforceCallingPermission();
- mStats.setOnBattery(onBattery, level);
+ synchronized (mStats) {
+ mStats.noteWifiMulticastDisabledFromSourceLocked(ws);
+ }
+ }
+
+ public boolean isOnBattery() {
+ return mStats.isOnBattery();
}
- public void recordCurrentLevel(int level) {
+ public void setBatteryState(int status, int health, int plugType, int level,
+ int temp, int volt) {
enforceCallingPermission();
- mStats.recordCurrentLevel(level);
+ mStats.setBatteryState(status, health, plugType, level, temp, volt);
}
public long getAwakeTimeBattery() {
@@ -359,7 +419,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
for (String arg : args) {
if ("--checkin".equals(arg)) {
isCheckin = true;
- break;
+ } else if ("--reset".equals(arg)) {
+ mStats.resetAllStatsLocked();
}
}
}
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index c3f0b3e..b268efa 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -26,6 +26,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.List;
@@ -73,61 +74,65 @@ class BroadcastRecord extends Binder {
ActivityInfo curReceiver; // info about the receiver that is currently running.
void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + this);
- pw.println(prefix + intent);
+ final long now = SystemClock.uptimeMillis();
+
+ pw.print(prefix); pw.println(this);
+ pw.print(prefix); pw.println(intent);
if (sticky) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
- pw.println(prefix + "extras: " + bundle.toString());
+ pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
}
}
- pw.println(prefix + "proc=" + callerApp);
- pw.println(prefix + "caller=" + callerPackage
- + " callingPid=" + callingPid
- + " callingUid=" + callingUid);
+ pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.print(" ");
+ pw.print(callerApp != null ? callerApp.toShortString() : "null");
+ pw.print(" pid="); pw.print(callingPid);
+ pw.print(" uid="); pw.println(callingUid);
if (requiredPermission != null) {
- pw.println(prefix + "requiredPermission=" + requiredPermission);
+ pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
}
- pw.println(prefix + "dispatchTime=" + dispatchTime + " ("
- + (SystemClock.uptimeMillis()-dispatchTime) + "ms since now)");
+ pw.print(prefix); pw.print("dispatchTime=");
+ TimeUtils.formatDuration(dispatchTime, now, pw);
if (finishTime != 0) {
- pw.println(prefix + "finishTime=" + finishTime + " ("
- + (SystemClock.uptimeMillis()-finishTime) + "ms since now)");
+ pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
} else {
- pw.println(prefix + "receiverTime=" + receiverTime + " ("
- + (SystemClock.uptimeMillis()-receiverTime) + "ms since now)");
+ pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
}
+ pw.println("");
if (anrCount != 0) {
- pw.println(prefix + "anrCount=" + anrCount);
+ pw.print(prefix); pw.print("anrCount="); pw.println(anrCount);
}
if (resultTo != null || resultCode != -1 || resultData != null) {
- pw.println(prefix + "resultTo=" + resultTo
- + " resultCode=" + resultCode + " resultData=" + resultData);
+ pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
+ pw.print(" resultCode="); pw.print(resultCode);
+ pw.print(" resultData="); pw.println(resultData);
}
if (resultExtras != null) {
- pw.println(prefix + "resultExtras=" + resultExtras);
+ pw.print(prefix); pw.print("resultExtras="); pw.println(resultExtras);
}
if (resultAbort || ordered || sticky || initialSticky) {
- pw.println(prefix + "resultAbort=" + resultAbort
- + " ordered=" + ordered + " sticky=" + sticky
- + " initialSticky=" + initialSticky);
+ pw.print(prefix); pw.print("resultAbort="); pw.print(resultAbort);
+ pw.print(" ordered="); pw.print(ordered);
+ pw.print(" sticky="); pw.print(sticky);
+ pw.print(" initialSticky="); pw.println(initialSticky);
}
if (nextReceiver != 0 || receiver != null) {
- pw.println(prefix + "nextReceiver=" + nextReceiver
- + " receiver=" + receiver);
+ pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver);
+ pw.print(" receiver="); pw.println(receiver);
}
if (curFilter != null) {
- pw.println(prefix + "curFilter=" + curFilter);
+ pw.print(prefix); pw.print("curFilter="); pw.println(curFilter);
}
if (curReceiver != null) {
- pw.println(prefix + "curReceiver=" + curReceiver);
+ pw.print(prefix); pw.print("curReceiver="); pw.println(curReceiver);
}
if (curApp != null) {
- pw.println(prefix + "curApp=" + curApp);
- pw.println(prefix + "curComponent="
- + (curComponent != null ? curComponent.toShortString() : "--"));
+ pw.print(prefix); pw.print("curApp="); pw.println(curApp);
+ pw.print(prefix); pw.print("curComponent=");
+ pw.println((curComponent != null ? curComponent.toShortString() : "--"));
if (curReceiver != null && curReceiver.applicationInfo != null) {
- pw.println(prefix + "curSourceDir=" + curReceiver.applicationInfo.sourceDir);
+ pw.print(prefix); pw.print("curSourceDir=");
+ pw.println(curReceiver.applicationInfo.sourceDir);
}
}
String stateStr = " (?)";
@@ -137,13 +142,14 @@ class BroadcastRecord extends Binder {
case CALL_IN_RECEIVE: stateStr=" (CALL_IN_RECEIVE)"; break;
case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
}
- pw.println(prefix + "state=" + state + stateStr);
+ pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
final int N = receivers != null ? receivers.size() : 0;
String p2 = prefix + " ";
PrintWriterPrinter printer = new PrintWriterPrinter(pw);
for (int i=0; i<N; i++) {
Object o = receivers.get(i);
- pw.println(prefix + "Receiver #" + i + ": " + o);
+ pw.print(prefix); pw.print("Receiver #"); pw.print(i);
+ pw.print(": "); pw.println(o);
if (o instanceof BroadcastFilter)
((BroadcastFilter)o).dumpBrief(pw, p2);
else if (o instanceof ResolveInfo)
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index f613b00..22acda9 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -26,7 +26,7 @@ import java.io.PrintWriter;
*/
class ConnectionRecord {
final AppBindRecord binding; // The application/service binding.
- final HistoryRecord activity; // If non-null, the owning activity.
+ final ActivityRecord activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
final int flags; // Binding options.
final int clientLabel; // String resource labeling this client.
@@ -42,7 +42,7 @@ class ConnectionRecord {
+ " flags=0x" + Integer.toHexString(flags));
}
- ConnectionRecord(AppBindRecord _binding, HistoryRecord _activity,
+ ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity,
IServiceConnection _conn, int _flags,
int _clientLabel, PendingIntent _clientIntent) {
binding = _binding;
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index c764635..44c9742 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.app.IActivityManager.ContentProviderHolder;
+import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.os.Process;
@@ -29,6 +30,7 @@ class ContentProviderRecord extends ContentProviderHolder {
final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
final int uid;
final ApplicationInfo appInfo;
+ final ComponentName name;
int externals; // number of non-framework processes supported by this provider
ProcessRecord app; // if non-null, hosting application
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
@@ -38,6 +40,7 @@ class ContentProviderRecord extends ContentProviderHolder {
super(_info);
uid = ai.uid;
appInfo = ai;
+ name = new ComponentName(_info.packageName, _info.name);
noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
}
@@ -45,6 +48,7 @@ class ContentProviderRecord extends ContentProviderHolder {
super(cpr.info);
uid = cpr.uid;
appInfo = cpr.appInfo;
+ name = cpr.name;
noReleaseNeeded = cpr.noReleaseNeeded;
}
diff --git a/services/java/com/android/server/am/LaunchWarningWindow.java b/services/java/com/android/server/am/LaunchWarningWindow.java
new file mode 100644
index 0000000..4130e33
--- /dev/null
+++ b/services/java/com/android/server/am/LaunchWarningWindow.java
@@ -0,0 +1,36 @@
+package com.android.server.am;
+
+import com.android.internal.R;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class LaunchWarningWindow extends Dialog {
+ public LaunchWarningWindow(Context context, ActivityRecord cur, ActivityRecord next) {
+ super(context, R.style.Theme_Toast);
+
+ requestWindowFeature(Window.FEATURE_LEFT_ICON);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+
+ setContentView(R.layout.launch_warning);
+ setTitle(context.getText(R.string.launch_warning_title));
+ getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
+ R.drawable.ic_dialog_alert);
+ ImageView icon = (ImageView)findViewById(R.id.replace_app_icon);
+ icon.setImageDrawable(next.info.applicationInfo.loadIcon(context.getPackageManager()));
+ TextView text = (TextView)findViewById(R.id.replace_message);
+ text.setText(context.getResources().getString(R.string.launch_warning_replace,
+ next.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
+ icon = (ImageView)findViewById(R.id.original_app_icon);
+ icon.setImageDrawable(cur.info.applicationInfo.loadIcon(context.getPackageManager()));
+ text = (TextView)findViewById(R.id.original_message);
+ text.setText(context.getResources().getString(R.string.launch_warning_original,
+ cur.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
+ }
+}
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 847e91b..7a85eb8 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -42,7 +42,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
final static class Key {
final int type;
final String packageName;
- final HistoryRecord activity;
+ final ActivityRecord activity;
final String who;
final int requestCode;
final Intent requestIntent;
@@ -52,7 +52,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
private static final int ODD_PRIME_NUMBER = 37;
- Key(int _t, String _p, HistoryRecord _a, String _w,
+ Key(int _t, String _p, ActivityRecord _a, String _w,
int _r, Intent _i, String _it, int _f) {
type = _t;
packageName = _p;
@@ -218,7 +218,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
}
break;
case IActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
- owner.sendActivityResultLocked(-1, key.activity,
+ key.activity.stack.sendActivityResultLocked(-1, key.activity,
key.who, key.requestCode, code, finalIntent);
break;
case IActivityManager.INTENT_SENDER_BROADCAST:
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index f49a182..353ff6d 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.Watchdog;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -27,9 +26,9 @@ import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -40,12 +39,12 @@ import java.util.HashSet;
* Full information about a particular process that
* is currently running.
*/
-class ProcessRecord implements Watchdog.PssRequestor {
+class ProcessRecord {
final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics
final ApplicationInfo info; // all about the first app in the process
final String processName; // name of the process
// List of packages running in the process
- final HashSet<String> pkgList = new HashSet();
+ final HashSet<String> pkgList = new HashSet<String>();
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
@@ -61,6 +60,7 @@ class ProcessRecord implements Watchdog.PssRequestor {
int setAdj; // Last set OOM adjustment for this process
int curSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
+ boolean keeping; // Actively running code so don't kill due to that?
boolean setIsForeground; // Running foreground UI when last set?
boolean foregroundServices; // Running any services that are foreground?
boolean bad; // True if disabled in the bad process list
@@ -75,6 +75,9 @@ class ProcessRecord implements Watchdog.PssRequestor {
Bundle instrumentationArguments;// as given to us
ComponentName instrumentationResultClass;// copy of instrumentationClass
BroadcastRecord curReceiver;// receiver currently running in the app
+ long lastWakeTime; // How long proc held wake lock at last check
+ long lastCpuTime; // How long proc has run CPU at last check
+ long curCpuTime; // How long proc has run CPU most recently
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
boolean reportLowMemory; // Set to true when waiting to report low mem
@@ -87,9 +90,9 @@ class ProcessRecord implements Watchdog.PssRequestor {
Object adjTarget; // Debugging: target component impacting oom_adj.
// contains HistoryRecord objects
- final ArrayList activities = new ArrayList();
+ final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
// all ServiceRecord running in this process
- final HashSet services = new HashSet();
+ final HashSet<ServiceRecord> services = new HashSet<ServiceRecord>();
// services that are currently executing code (need to remain foreground).
final HashSet<ServiceRecord> executingServices
= new HashSet<ServiceRecord>();
@@ -99,7 +102,8 @@ class ProcessRecord implements Watchdog.PssRequestor {
// all IIntentReceivers that are registered from this process.
final HashSet<ReceiverList> receivers = new HashSet<ReceiverList>();
// class (String) -> ContentProviderRecord
- final HashMap pubProviders = new HashMap();
+ final HashMap<String, ContentProviderRecord> pubProviders
+ = new HashMap<String, ContentProviderRecord>();
// All ContentProviderRecord process is using
final HashMap<ContentProviderRecord, Integer> conProviders
= new HashMap<ContentProviderRecord, Integer>();
@@ -111,7 +115,6 @@ class ProcessRecord implements Watchdog.PssRequestor {
Dialog anrDialog; // dialog being displayed due to app not resp.
boolean removed; // has app package been removed from device?
boolean debugging; // was app launched for debugging?
- int persistentActivities; // number of activities that are persistent
boolean waitedForDebugger; // has process show wait for debugger dialog?
Dialog waitDialog; // current wait for debugger dialog
@@ -128,7 +131,8 @@ class ProcessRecord implements Watchdog.PssRequestor {
ComponentName errorReportReceiver;
void dump(PrintWriter pw, String prefix) {
- long now = SystemClock.uptimeMillis();
+ final long now = SystemClock.uptimeMillis();
+
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
@@ -158,8 +162,10 @@ class ProcessRecord implements Watchdog.PssRequestor {
pw.print(" curReceiver="); pw.println(curReceiver);
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
- pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime);
- pw.print(" lruWeight="); pw.println(lruWeight);
+ pw.print(prefix); pw.print("lastActivityTime=");
+ TimeUtils.formatDuration(lastActivityTime, now, pw);
+ pw.print(" lruWeight="); pw.print(lruWeight);
+ pw.print(" keeping="); pw.print(keeping);
pw.print(" hidden="); pw.print(hidden);
pw.print(" empty="); pw.println(empty);
pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
@@ -174,10 +180,28 @@ class ProcessRecord implements Watchdog.PssRequestor {
pw.print(" foregroundServices="); pw.print(foregroundServices);
pw.print(" forcingToForeground="); pw.println(forcingToForeground);
pw.print(prefix); pw.print("persistent="); pw.print(persistent);
- pw.print(" removed="); pw.print(removed);
- pw.print(" persistentActivities="); pw.println(persistentActivities);
+ pw.print(" removed="); pw.println(removed);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.println(lruSeq);
+ if (!keeping) {
+ long wtime;
+ synchronized (batteryStats.getBatteryStats()) {
+ wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid,
+ pid, SystemClock.elapsedRealtime());
+ }
+ long timeUsed = wtime - lastWakeTime;
+ pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
+ pw.print(" time used=");
+ TimeUtils.formatDuration(timeUsed, pw); pw.println("");
+ pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime);
+ pw.print(" time used=");
+ TimeUtils.formatDuration(curCpuTime-lastCpuTime, pw); pw.println("");
+ }
+ pw.print(prefix); pw.print("lastRequestedGc=");
+ TimeUtils.formatDuration(lastRequestedGc, now, pw);
+ pw.print(" lastLowMemory=");
+ TimeUtils.formatDuration(lastLowMemory, now, pw);
+ pw.print(" reportLowMemory="); pw.println(reportLowMemory);
if (killedBackground) {
pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
}
@@ -233,7 +257,6 @@ class ProcessRecord implements Watchdog.PssRequestor {
curAdj = setAdj = -100;
persistent = false;
removed = false;
- persistentActivities = 0;
}
public void setPid(int _pid) {
@@ -249,7 +272,7 @@ class ProcessRecord implements Watchdog.PssRequestor {
public boolean isInterestingToUserLocked() {
final int size = activities.size();
for (int i = 0 ; i < size ; i++) {
- HistoryRecord r = (HistoryRecord) activities.get(i);
+ ActivityRecord r = activities.get(i);
if (r.isInterestingToUserLocked()) {
return true;
}
@@ -261,17 +284,7 @@ class ProcessRecord implements Watchdog.PssRequestor {
int i = activities.size();
while (i > 0) {
i--;
- ((HistoryRecord)activities.get(i)).stopFreezingScreenLocked(true);
- }
- }
-
- public void requestPss() {
- IApplicationThread localThread = thread;
- if (localThread != null) {
- try {
- localThread.requestPss();
- } catch (RemoteException e) {
- }
+ activities.get(i).stopFreezingScreenLocked(true);
}
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 0542497..e5aceb4 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.NotificationManagerService;
import android.app.INotificationManager;
import android.app.Notification;
@@ -30,10 +31,12 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -41,6 +44,12 @@ import java.util.List;
* A running application service.
*/
class ServiceRecord extends Binder {
+ // Maximum number of delivery attempts before giving up.
+ static final int MAX_DELIVERY_COUNT = 3;
+
+ // Maximum number of times it can fail during execution before giving up.
+ static final int MAX_DONE_EXECUTING_COUNT = 6;
+
final ActivityManagerService ams;
final BatteryStatsImpl.Uid.Pkg.Serv stats;
final ComponentName name; // service component.
@@ -63,32 +72,9 @@ class ServiceRecord extends Binder {
final HashMap<Intent.FilterComparison, IntentBindRecord> bindings
= new HashMap<Intent.FilterComparison, IntentBindRecord>();
// All active bindings to the service.
- final HashMap<IBinder, ConnectionRecord> connections
- = new HashMap<IBinder, ConnectionRecord>();
+ final HashMap<IBinder, ArrayList<ConnectionRecord>> connections
+ = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
// IBinder -> ConnectionRecord of all bound clients
-
- // Maximum number of delivery attempts before giving up.
- static final int MAX_DELIVERY_COUNT = 3;
-
- // Maximum number of times it can fail during execution before giving up.
- static final int MAX_DONE_EXECUTING_COUNT = 6;
-
- static class StartItem {
- final int id;
- final Intent intent;
- long deliveredTime;
- int deliveryCount;
- int doneExecutingCount;
-
- StartItem(int _id, Intent _intent) {
- id = _id;
- intent = _intent;
- }
- }
- final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
- // start() arguments which been delivered.
- final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
- // start() arguments that haven't yet been delivered.
ProcessRecord app; // where this service is running or null.
boolean isForeground; // is service currently in foreground mode?
@@ -110,22 +96,93 @@ class ServiceRecord extends Binder {
String stringName; // caching of toString
+ static class StartItem {
+ final ServiceRecord sr;
+ final int id;
+ final Intent intent;
+ final int targetPermissionUid;
+ long deliveredTime;
+ int deliveryCount;
+ int doneExecutingCount;
+ UriPermissionOwner uriPermissions;
+
+ String stringName; // caching of toString
+
+ StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+ sr = _sr;
+ id = _id;
+ intent = _intent;
+ targetPermissionUid = _targetPermissionUid;
+ }
+
+ UriPermissionOwner getUriPermissionsLocked() {
+ if (uriPermissions == null) {
+ uriPermissions = new UriPermissionOwner(sr.ams, this);
+ }
+ return uriPermissions;
+ }
+
+ void removeUriPermissionsLocked() {
+ if (uriPermissions != null) {
+ uriPermissions.removeUriPermissionsLocked();
+ uriPermissions = null;
+ }
+ }
+
+ public String toString() {
+ if (stringName != null) {
+ return stringName;
+ }
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("ServiceRecord{")
+ .append(Integer.toHexString(System.identityHashCode(sr)))
+ .append(' ').append(sr.shortName)
+ .append(" StartItem ")
+ .append(Integer.toHexString(System.identityHashCode(this)))
+ .append(" id=").append(id).append('}');
+ return stringName = sb.toString();
+ }
+ }
+
+ final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
+ // start() arguments which been delivered.
+ final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
+ // start() arguments that haven't yet been delivered.
+
void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
final int N = list.size();
for (int i=0; i<N; i++) {
StartItem si = list.get(i);
pw.print(prefix); pw.print("#"); pw.print(i);
pw.print(" id="); pw.print(si.id);
- if (now != 0) pw.print(" dur="); pw.print(now-si.deliveredTime);
+ if (now != 0) {
+ pw.print(" dur=");
+ TimeUtils.formatDuration(si.deliveredTime, now, pw);
+ }
if (si.deliveryCount != 0) {
pw.print(" dc="); pw.print(si.deliveryCount);
}
if (si.doneExecutingCount != 0) {
pw.print(" dxc="); pw.print(si.doneExecutingCount);
}
- pw.print(" ");
+ pw.println("");
+ pw.print(prefix); pw.print(" intent=");
if (si.intent != null) pw.println(si.intent.toString());
else pw.println("null");
+ if (si.targetPermissionUid >= 0) {
+ pw.print(prefix); pw.print(" targetPermissionUid=");
+ pw.println(si.targetPermissionUid);
+ }
+ if (si.uriPermissions != null) {
+ if (si.uriPermissions.readUriPermissions != null) {
+ pw.print(prefix); pw.print(" readUriPermissions=");
+ pw.println(si.uriPermissions.readUriPermissions);
+ }
+ if (si.uriPermissions.writeUriPermissions != null) {
+ pw.print(prefix); pw.print(" writeUriPermissions=");
+ pw.println(si.uriPermissions.writeUriPermissions);
+ }
+ }
}
}
@@ -139,18 +196,26 @@ class ServiceRecord extends Binder {
pw.print(prefix); pw.print("permission="); pw.println(permission);
}
long now = SystemClock.uptimeMillis();
- pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
- if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
- pw.print(" dataDir="); pw.println(dataDir);
+ long nowReal = SystemClock.elapsedRealtime();
+ pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
+ if (!resDir.equals(baseDir)) pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
if (isForeground || foregroundId != 0) {
pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
pw.print(" foregroundId="); pw.print(foregroundId);
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
}
- pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now);
- pw.print(" executingStart="); pw.print(executingStart-now);
- pw.print(" restartTime="); pw.println(restartTime);
+ pw.print(prefix); pw.print("createTime=");
+ TimeUtils.formatDuration(createTime, nowReal, pw);
+ pw.print(" lastActivity=");
+ TimeUtils.formatDuration(lastActivity, now, pw);
+ pw.println("");
+ pw.print(prefix); pw.print(" executingStart=");
+ TimeUtils.formatDuration(executingStart, now, pw);
+ pw.print(" restartTime=");
+ TimeUtils.formatDuration(restartTime, now, pw);
+ pw.println("");
if (startRequested || lastStartId != 0) {
pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
pw.print(" stopIfKilled="); pw.print(stopIfKilled);
@@ -161,13 +226,15 @@ class ServiceRecord extends Binder {
|| restartDelay != 0 || nextRestartTime != 0) {
pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
pw.print(" restartCount="); pw.print(restartCount);
- pw.print(" restartDelay="); pw.print(restartDelay-now);
- pw.print(" nextRestartTime="); pw.print(nextRestartTime-now);
+ pw.print(" restartDelay=");
+ TimeUtils.formatDuration(restartDelay, now, pw);
+ pw.print(" nextRestartTime=");
+ TimeUtils.formatDuration(nextRestartTime, now, pw);
pw.print(" crashCount="); pw.println(crashCount);
}
if (deliveredStarts.size() > 0) {
pw.print(prefix); pw.println("Delivered Starts:");
- dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
+ dumpStartList(pw, prefix, deliveredStarts, now);
}
if (pendingStarts.size() > 0) {
pw.print(prefix); pw.println("Pending Starts:");
@@ -186,10 +253,12 @@ class ServiceRecord extends Binder {
}
if (connections.size() > 0) {
pw.print(prefix); pw.println("All Connections:");
- Iterator<ConnectionRecord> it = connections.values().iterator();
+ Iterator<ArrayList<ConnectionRecord>> it = connections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord c = it.next();
- pw.print(prefix); pw.print(" "); pw.println(c);
+ ArrayList<ConnectionRecord> c = it.next();
+ for (int i=0; i<c.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(c.get(i));
+ }
}
}
}
@@ -212,7 +281,8 @@ class ServiceRecord extends Binder {
dataDir = sInfo.applicationInfo.dataDir;
exported = sInfo.exported;
this.restarter = restarter;
- createTime = lastActivity = SystemClock.uptimeMillis();
+ createTime = SystemClock.elapsedRealtime();
+ lastActivity = SystemClock.uptimeMillis();
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
@@ -252,6 +322,8 @@ class ServiceRecord extends Binder {
}
public void postNotification() {
+ final int appUid = appInfo.uid;
+ final int appPid = app.pid;
if (foregroundId != 0 && foregroundNoti != null) {
// Do asynchronous communication with notification manager to
// avoid deadlocks.
@@ -260,22 +332,24 @@ class ServiceRecord extends Binder {
final Notification localForegroundNoti = foregroundNoti;
ams.mHandler.post(new Runnable() {
public void run() {
- INotificationManager inm = NotificationManager.getService();
- if (inm == null) {
+ NotificationManagerService nm =
+ (NotificationManagerService) NotificationManager.getService();
+ if (nm == null) {
return;
}
try {
int[] outId = new int[1];
- inm.enqueueNotification(localPackageName, localForegroundId,
- localForegroundNoti, outId);
+ nm.enqueueNotificationInternal(localPackageName, appUid, appPid,
+ null, localForegroundId, localForegroundNoti, outId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for service", e);
// If it gave us a garbage notification, it doesn't
// get to be foreground.
ams.setServiceForeground(name, ServiceRecord.this,
- localForegroundId, null, true);
- } catch (RemoteException e) {
+ 0, null, true);
+ ams.crashApplication(appUid, appPid, localPackageName,
+ "Bad notification for startForeground: " + e);
}
}
});
@@ -306,6 +380,13 @@ class ServiceRecord extends Binder {
}
}
+ public void clearDeliveredStartsLocked() {
+ for (int i=deliveredStarts.size()-1; i>=0; i--) {
+ deliveredStarts.get(i).removeUriPermissionsLocked();
+ }
+ deliveredStarts.clear();
+ }
+
public String toString() {
if (stringName != null) {
return stringName;
diff --git a/services/java/com/android/server/am/StrictModeViolationDialog.java b/services/java/com/android/server/am/StrictModeViolationDialog.java
new file mode 100644
index 0000000..fe76d18
--- /dev/null
+++ b/services/java/com/android/server/am/StrictModeViolationDialog.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2006 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.am;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Slog;
+
+class StrictModeViolationDialog extends BaseErrorDialog {
+ private final static String TAG = "StrictModeViolationDialog";
+
+ private final AppErrorResult mResult;
+ private final ProcessRecord mProc;
+
+ // Event 'what' codes
+ static final int ACTION_OK = 0;
+ static final int ACTION_OK_AND_REPORT = 1;
+
+ // 1-minute timeout, then we automatically dismiss the violation
+ // dialog
+ static final long DISMISS_TIMEOUT = 1000 * 60 * 1;
+
+ public StrictModeViolationDialog(Context context, AppErrorResult result, ProcessRecord app) {
+ super(context);
+
+ Resources res = context.getResources();
+
+ mProc = app;
+ mResult = result;
+ CharSequence name;
+ if ((app.pkgList.size() == 1) &&
+ (name=context.getPackageManager().getApplicationLabel(app.info)) != null) {
+ setMessage(res.getString(
+ com.android.internal.R.string.smv_application,
+ name.toString(), app.info.processName));
+ } else {
+ name = app.processName;
+ setMessage(res.getString(
+ com.android.internal.R.string.smv_process,
+ name.toString()));
+ }
+
+ setCancelable(false);
+
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ res.getText(com.android.internal.R.string.dlg_ok),
+ mHandler.obtainMessage(ACTION_OK));
+
+ if (app.errorReportReceiver != null) {
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ res.getText(com.android.internal.R.string.report),
+ mHandler.obtainMessage(ACTION_OK_AND_REPORT));
+ }
+
+ setTitle(res.getText(com.android.internal.R.string.aerr_title));
+ getWindow().addFlags(FLAG_SYSTEM_ERROR);
+ getWindow().setTitle("Strict Mode Violation: " + app.info.processName);
+
+ // After the timeout, pretend the user clicked the quit button
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(ACTION_OK),
+ DISMISS_TIMEOUT);
+ }
+
+ private final Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ synchronized (mProc) {
+ if (mProc != null && mProc.crashDialog == StrictModeViolationDialog.this) {
+ mProc.crashDialog = null;
+ }
+ }
+ mResult.set(msg.what);
+
+ // If this is a timeout we won't be automatically closed, so go
+ // ahead and explicitly dismiss ourselves just in case.
+ dismiss();
+ }
+ };
+}
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index ffa8a2a..e3347cb 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -22,13 +22,21 @@ import android.net.Uri;
import java.io.PrintWriter;
import java.util.HashSet;
+/**
+ * Description of a permission granted to an app to access a particular URI.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+ * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ */
class UriPermission {
final int uid;
final Uri uri;
int modeFlags = 0;
int globalModeFlags = 0;
- final HashSet<HistoryRecord> readActivities = new HashSet<HistoryRecord>();
- final HashSet<HistoryRecord> writeActivities = new HashSet<HistoryRecord>();
+ final HashSet<UriPermissionOwner> readOwners = new HashSet<UriPermissionOwner>();
+ final HashSet<UriPermissionOwner> writeOwners = new HashSet<UriPermissionOwner>();
String stringName;
@@ -37,31 +45,25 @@ class UriPermission {
uri = _uri;
}
- void clearModes(int modeFlags) {
- if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ void clearModes(int modeFlagsToClear) {
+ if ((modeFlagsToClear&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- if (readActivities.size() > 0) {
- for (HistoryRecord r : readActivities) {
- r.readUriPermissions.remove(this);
- if (r.readUriPermissions.size() == 0) {
- r.readUriPermissions = null;
- }
+ if (readOwners.size() > 0) {
+ for (UriPermissionOwner r : readOwners) {
+ r.removeReadPermission(this);
}
- readActivities.clear();
+ readOwners.clear();
}
}
- if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if ((modeFlagsToClear&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- if (readActivities.size() > 0) {
- for (HistoryRecord r : readActivities) {
- r.writeUriPermissions.remove(this);
- if (r.writeUriPermissions.size() == 0) {
- r.writeUriPermissions = null;
- }
+ if (readOwners.size() > 0) {
+ for (UriPermissionOwner r : writeOwners) {
+ r.removeWritePermission(this);
}
- readActivities.clear();
+ readOwners.clear();
}
}
}
@@ -85,11 +87,17 @@ class UriPermission {
pw.print(" uid="); pw.print(uid);
pw.print(" globalModeFlags=0x");
pw.println(Integer.toHexString(globalModeFlags));
- if (readActivities.size() != 0) {
- pw.print(prefix); pw.print("readActivities="); pw.println(readActivities);
+ if (readOwners.size() != 0) {
+ pw.print(prefix); pw.println("readOwners:");
+ for (UriPermissionOwner owner : readOwners) {
+ pw.print(prefix); pw.print(" * "); pw.println(owner);
+ }
}
- if (writeActivities.size() != 0) {
- pw.print(prefix); pw.print("writeActivities="); pw.println(writeActivities);
+ if (writeOwners.size() != 0) {
+ pw.print(prefix); pw.println("writeOwners:");
+ for (UriPermissionOwner owner : writeOwners) {
+ pw.print(prefix); pw.print(" * "); pw.println(owner);
+ }
}
}
}
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
new file mode 100644
index 0000000..99c82e6
--- /dev/null
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2010 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.am;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+class UriPermissionOwner {
+ final ActivityManagerService service;
+ final Object owner;
+
+ Binder externalToken;
+
+ HashSet<UriPermission> readUriPermissions; // special access to reading uris.
+ HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+
+ class ExternalToken extends Binder {
+ UriPermissionOwner getOwner() {
+ return UriPermissionOwner.this;
+ }
+ }
+
+ UriPermissionOwner(ActivityManagerService _service, Object _owner) {
+ service = _service;
+ owner = _owner;
+ }
+
+ Binder getExternalTokenLocked() {
+ if (externalToken != null) {
+ externalToken = new ExternalToken();
+ }
+ return externalToken;
+ }
+
+ static UriPermissionOwner fromExternalToken(IBinder token) {
+ if (token instanceof ExternalToken) {
+ return ((ExternalToken)token).getOwner();
+ }
+ return null;
+ }
+
+ void removeUriPermissionsLocked() {
+ removeUriPermissionsLocked(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+
+ void removeUriPermissionsLocked(int mode) {
+ if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
+ && readUriPermissions != null) {
+ for (UriPermission perm : readUriPermissions) {
+ perm.readOwners.remove(this);
+ if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ }
+ readUriPermissions = null;
+ }
+ if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+ && writeUriPermissions != null) {
+ for (UriPermission perm : writeUriPermissions) {
+ perm.writeOwners.remove(this);
+ if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ }
+ writeUriPermissions = null;
+ }
+ }
+
+ void removeUriPermissionLocked(Uri uri, int mode) {
+ if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
+ && readUriPermissions != null) {
+ Iterator<UriPermission> it = readUriPermissions.iterator();
+ while (it.hasNext()) {
+ UriPermission perm = it.next();
+ if (uri.equals(perm.uri)) {
+ perm.readOwners.remove(this);
+ if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ it.remove();
+ }
+ }
+ if (readUriPermissions.size() == 0) {
+ readUriPermissions = null;
+ }
+ }
+ if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+ && writeUriPermissions != null) {
+ Iterator<UriPermission> it = writeUriPermissions.iterator();
+ while (it.hasNext()) {
+ UriPermission perm = it.next();
+ if (uri.equals(perm.uri)) {
+ perm.writeOwners.remove(this);
+ if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ it.remove();
+ }
+ }
+ if (writeUriPermissions.size() == 0) {
+ writeUriPermissions = null;
+ }
+ }
+ }
+
+ public void addReadPermission(UriPermission perm) {
+ if (readUriPermissions == null) {
+ readUriPermissions = new HashSet<UriPermission>();
+ }
+ readUriPermissions.add(perm);
+ }
+
+ public void addWritePermission(UriPermission perm) {
+ if (writeUriPermissions == null) {
+ writeUriPermissions = new HashSet<UriPermission>();
+ }
+ writeUriPermissions.add(perm);
+ }
+
+ public void removeReadPermission(UriPermission perm) {
+ readUriPermissions.remove(perm);
+ if (readUriPermissions.size() == 0) {
+ readUriPermissions = null;
+ }
+ }
+
+ public void removeWritePermission(UriPermission perm) {
+ writeUriPermissions.remove(perm);
+ if (writeUriPermissions.size() == 0) {
+ writeUriPermissions = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return owner.toString();
+ }
+}
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 1b9e1c7..6e8f248 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -23,6 +23,8 @@ import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import com.android.internal.os.PkgUsageStats;
+
+import android.os.FileUtils;
import android.os.Parcel;
import android.os.Process;
import android.os.ServiceManager;
@@ -44,6 +46,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
/**
* This service collects the statistics associated with usage
@@ -54,6 +59,7 @@ import java.util.TimeZone;
public final class UsageStatsService extends IUsageStats.Stub {
public static final String SERVICE_NAME = "usagestats";
private static final boolean localLOGV = false;
+ private static final boolean REPORT_UNEXPECTED = false;
private static final String TAG = "UsageStats";
// Current on-disk Parcel version
@@ -88,11 +94,13 @@ public final class UsageStatsService extends IUsageStats.Stub {
private boolean mIsResumed;
private File mFile;
private String mFileLeaf;
- //private File mBackupFile;
- private long mLastWriteElapsedTime;
private File mDir;
- private Calendar mCal;
- private int mLastWriteDay;
+
+ private Calendar mCal; // guarded by itself
+
+ private final AtomicInteger mLastWriteDay = new AtomicInteger(-1);
+ private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
+ private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
static class TimeStats {
int count;
@@ -241,31 +249,33 @@ public final class UsageStatsService extends IUsageStats.Stub {
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
mFile = new File(mDir, mFileLeaf);
readStatsFromFile();
- mLastWriteElapsedTime = SystemClock.elapsedRealtime();
+ mLastWriteElapsedTime.set(SystemClock.elapsedRealtime());
// mCal was set by getCurrentDateStr(), want to use that same time.
- mLastWriteDay = mCal.get(Calendar.DAY_OF_YEAR);
+ mLastWriteDay.set(mCal.get(Calendar.DAY_OF_YEAR));
}
/*
* Utility method to convert date into string.
*/
private String getCurrentDateStr(String prefix) {
- mCal.setTimeInMillis(System.currentTimeMillis());
StringBuilder sb = new StringBuilder();
- if (prefix != null) {
- sb.append(prefix);
- }
- sb.append(mCal.get(Calendar.YEAR));
- int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
- if (mm < 10) {
- sb.append("0");
- }
- sb.append(mm);
- int dd = mCal.get(Calendar.DAY_OF_MONTH);
- if (dd < 10) {
- sb.append("0");
+ synchronized (mCal) {
+ mCal.setTimeInMillis(System.currentTimeMillis());
+ if (prefix != null) {
+ sb.append(prefix);
+ }
+ sb.append(mCal.get(Calendar.YEAR));
+ int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
+ if (mm < 10) {
+ sb.append("0");
+ }
+ sb.append(mm);
+ int dd = mCal.get(Calendar.DAY_OF_MONTH);
+ if (dd < 10) {
+ sb.append("0");
+ }
+ sb.append(dd);
}
- sb.append(dd);
return sb.toString();
}
@@ -360,23 +370,56 @@ public final class UsageStatsService extends IUsageStats.Stub {
file.delete();
}
}
-
- private void writeStatsToFile(boolean force) {
- synchronized (mFileLock) {
+
+ /**
+ * Conditionally start up a disk write if it's been awhile, or the
+ * day has rolled over.
+ *
+ * This is called indirectly from user-facing actions (when
+ * 'force' is false) so it tries to be quick, without writing to
+ * disk directly or acquiring heavy locks.
+ *
+ * @params force do an unconditional, synchronous stats flush
+ * to disk on the current thread.
+ */
+ private void writeStatsToFile(final boolean force) {
+ int curDay;
+ synchronized (mCal) {
mCal.setTimeInMillis(System.currentTimeMillis());
- final int curDay = mCal.get(Calendar.DAY_OF_YEAR);
- // Determine if the day changed... note that this will be wrong
- // if the year has changed but we are in the same day of year...
- // we can probably live with this.
- final boolean dayChanged = curDay != mLastWriteDay;
- long currElapsedTime = SystemClock.elapsedRealtime();
- if (!force) {
- if (((currElapsedTime-mLastWriteElapsedTime) < FILE_WRITE_INTERVAL) &&
- (!dayChanged)) {
- // wait till the next update
- return;
- }
+ curDay = mCal.get(Calendar.DAY_OF_YEAR);
+ }
+ final boolean dayChanged = curDay != mLastWriteDay.get();
+
+ // Determine if the day changed... note that this will be wrong
+ // if the year has changed but we are in the same day of year...
+ // we can probably live with this.
+ final long currElapsedTime = SystemClock.elapsedRealtime();
+
+ // Fast common path, without taking the often-contentious
+ // mFileLock.
+ if (!force) {
+ if (!dayChanged &&
+ (currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
+ // wait till the next update
+ return;
}
+ if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
+ new Thread("UsageStatsService_DiskWriter") {
+ public void run() {
+ try {
+ if (localLOGV) Slog.d(TAG, "Disk writer thread starting.");
+ writeStatsToFile(true);
+ } finally {
+ mUnforcedDiskWriteRunning.set(false);
+ if (localLOGV) Slog.d(TAG, "Disk writer thread ending.");
+ }
+ }
+ }.start();
+ }
+ return;
+ }
+
+ synchronized (mFileLock) {
// Get the most recent file
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
// Copy current file to back up
@@ -395,10 +438,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
try {
// Write mStats to file
- writeStatsFLOCK();
- mLastWriteElapsedTime = currElapsedTime;
+ writeStatsFLOCK(mFile);
+ mLastWriteElapsedTime.set(currElapsedTime);
if (dayChanged) {
- mLastWriteDay = curDay;
+ mLastWriteDay.set(curDay);
// clear stats
synchronized (mStats) {
mStats.clear();
@@ -418,10 +461,11 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
}
}
+ if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
}
- private void writeStatsFLOCK() throws IOException {
- FileOutputStream stream = new FileOutputStream(mFile);
+ private void writeStatsFLOCK(File file) throws IOException {
+ FileOutputStream stream = new FileOutputStream(file);
try {
Parcel out = Parcel.obtain();
writeStatsToParcelFLOCK(out);
@@ -429,6 +473,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
out.recycle();
stream.flush();
} finally {
+ FileUtils.sync(stream);
stream.close();
}
}
@@ -452,7 +497,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
public void shutdown() {
- Slog.w(TAG, "Writing usage stats before shutdown...");
+ Slog.i(TAG, "Writing usage stats before shutdown...");
writeStatsToFile(true);
}
@@ -479,7 +524,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
if (mLastResumedPkg != null) {
// We last resumed some other package... just pause it now
// to recover.
- Slog.i(TAG, "Unexpected resume of " + pkgName
+ if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
+ " while already resumed in " + mLastResumedPkg);
PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
if (pus != null) {
@@ -518,7 +563,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
return;
}
if (!mIsResumed) {
- Slog.i(TAG, "Something wrong here, didn't expect "
+ if (REPORT_UNEXPECTED) Slog.i(TAG, "Something wrong here, didn't expect "
+ pkgName + " to be paused");
return;
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index b29f875..a73a4ce 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -26,15 +26,16 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.Usb;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkInfo;
import android.net.NetworkUtils;
-import android.os.BatteryManager;
import android.os.Binder;
import android.os.Environment;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -110,8 +111,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private boolean mUsbMassStorageOff; // track the status of USB Mass Storage
private boolean mUsbConnected; // track the status of USB connection
+ // mUsbHandler message
+ static final int USB_STATE_CHANGE = 1;
+ static final int USB_DISCONNECTED = 0;
+ static final int USB_CONNECTED = 1;
+
+ // Time to delay before processing USB disconnect events
+ static final long USB_DISCONNECT_DELAY = 1000;
+
public Tethering(Context context, Looper looper) {
- Log.d(TAG, "Tethering starting");
mContext = context;
mLooper = looper;
@@ -135,7 +143,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mStateReceiver = new StateReceiver();
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Usb.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(mStateReceiver, filter);
@@ -421,13 +429,25 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
+ private Handler mUsbHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ mUsbConnected = (msg.arg1 == USB_CONNECTED);
+ updateUsbStatus();
+ }
+ };
+
private class StateReceiver extends BroadcastReceiver {
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
- if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- mUsbConnected = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
- == BatteryManager.BATTERY_PLUGGED_USB);
- Tethering.this.updateUsbStatus();
+ if (action.equals(Usb.ACTION_USB_STATE)) {
+ // process connect events immediately, but delay handling disconnects
+ // to debounce USB configuration changes
+ boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
+ Message msg = Message.obtain(mUsbHandler, USB_STATE_CHANGE,
+ (connected ? USB_CONNECTED : USB_DISCONNECTED), 0);
+ mUsbHandler.removeMessages(USB_STATE_CHANGE);
+ mUsbHandler.sendMessageDelayed(msg, connected ? 0 : USB_DISCONNECT_DELAY);
} else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
mUsbMassStorageOff = false;
updateUsbStatus();
@@ -436,7 +456,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mUsbMassStorageOff = true;
updateUsbStatus();
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBooted = true;
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
new file mode 100644
index 0000000..e3131fe
--- /dev/null
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2010 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.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Address;
+import android.location.GeocoderParams;
+import android.location.IGeocodeProvider;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * A class for proxying IGeocodeProvider implementations.
+ *
+ * {@hide}
+ */
+public class GeocoderProxy {
+
+ private static final String TAG = "GeocoderProxy";
+
+ private final Context mContext;
+ private final Intent mIntent;
+ private final Object mMutex = new Object(); // synchronizes access to mServiceConnection
+ private Connection mServiceConnection = new Connection(); // never null
+
+ public GeocoderProxy(Context context, String serviceName) {
+ mContext = context;
+ mIntent = new Intent(serviceName);
+ mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * When unbundled NetworkLocationService package is updated, we
+ * need to unbind from the old version and re-bind to the new one.
+ */
+ public void reconnect() {
+ synchronized (mMutex) {
+ mContext.unbindService(mServiceConnection);
+ mServiceConnection = new Connection();
+ mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ private class Connection implements ServiceConnection {
+
+ private IGeocodeProvider mProvider;
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "onServiceConnected " + className);
+ synchronized (this) {
+ mProvider = IGeocodeProvider.Stub.asInterface(service);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "onServiceDisconnected " + className);
+ synchronized (this) {
+ mProvider = null;
+ }
+ }
+
+ public IGeocodeProvider getProvider() {
+ synchronized (this) {
+ return mProvider;
+ }
+ }
+ }
+
+ public String getFromLocation(double latitude, double longitude, int maxResults,
+ GeocoderParams params, List<Address> addrs) {
+ IGeocodeProvider provider;
+ synchronized (mMutex) {
+ provider = mServiceConnection.getProvider();
+ }
+ if (provider != null) {
+ try {
+ return provider.getFromLocation(latitude, longitude, maxResults,
+ params, addrs);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocation failed", e);
+ }
+ }
+ return "Service not Available";
+ }
+
+ public String getFromLocationName(String locationName,
+ double lowerLeftLatitude, double lowerLeftLongitude,
+ double upperRightLatitude, double upperRightLongitude, int maxResults,
+ GeocoderParams params, List<Address> addrs) {
+ IGeocodeProvider provider;
+ synchronized (mMutex) {
+ provider = mServiceConnection.getProvider();
+ }
+ if (provider != null) {
+ try {
+ return provider.getFromLocationName(locationName, lowerLeftLatitude,
+ lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+ maxResults, params, addrs);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocationName failed", e);
+ }
+ }
+ return "Service not Available";
+ }
+}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
new file mode 100755
index 0000000..3561862
--- /dev/null
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -0,0 +1,1621 @@
+/*
+ * Copyright (C) 2008 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.location;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.location.Criteria;
+import android.location.IGpsStatusListener;
+import android.location.IGpsStatusProvider;
+import android.location.ILocationManager;
+import android.location.INetInitiatedListener;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.SntpClient;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.TelephonyManager;
+import android.telephony.gsm.GsmCellLocation;
+import android.telephony.SmsMessage;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.Phone;
+import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.util.HexDump;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.StringBufferInputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Properties;
+import java.util.Map.Entry;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A GPS implementation of LocationProvider used by LocationManager.
+ *
+ * {@hide}
+ */
+public class GpsLocationProvider implements LocationProviderInterface {
+
+ private static final String TAG = "GpsLocationProvider";
+
+ private static final boolean DEBUG = false;
+ private static final boolean VERBOSE = false;
+
+ // these need to match GpsPositionMode enum in gps.h
+ private static final int GPS_POSITION_MODE_STANDALONE = 0;
+ private static final int GPS_POSITION_MODE_MS_BASED = 1;
+ private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
+
+ // these need to match GpsPositionRecurrence enum in gps.h
+ private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
+ private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
+
+ // these need to match GpsStatusValue defines in gps.h
+ private static final int GPS_STATUS_NONE = 0;
+ private static final int GPS_STATUS_SESSION_BEGIN = 1;
+ private static final int GPS_STATUS_SESSION_END = 2;
+ private static final int GPS_STATUS_ENGINE_ON = 3;
+ private static final int GPS_STATUS_ENGINE_OFF = 4;
+
+ // these need to match GpsApgsStatusValue defines in gps.h
+ /** AGPS status event values. */
+ private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
+ private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
+ private static final int GPS_AGPS_DATA_CONNECTED = 3;
+ private static final int GPS_AGPS_DATA_CONN_DONE = 4;
+ private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
+
+ // these need to match GpsLocationFlags enum in gps.h
+ private static final int LOCATION_INVALID = 0;
+ private static final int LOCATION_HAS_LAT_LONG = 1;
+ private static final int LOCATION_HAS_ALTITUDE = 2;
+ private static final int LOCATION_HAS_SPEED = 4;
+ private static final int LOCATION_HAS_BEARING = 8;
+ private static final int LOCATION_HAS_ACCURACY = 16;
+
+// IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
+ private static final int GPS_DELETE_EPHEMERIS = 0x0001;
+ private static final int GPS_DELETE_ALMANAC = 0x0002;
+ private static final int GPS_DELETE_POSITION = 0x0004;
+ private static final int GPS_DELETE_TIME = 0x0008;
+ private static final int GPS_DELETE_IONO = 0x0010;
+ private static final int GPS_DELETE_UTC = 0x0020;
+ private static final int GPS_DELETE_HEALTH = 0x0040;
+ private static final int GPS_DELETE_SVDIR = 0x0080;
+ private static final int GPS_DELETE_SVSTEER = 0x0100;
+ private static final int GPS_DELETE_SADATA = 0x0200;
+ private static final int GPS_DELETE_RTI = 0x0400;
+ private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
+ private static final int GPS_DELETE_ALL = 0xFFFF;
+
+ // The GPS_CAPABILITY_* flags must match the values in gps.h
+ private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
+ private static final int GPS_CAPABILITY_MSB = 0x0000002;
+ private static final int GPS_CAPABILITY_MSA = 0x0000004;
+ private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
+
+
+ // these need to match AGpsType enum in gps.h
+ private static final int AGPS_TYPE_SUPL = 1;
+ private static final int AGPS_TYPE_C2K = 2;
+
+ // for mAGpsDataConnectionState
+ private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
+ private static final int AGPS_DATA_CONNECTION_OPENING = 1;
+ private static final int AGPS_DATA_CONNECTION_OPEN = 2;
+
+ // Handler messages
+ private static final int CHECK_LOCATION = 1;
+ private static final int ENABLE = 2;
+ private static final int ENABLE_TRACKING = 3;
+ private static final int UPDATE_NETWORK_STATE = 4;
+ private static final int INJECT_NTP_TIME = 5;
+ private static final int DOWNLOAD_XTRA_DATA = 6;
+ private static final int UPDATE_LOCATION = 7;
+ private static final int ADD_LISTENER = 8;
+ private static final int REMOVE_LISTENER = 9;
+ private static final int REQUEST_SINGLE_SHOT = 10;
+
+ // Request setid
+ private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
+ private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
+
+ // Request ref location
+ private static final int AGPS_RIL_REQUEST_REFLOC_CELLID = 1;
+ private static final int AGPS_RIL_REQUEST_REFLOC_MAC = 2;
+
+ // ref. location info
+ private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
+ private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
+ private static final int AGPS_REG_LOCATION_TYPE_MAC = 3;
+
+ // set id info
+ private static final int AGPS_SETID_TYPE_NONE = 0;
+ private static final int AGPS_SETID_TYPE_IMSI = 1;
+ private static final int AGPS_SETID_TYPE_MSISDN = 2;
+
+ private static final String PROPERTIES_FILE = "/etc/gps.conf";
+
+ private int mLocationFlags = LOCATION_INVALID;
+
+ // current status
+ private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
+
+ // time for last status update
+ private long mStatusUpdateTime = SystemClock.elapsedRealtime();
+
+ // turn off GPS fix icon if we haven't received a fix in 10 seconds
+ private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
+
+ // stop trying if we do not receive a fix within 60 seconds
+ private static final int NO_FIX_TIMEOUT = 60 * 1000;
+
+ // true if we are enabled
+ private volatile boolean mEnabled;
+
+ // true if we have network connectivity
+ private boolean mNetworkAvailable;
+
+ // flags to trigger NTP or XTRA data download when network becomes available
+ // initialized to true so we do NTP and XTRA when the network comes up after booting
+ private boolean mInjectNtpTimePending = true;
+ private boolean mDownloadXtraDataPending = false;
+
+ // true if GPS is navigating
+ private boolean mNavigating;
+
+ // true if GPS engine is on
+ private boolean mEngineOn;
+
+ // requested frequency of fixes, in milliseconds
+ private int mFixInterval = 1000;
+
+ // true if we started navigation
+ private boolean mStarted;
+
+ // true if single shot request is in progress
+ private boolean mSingleShot;
+
+ // capabilities of the GPS engine
+ private int mEngineCapabilities;
+
+ // true if XTRA is supported
+ private boolean mSupportsXtra;
+
+ // for calculating time to first fix
+ private long mFixRequestTime = 0;
+ // time to first fix for most recent session
+ private int mTTFF = 0;
+ // time we received our last fix
+ private long mLastFixTime;
+
+ private int mPositionMode;
+
+ // properties loaded from PROPERTIES_FILE
+ private Properties mProperties;
+ private String mNtpServer;
+ private String mSuplServerHost;
+ private int mSuplServerPort;
+ private String mC2KServerHost;
+ private int mC2KServerPort;
+
+ private final Context mContext;
+ private final ILocationManager mLocationManager;
+ private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
+ private Bundle mLocationExtras = new Bundle();
+ private ArrayList<Listener> mListeners = new ArrayList<Listener>();
+
+ // GpsLocationProvider's handler thread
+ private final Thread mThread;
+ // Handler for processing events in mThread.
+ private Handler mHandler;
+ // Used to signal when our main thread has initialized everything
+ private final CountDownLatch mInitializedLatch = new CountDownLatch(1);
+
+ private String mAGpsApn;
+ private int mAGpsDataConnectionState;
+ private final ConnectivityManager mConnMgr;
+ private final GpsNetInitiatedHandler mNIHandler;
+
+ // Wakelocks
+ private final static String WAKELOCK_KEY = "GpsLocationProvider";
+ private final PowerManager.WakeLock mWakeLock;
+ // bitfield of pending messages to our Handler
+ // used only for messages that cannot have multiple instances queued
+ private int mPendingMessageBits;
+ // separate counter for ADD_LISTENER and REMOVE_LISTENER messages,
+ // which might have multiple instances queued
+ private int mPendingListenerMessages;
+
+ // Alarms
+ private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
+ private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
+ private final AlarmManager mAlarmManager;
+ private final PendingIntent mWakeupIntent;
+ private final PendingIntent mTimeoutIntent;
+
+ private final IBatteryStats mBatteryStats;
+ private final SparseIntArray mClientUids = new SparseIntArray();
+
+ // how often to request NTP time, in milliseconds
+ // current setting 4 hours
+ private static final long NTP_INTERVAL = 4*60*60*1000;
+ // how long to wait if we have a network error in NTP or XTRA downloading
+ // current setting - 5 minutes
+ private static final long RETRY_INTERVAL = 5*60*1000;
+
+ // to avoid injecting bad NTP time, we reject any time fixes that differ from system time
+ // by more than 5 minutes.
+ private static final long MAX_NTP_SYSTEM_TIME_OFFSET = 5*60*1000;
+
+ private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
+ public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
+ if (listener == null) {
+ throw new NullPointerException("listener is null in addGpsStatusListener");
+ }
+
+ synchronized(mListeners) {
+ IBinder binder = listener.asBinder();
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener test = mListeners.get(i);
+ if (binder.equals(test.mListener.asBinder())) {
+ // listener already added
+ return;
+ }
+ }
+
+ Listener l = new Listener(listener);
+ binder.linkToDeath(l, 0);
+ mListeners.add(l);
+ }
+ }
+
+ public void removeGpsStatusListener(IGpsStatusListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("listener is null in addGpsStatusListener");
+ }
+
+ synchronized(mListeners) {
+ IBinder binder = listener.asBinder();
+ Listener l = null;
+ int size = mListeners.size();
+ for (int i = 0; i < size && l == null; i++) {
+ Listener test = mListeners.get(i);
+ if (binder.equals(test.mListener.asBinder())) {
+ l = test;
+ }
+ }
+
+ if (l != null) {
+ mListeners.remove(l);
+ binder.unlinkToDeath(l, 0);
+ }
+ }
+ }
+ };
+
+ public IGpsStatusProvider getGpsStatusProvider() {
+ return mGpsStatusProvider;
+ }
+
+ private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
+ @Override public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(ALARM_WAKEUP)) {
+ if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
+ startNavigating(false);
+ } else if (action.equals(ALARM_TIMEOUT)) {
+ if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
+ hibernate();
+ } else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
+ checkSmsSuplInit(intent);
+ } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
+ checkWapSuplInit(intent);
+ }
+ }
+ };
+
+ private void checkSmsSuplInit(Intent intent) {
+ SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
+ for (int i=0; i <messages.length; i++) {
+ byte[] supl_init = messages[i].getUserData();
+ native_agps_ni_message(supl_init,supl_init.length);
+ }
+ }
+
+ private void checkWapSuplInit(Intent intent) {
+ byte[] supl_init = (byte[]) intent.getExtra("data");
+ native_agps_ni_message(supl_init,supl_init.length);
+ }
+
+ public static boolean isSupported() {
+ return native_is_supported();
+ }
+
+ public GpsLocationProvider(Context context, ILocationManager locationManager) {
+ mContext = context;
+ mLocationManager = locationManager;
+ mNIHandler = new GpsNetInitiatedHandler(context);
+
+ mLocation.setExtras(mLocationExtras);
+
+ // Create a wake lock
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+ mWakeLock.setReferenceCounted(false);
+
+ mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
+ mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
+ intentFilter.addDataScheme("sms");
+ intentFilter.addDataAuthority("localhost","7275");
+ context.registerReceiver(mBroadcastReciever, intentFilter);
+
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
+ try {
+ intentFilter.addDataType("application/vnd.omaloc-supl-init");
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Log.w(TAG, "Malformed SUPL init mime type");
+ }
+ context.registerReceiver(mBroadcastReciever, intentFilter);
+
+ mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ // Battery statistics service to be notified when GPS turns on or off
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+ mProperties = new Properties();
+ try {
+ File file = new File(PROPERTIES_FILE);
+ FileInputStream stream = new FileInputStream(file);
+ mProperties.load(stream);
+ stream.close();
+ mNtpServer = mProperties.getProperty("NTP_SERVER", null);
+
+ mSuplServerHost = mProperties.getProperty("SUPL_HOST");
+ String portString = mProperties.getProperty("SUPL_PORT");
+ if (mSuplServerHost != null && portString != null) {
+ try {
+ mSuplServerPort = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
+ }
+ }
+
+ mC2KServerHost = mProperties.getProperty("C2K_HOST");
+ portString = mProperties.getProperty("C2K_PORT");
+ if (mC2KServerHost != null && portString != null) {
+ try {
+ mC2KServerPort = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse C2K_PORT: " + portString);
+ }
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
+ }
+
+ // wait until we are fully initialized before returning
+ mThread = new GpsLocationProviderThread();
+ mThread.start();
+ while (true) {
+ try {
+ mInitializedLatch.await();
+ break;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private void initialize() {
+ // register our receiver on our thread rather than the main thread
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ALARM_WAKEUP);
+ intentFilter.addAction(ALARM_TIMEOUT);
+ mContext.registerReceiver(mBroadcastReciever, intentFilter);
+ }
+
+ /**
+ * Returns the name of this provider.
+ */
+ public String getName() {
+ return LocationManager.GPS_PROVIDER;
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * data network (e.g., the Internet), false otherwise.
+ */
+ public boolean requiresNetwork() {
+ return true;
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ sendMessage(UPDATE_NETWORK_STATE, state, info);
+ }
+
+ private void handleUpdateNetworkState(int state, NetworkInfo info) {
+ mNetworkAvailable = (state == LocationProvider.AVAILABLE);
+
+ if (DEBUG) {
+ Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
+ + " info: " + info);
+ }
+
+ if (info != null) {
+ native_update_network_state(info.isConnected(), info.getType(),
+ info.isRoaming(), info.getExtraInfo());
+ }
+
+ if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
+ && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
+ String apnName = info.getExtraInfo();
+ if (mNetworkAvailable && apnName != null && apnName.length() > 0) {
+ mAGpsApn = apnName;
+ if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open");
+ native_agps_data_conn_open(apnName);
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
+ } else {
+ if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed");
+ mAGpsApn = null;
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ native_agps_data_conn_failed();
+ }
+ }
+
+ if (mNetworkAvailable) {
+ if (mInjectNtpTimePending) {
+ sendMessage(INJECT_NTP_TIME, 0, null);
+ }
+ if (mDownloadXtraDataPending) {
+ sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
+ }
+ }
+ }
+
+ private void handleInjectNtpTime() {
+ if (!mNetworkAvailable) {
+ // try again when network is up
+ mInjectNtpTimePending = true;
+ return;
+ }
+ mInjectNtpTimePending = false;
+
+ SntpClient client = new SntpClient();
+ long delay;
+
+ if (client.requestTime(mNtpServer, 10000)) {
+ long time = client.getNtpTime();
+ long timeReference = client.getNtpTimeReference();
+ int certainty = (int)(client.getRoundTripTime()/2);
+ long now = System.currentTimeMillis();
+
+ Log.d(TAG, "NTP server returned: "
+ + time + " (" + new Date(time)
+ + ") reference: " + timeReference
+ + " certainty: " + certainty
+ + " system time offset: " + (time - now));
+
+ native_inject_time(time, timeReference, certainty);
+ delay = NTP_INTERVAL;
+ } else {
+ if (DEBUG) Log.d(TAG, "requestTime failed");
+ delay = RETRY_INTERVAL;
+ }
+
+ // send delayed message for next NTP injection
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.removeMessages(INJECT_NTP_TIME);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+ }
+
+ private void handleDownloadXtraData() {
+ if (!mNetworkAvailable) {
+ // try again when network is up
+ mDownloadXtraDataPending = true;
+ return;
+ }
+ mDownloadXtraDataPending = false;
+
+
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
+ byte[] data = xtraDownloader.downloadXtraData();
+ if (data != null) {
+ if (DEBUG) {
+ Log.d(TAG, "calling native_inject_xtra_data");
+ }
+ native_inject_xtra_data(data, data.length);
+ } else {
+ // try again later
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
+ }
+ }
+
+ /**
+ * This is called to inform us when another location provider returns a location.
+ * Someday we might use this for network location injection to aid the GPS
+ */
+ public void updateLocation(Location location) {
+ sendMessage(UPDATE_LOCATION, 0, location);
+ }
+
+ private void handleUpdateLocation(Location location) {
+ if (location.hasAccuracy()) {
+ native_inject_location(location.getLatitude(), location.getLongitude(),
+ location.getAccuracy());
+ }
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * satellite-based positioning system (e.g., GPS), false
+ * otherwise.
+ */
+ public boolean requiresSatellite() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider requires access to an appropriate
+ * cellular network (e.g., to make use of cell tower IDs), false
+ * otherwise.
+ */
+ public boolean requiresCell() {
+ return false;
+ }
+
+ /**
+ * Returns true if the use of this provider may result in a
+ * monetary charge to the user, false if use is free. It is up to
+ * each provider to give accurate information.
+ */
+ public boolean hasMonetaryCost() {
+ return false;
+ }
+
+ /**
+ * Returns true if the provider is able to provide altitude
+ * information, false otherwise. A provider that reports altitude
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsAltitude() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider is able to provide speed
+ * information, false otherwise. A provider that reports speed
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsSpeed() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider is able to provide bearing
+ * information, false otherwise. A provider that reports bearing
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsBearing() {
+ return true;
+ }
+
+ /**
+ * Returns the power requirement for this provider.
+ *
+ * @return the power requirement for this provider, as one of the
+ * constants Criteria.POWER_REQUIREMENT_*.
+ */
+ public int getPowerRequirement() {
+ return Criteria.POWER_HIGH;
+ }
+
+ /**
+ * Returns true if this provider meets the given criteria,
+ * false otherwise.
+ */
+ public boolean meetsCriteria(Criteria criteria) {
+ return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
+ }
+
+ /**
+ * Returns the horizontal accuracy of this provider
+ *
+ * @return the accuracy of location from this provider, as one
+ * of the constants Criteria.ACCURACY_*.
+ */
+ public int getAccuracy() {
+ return Criteria.ACCURACY_FINE;
+ }
+
+ /**
+ * Enables this provider. When enabled, calls to getStatus()
+ * must be handled. Hardware may be started up
+ * when the provider is enabled.
+ */
+ public void enable() {
+ synchronized (mHandler) {
+ sendMessage(ENABLE, 1, null);
+ }
+ }
+
+ private void handleEnable() {
+ if (DEBUG) Log.d(TAG, "handleEnable");
+ if (mEnabled) return;
+ mEnabled = native_init();
+
+ if (mEnabled) {
+ mSupportsXtra = native_supports_xtra();
+ if (mSuplServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+ }
+ if (mC2KServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
+ }
+ } else {
+ Log.w(TAG, "Failed to enable location provider");
+ }
+ }
+
+ /**
+ * Disables this provider. When disabled, calls to getStatus()
+ * need not be handled. Hardware may be shut
+ * down while the provider is disabled.
+ */
+ public void disable() {
+ synchronized (mHandler) {
+ sendMessage(ENABLE, 0, null);
+ }
+ }
+
+ private void handleDisable() {
+ if (DEBUG) Log.d(TAG, "handleDisable");
+ if (!mEnabled) return;
+
+ mEnabled = false;
+ stopNavigating();
+
+ // do this before releasing wakelock
+ native_cleanup();
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public int getStatus(Bundle extras) {
+ if (extras != null) {
+ extras.putInt("satellites", mSvCount);
+ }
+ return mStatus;
+ }
+
+ private void updateStatus(int status, int svCount) {
+ if (status != mStatus || svCount != mSvCount) {
+ mStatus = status;
+ mSvCount = svCount;
+ mLocationExtras.putInt("satellites", svCount);
+ mStatusUpdateTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return mStatusUpdateTime;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ // FIXME - should set a flag here to avoid race conditions with single shot request
+ synchronized (mHandler) {
+ sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
+ }
+ }
+
+ private void handleEnableLocationTracking(boolean enable) {
+ if (enable) {
+ mTTFF = 0;
+ mLastFixTime = 0;
+ startNavigating(false);
+ } else {
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
+ stopNavigating();
+ }
+ }
+
+ public boolean requestSingleShotFix() {
+ if (mStarted) {
+ // cannot do single shot if already navigating
+ return false;
+ }
+ synchronized (mHandler) {
+ mHandler.removeMessages(REQUEST_SINGLE_SHOT);
+ Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT);
+ mHandler.sendMessage(m);
+ }
+ return true;
+ }
+
+ private void handleRequestSingleShot() {
+ mTTFF = 0;
+ mLastFixTime = 0;
+ startNavigating(true);
+ }
+
+ public void setMinTime(long minTime, WorkSource ws) {
+ if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
+
+ if (minTime >= 0) {
+ mFixInterval = (int)minTime;
+
+ if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
+ mFixInterval, 0, 0)) {
+ Log.e(TAG, "set_position_mode failed in setMinTime()");
+ }
+ }
+ }
+ }
+
+ public String getInternalState() {
+ return native_get_internal_state();
+ }
+
+ private final class Listener implements IBinder.DeathRecipient {
+ final IGpsStatusListener mListener;
+
+ int mSensors = 0;
+
+ Listener(IGpsStatusListener listener) {
+ mListener = listener;
+ }
+
+ public void binderDied() {
+ if (DEBUG) Log.d(TAG, "GPS status listener died");
+
+ synchronized(mListeners) {
+ mListeners.remove(this);
+ }
+ if (mListener != null) {
+ mListener.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+ }
+
+ public void addListener(int uid) {
+ synchronized (mWakeLock) {
+ mPendingListenerMessages++;
+ mWakeLock.acquire();
+ Message m = Message.obtain(mHandler, ADD_LISTENER);
+ m.arg1 = uid;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleAddListener(int uid) {
+ synchronized(mListeners) {
+ if (mClientUids.indexOfKey(uid) >= 0) {
+ // Shouldn't be here -- already have this uid.
+ Log.w(TAG, "Duplicate add listener for uid " + uid);
+ return;
+ }
+ mClientUids.put(uid, 0);
+ if (mNavigating) {
+ try {
+ mBatteryStats.noteStartGps(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in addListener");
+ }
+ }
+ }
+ }
+
+ public void removeListener(int uid) {
+ synchronized (mWakeLock) {
+ mPendingListenerMessages++;
+ mWakeLock.acquire();
+ Message m = Message.obtain(mHandler, REMOVE_LISTENER);
+ m.arg1 = uid;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleRemoveListener(int uid) {
+ synchronized(mListeners) {
+ if (mClientUids.indexOfKey(uid) < 0) {
+ // Shouldn't be here -- don't have this uid.
+ Log.w(TAG, "Unneeded remove listener for uid " + uid);
+ return;
+ }
+ mClientUids.delete(uid);
+ if (mNavigating) {
+ try {
+ mBatteryStats.noteStopGps(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in removeListener");
+ }
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+
+ long identity = Binder.clearCallingIdentity();
+ boolean result = false;
+
+ if ("delete_aiding_data".equals(command)) {
+ result = deleteAidingData(extras);
+ } else if ("force_time_injection".equals(command)) {
+ sendMessage(INJECT_NTP_TIME, 0, null);
+ result = true;
+ } else if ("force_xtra_injection".equals(command)) {
+ if (mSupportsXtra) {
+ xtraDownloadRequest();
+ result = true;
+ }
+ } else {
+ Log.w(TAG, "sendExtraCommand: unknown command " + command);
+ }
+
+ Binder.restoreCallingIdentity(identity);
+ return result;
+ }
+
+ private boolean deleteAidingData(Bundle extras) {
+ int flags;
+
+ if (extras == null) {
+ flags = GPS_DELETE_ALL;
+ } else {
+ flags = 0;
+ if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
+ if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
+ if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
+ if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
+ if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
+ if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
+ if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
+ if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
+ if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
+ if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
+ if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
+ if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
+ if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
+ }
+
+ if (flags != 0) {
+ native_delete_aiding_data(flags);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void startNavigating(boolean singleShot) {
+ if (!mStarted) {
+ if (DEBUG) Log.d(TAG, "startNavigating");
+ mStarted = true;
+ mSingleShot = singleShot;
+ mPositionMode = GPS_POSITION_MODE_STANDALONE;
+
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
+ if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
+ mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
+ } else if (hasCapability(GPS_CAPABILITY_MSB)) {
+ mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ }
+ }
+
+ int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
+ if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
+ interval, 0, 0)) {
+ mStarted = false;
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
+ if (!native_start()) {
+ mStarted = false;
+ Log.e(TAG, "native_start failed in startNavigating()");
+ return;
+ }
+
+ // reset SV count to zero
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
+ mFixRequestTime = System.currentTimeMillis();
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
+ // and our fix interval is not short
+ if (mFixInterval >= NO_FIX_TIMEOUT) {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
+ }
+ }
+ }
+ }
+
+ private void stopNavigating() {
+ if (DEBUG) Log.d(TAG, "stopNavigating");
+ if (mStarted) {
+ mStarted = false;
+ mSingleShot = false;
+ native_stop();
+ mTTFF = 0;
+ mLastFixTime = 0;
+ mLocationFlags = LOCATION_INVALID;
+
+ // reset SV count to zero
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
+ }
+ }
+
+ private void hibernate() {
+ // stop GPS until our next fix interval arrives
+ stopNavigating();
+ mAlarmManager.cancel(mTimeoutIntent);
+ mAlarmManager.cancel(mWakeupIntent);
+ long now = SystemClock.elapsedRealtime();
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent);
+ }
+
+ private boolean hasCapability(int capability) {
+ return ((mEngineCapabilities & capability) != 0);
+ }
+
+ /**
+ * called from native code to update our position.
+ */
+ private void reportLocation(int flags, double latitude, double longitude, double altitude,
+ float speed, float bearing, float accuracy, long timestamp) {
+ if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
+ " timestamp: " + timestamp);
+
+ synchronized (mLocation) {
+ mLocationFlags = flags;
+ if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mLocation.setLatitude(latitude);
+ mLocation.setLongitude(longitude);
+ mLocation.setTime(timestamp);
+ }
+ if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+ mLocation.setAltitude(altitude);
+ } else {
+ mLocation.removeAltitude();
+ }
+ if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+ mLocation.setSpeed(speed);
+ } else {
+ mLocation.removeSpeed();
+ }
+ if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+ mLocation.setBearing(bearing);
+ } else {
+ mLocation.removeBearing();
+ }
+ if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+ mLocation.setAccuracy(accuracy);
+ } else {
+ mLocation.removeAccuracy();
+ }
+
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+
+ mLastFixTime = System.currentTimeMillis();
+ // report time to first fix
+ if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mTTFF = (int)(mLastFixTime - mFixRequestTime);
+ if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF);
+
+ // notify status listeners
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onFirstFix(mTTFF);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in stopNavigating");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+
+ if (mSingleShot) {
+ stopNavigating();
+ }
+ if (mStarted && mStatus != LocationProvider.AVAILABLE) {
+ // we want to time out if we do not receive a fix
+ // within the time out and we are requesting infrequent fixes
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
+
+ // send an intent to notify that the GPS is receiving fixes.
+ Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
+ mContext.sendBroadcast(intent);
+ updateStatus(LocationProvider.AVAILABLE, mSvCount);
+ }
+
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted && mFixInterval > 1000) {
+ if (DEBUG) Log.d(TAG, "got fix, hibernating");
+ hibernate();
+ }
+ }
+
+ /**
+ * called from native code to update our status
+ */
+ private void reportStatus(int status) {
+ if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
+
+ synchronized(mListeners) {
+ boolean wasNavigating = mNavigating;
+
+ switch (status) {
+ case GPS_STATUS_SESSION_BEGIN:
+ mNavigating = true;
+ mEngineOn = true;
+ break;
+ case GPS_STATUS_SESSION_END:
+ mNavigating = false;
+ break;
+ case GPS_STATUS_ENGINE_ON:
+ mEngineOn = true;
+ break;
+ case GPS_STATUS_ENGINE_OFF:
+ mEngineOn = false;
+ mNavigating = false;
+ break;
+ }
+
+ if (wasNavigating != mNavigating) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ if (mNavigating) {
+ listener.mListener.onGpsStarted();
+ } else {
+ listener.mListener.onGpsStopped();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportStatus");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+
+ try {
+ // update battery stats
+ for (int i=mClientUids.size() - 1; i >= 0; i--) {
+ int uid = mClientUids.keyAt(i);
+ if (mNavigating) {
+ mBatteryStats.noteStartGps(uid);
+ } else {
+ mBatteryStats.noteStopGps(uid);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportStatus");
+ }
+
+ // send an intent to notify that the GPS has been enabled or disabled.
+ Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
+ mContext.sendBroadcast(intent);
+ }
+ }
+ }
+
+ /**
+ * called from native code to update SV info
+ */
+ private void reportSvStatus() {
+
+ int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
+
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
+ mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
+ mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportSvInfo");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "SV count: " + svCount +
+ " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
+ " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
+ for (int i = 0; i < svCount; i++) {
+ Log.v(TAG, "sv: " + mSvs[i] +
+ " snr: " + (float)mSnrs[i]/10 +
+ " elev: " + mSvElevations[i] +
+ " azimuth: " + mSvAzimuths[i] +
+ ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") +
+ ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " A") +
+ ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
+ }
+ }
+
+ // return number of sets used in fix instead of total
+ updateStatus(mStatus, Integer.bitCount(mSvMasks[USED_FOR_FIX_MASK]));
+
+ if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
+ System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
+ // send an intent to notify that the GPS is no longer receiving fixes.
+ Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
+ mContext.sendBroadcast(intent);
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
+ }
+ }
+
+ /**
+ * called from native code to update AGPS status
+ */
+ private void reportAGpsStatus(int type, int status) {
+ switch (status) {
+ case GPS_REQUEST_AGPS_DATA_CONN:
+ if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
+ // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
+ // to avoid a race condition with handleUpdateNetworkState()
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+ int result = mConnMgr.startUsingNetworkFeature(
+ ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+ if (result == Phone.APN_ALREADY_ACTIVE) {
+ if (DEBUG) Log.d(TAG, "Phone.APN_ALREADY_ACTIVE");
+ if (mAGpsApn != null) {
+ native_agps_data_conn_open(mAGpsApn);
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
+ } else {
+ Log.e(TAG, "mAGpsApn not set when receiving Phone.APN_ALREADY_ACTIVE");
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ native_agps_data_conn_failed();
+ }
+ } else if (result == Phone.APN_REQUEST_STARTED) {
+ if (DEBUG) Log.d(TAG, "Phone.APN_REQUEST_STARTED");
+ // Nothing to do here
+ } else {
+ if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed");
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ native_agps_data_conn_failed();
+ }
+ break;
+ case GPS_RELEASE_AGPS_DATA_CONN:
+ if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
+ if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
+ mConnMgr.stopUsingNetworkFeature(
+ ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+ native_agps_data_conn_closed();
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ }
+ break;
+ case GPS_AGPS_DATA_CONNECTED:
+ if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
+ break;
+ case GPS_AGPS_DATA_CONN_DONE:
+ if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
+ break;
+ case GPS_AGPS_DATA_CONN_FAILED:
+ if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
+ break;
+ }
+ }
+
+ /**
+ * called from native code to report NMEA data received
+ */
+ private void reportNmea(long timestamp) {
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ if (size > 0) {
+ // don't bother creating the String if we have no listeners
+ int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
+ String nmea = new String(mNmeaBuffer, 0, length);
+
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onNmeaReceived(timestamp, nmea);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportNmea");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * called from native code to inform us what the GPS engine capabilities are
+ */
+ private void setEngineCapabilities(int capabilities) {
+ mEngineCapabilities = capabilities;
+ }
+
+ /**
+ * called from native code to request XTRA data
+ */
+ private void xtraDownloadRequest() {
+ if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
+ sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
+ }
+
+ //=============================================================
+ // NI Client support
+ //=============================================================
+ private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
+ // Sends a response for an NI reqeust to HAL.
+ public boolean sendNiResponse(int notificationId, int userResponse)
+ {
+ // TODO Add Permission check
+
+ StringBuilder extrasBuf = new StringBuilder();
+
+ if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
+ ", response: " + userResponse);
+ native_send_ni_response(notificationId, userResponse);
+ return true;
+ }
+ };
+
+ public INetInitiatedListener getNetInitiatedListener() {
+ return mNetInitiatedListener;
+ }
+
+ // Called by JNI function to report an NI request.
+ public void reportNiNotification(
+ int notificationId,
+ int niType,
+ int notifyFlags,
+ int timeout,
+ int defaultResponse,
+ String requestorId,
+ String text,
+ int requestorIdEncoding,
+ int textEncoding,
+ String extras // Encoded extra data
+ )
+ {
+ Log.i(TAG, "reportNiNotification: entered");
+ Log.i(TAG, "notificationId: " + notificationId +
+ ", niType: " + niType +
+ ", notifyFlags: " + notifyFlags +
+ ", timeout: " + timeout +
+ ", defaultResponse: " + defaultResponse);
+
+ Log.i(TAG, "requestorId: " + requestorId +
+ ", text: " + text +
+ ", requestorIdEncoding: " + requestorIdEncoding +
+ ", textEncoding: " + textEncoding);
+
+ GpsNiNotification notification = new GpsNiNotification();
+
+ notification.notificationId = notificationId;
+ notification.niType = niType;
+ notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
+ notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
+ notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
+ notification.timeout = timeout;
+ notification.defaultResponse = defaultResponse;
+ notification.requestorId = requestorId;
+ notification.text = text;
+ notification.requestorIdEncoding = requestorIdEncoding;
+ notification.textEncoding = textEncoding;
+
+ // Process extras, assuming the format is
+ // one of more lines of "key = value"
+ Bundle bundle = new Bundle();
+
+ if (extras == null) extras = "";
+ Properties extraProp = new Properties();
+
+ try {
+ extraProp.load(new StringBufferInputStream(extras));
+ }
+ catch (IOException e)
+ {
+ Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
+ }
+
+ for (Entry<Object, Object> ent : extraProp.entrySet())
+ {
+ bundle.putString((String) ent.getKey(), (String) ent.getValue());
+ }
+
+ notification.extras = bundle;
+
+ mNIHandler.handleNiNotification(notification);
+ }
+
+ /**
+ * Called from native code to request set id info.
+ * We should be careful about receiving null string from the TelephonyManager,
+ * because sending null String to JNI function would cause a crash.
+ */
+
+ private void requestSetID(int flags) {
+ TelephonyManager phone = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ int type = AGPS_SETID_TYPE_NONE;
+ String data = "";
+
+ if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
+ String data_temp = phone.getSubscriberId();
+ if (data_temp == null) {
+ // This means the framework does not have the SIM card ready.
+ } else {
+ // This means the framework has the SIM card.
+ data = data_temp;
+ type = AGPS_SETID_TYPE_IMSI;
+ }
+ }
+ else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
+ String data_temp = phone.getLine1Number();
+ if (data_temp == null) {
+ // This means the framework does not have the SIM card ready.
+ } else {
+ // This means the framework has the SIM card.
+ data = data_temp;
+ type = AGPS_SETID_TYPE_MSISDN;
+ }
+ }
+ native_agps_set_id(type, data);
+ }
+
+ /**
+ * Called from native code to request reference location info
+ */
+
+ private void requestRefLocation(int flags) {
+ TelephonyManager phone = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
+ GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
+ if ((gsm_cell != null) && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) &&
+ (phone.getNetworkOperator() != null) &&
+ (phone.getNetworkOperator().length() > 3)) {
+ int type;
+ int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0,3));
+ int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
+ int networkType = phone.getNetworkType();
+ if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
+ || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
+ || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
+ || networkType == TelephonyManager.NETWORK_TYPE_HSPA) {
+ type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
+ } else {
+ type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
+ }
+ native_agps_set_ref_location_cellid(type, mcc, mnc,
+ gsm_cell.getLac(), gsm_cell.getCid());
+ } else {
+ Log.e(TAG,"Error getting cell location info.");
+ }
+ }
+ else {
+ Log.e(TAG,"CDMA not supported.");
+ }
+ }
+
+ private void sendMessage(int message, int arg, Object obj) {
+ // hold a wake lock while messages are pending
+ synchronized (mWakeLock) {
+ mPendingMessageBits |= (1 << message);
+ mWakeLock.acquire();
+ mHandler.removeMessages(message);
+ Message m = Message.obtain(mHandler, message);
+ m.arg1 = arg;
+ m.obj = obj;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private final class ProviderHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ int message = msg.what;
+ switch (message) {
+ case ENABLE:
+ if (msg.arg1 == 1) {
+ handleEnable();
+ } else {
+ handleDisable();
+ }
+ break;
+ case ENABLE_TRACKING:
+ handleEnableLocationTracking(msg.arg1 == 1);
+ break;
+ case REQUEST_SINGLE_SHOT:
+ handleRequestSingleShot();
+ break;
+ case UPDATE_NETWORK_STATE:
+ handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
+ break;
+ case INJECT_NTP_TIME:
+ handleInjectNtpTime();
+ break;
+ case DOWNLOAD_XTRA_DATA:
+ if (mSupportsXtra) {
+ handleDownloadXtraData();
+ }
+ break;
+ case UPDATE_LOCATION:
+ handleUpdateLocation((Location)msg.obj);
+ break;
+ case ADD_LISTENER:
+ handleAddListener(msg.arg1);
+ break;
+ case REMOVE_LISTENER:
+ handleRemoveListener(msg.arg1);
+ break;
+ }
+ // release wake lock if no messages are pending
+ synchronized (mWakeLock) {
+ mPendingMessageBits &= ~(1 << message);
+ if (message == ADD_LISTENER || message == REMOVE_LISTENER) {
+ mPendingListenerMessages--;
+ }
+ if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) {
+ mWakeLock.release();
+ }
+ }
+ }
+ };
+
+ private final class GpsLocationProviderThread extends Thread {
+
+ public GpsLocationProviderThread() {
+ super("GpsLocationProvider");
+ }
+
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ initialize();
+ Looper.prepare();
+ mHandler = new ProviderHandler();
+ // signal when we are initialized and ready to go
+ mInitializedLatch.countDown();
+ Looper.loop();
+ }
+ }
+
+ // for GPS SV statistics
+ private static final int MAX_SVS = 32;
+ private static final int EPHEMERIS_MASK = 0;
+ private static final int ALMANAC_MASK = 1;
+ private static final int USED_FOR_FIX_MASK = 2;
+
+ // preallocated arrays, to avoid memory allocation in reportStatus()
+ private int mSvs[] = new int[MAX_SVS];
+ private float mSnrs[] = new float[MAX_SVS];
+ private float mSvElevations[] = new float[MAX_SVS];
+ private float mSvAzimuths[] = new float[MAX_SVS];
+ private int mSvMasks[] = new int[3];
+ private int mSvCount;
+ // preallocated to avoid memory allocation in reportNmea()
+ private byte[] mNmeaBuffer = new byte[120];
+
+ static { class_init_native(); }
+ private static native void class_init_native();
+ private static native boolean native_is_supported();
+
+ private native boolean native_init();
+ private native void native_cleanup();
+ private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
+ int preferred_accuracy, int preferred_time);
+ private native boolean native_start();
+ private native boolean native_stop();
+ private native void native_delete_aiding_data(int flags);
+ // returns number of SVs
+ // mask[0] is ephemeris mask and mask[1] is almanac mask
+ private native int native_read_sv_status(int[] svs, float[] snrs,
+ float[] elevations, float[] azimuths, int[] masks);
+ private native int native_read_nmea(byte[] buffer, int bufferSize);
+ private native void native_inject_location(double latitude, double longitude, float accuracy);
+
+ // XTRA Support
+ private native void native_inject_time(long time, long timeReference, int uncertainty);
+ private native boolean native_supports_xtra();
+ private native void native_inject_xtra_data(byte[] data, int length);
+
+ // DEBUG Support
+ private native String native_get_internal_state();
+
+ // AGPS Support
+ private native void native_agps_data_conn_open(String apn);
+ private native void native_agps_data_conn_closed();
+ private native void native_agps_data_conn_failed();
+ private native void native_agps_ni_message(byte [] msg, int length);
+ private native void native_set_agps_server(int type, String hostname, int port);
+
+ // Network-initiated (NI) Support
+ private native void native_send_ni_response(int notificationId, int userResponse);
+
+ // AGPS ril suport
+ private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
+ int lac, int cid);
+ private native void native_agps_set_id(int type, String setid);
+
+ private native void native_update_network_state(boolean connected, int type,
+ boolean roaming, String extraInfo);
+}
diff --git a/services/java/com/android/server/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
new file mode 100644
index 0000000..813d255
--- /dev/null
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008 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.location;
+
+import android.content.Context;
+import android.net.Proxy;
+import android.net.http.AndroidHttpClient;
+import android.util.Config;
+import android.util.Log;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.params.ConnRouteParams;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Random;
+
+/**
+ * A class for downloading GPS XTRA data.
+ *
+ * {@hide}
+ */
+public class GpsXtraDownloader {
+
+ private static final String TAG = "GpsXtraDownloader";
+ static final boolean DEBUG = false;
+
+ private Context mContext;
+ private String[] mXtraServers;
+ // to load balance our server requests
+ private int mNextServerIndex;
+
+ GpsXtraDownloader(Context context, Properties properties) {
+ mContext = context;
+
+ // read XTRA servers from the Properties object
+ int count = 0;
+ String server1 = properties.getProperty("XTRA_SERVER_1");
+ String server2 = properties.getProperty("XTRA_SERVER_2");
+ String server3 = properties.getProperty("XTRA_SERVER_3");
+ if (server1 != null) count++;
+ if (server2 != null) count++;
+ if (server3 != null) count++;
+
+ if (count == 0) {
+ Log.e(TAG, "No XTRA servers were specified in the GPS configuration");
+ return;
+ } else {
+ mXtraServers = new String[count];
+ count = 0;
+ if (server1 != null) mXtraServers[count++] = server1;
+ if (server2 != null) mXtraServers[count++] = server2;
+ if (server3 != null) mXtraServers[count++] = server3;
+
+ // randomize first server
+ Random random = new Random();
+ mNextServerIndex = random.nextInt(count);
+ }
+ }
+
+ byte[] downloadXtraData() {
+ String proxyHost = Proxy.getHost(mContext);
+ int proxyPort = Proxy.getPort(mContext);
+ boolean useProxy = (proxyHost != null && proxyPort != -1);
+ byte[] result = null;
+ int startIndex = mNextServerIndex;
+
+ if (mXtraServers == null) {
+ return null;
+ }
+
+ // load balance our requests among the available servers
+ while (result == null) {
+ result = doDownload(mXtraServers[mNextServerIndex], useProxy, proxyHost, proxyPort);
+
+ // increment mNextServerIndex and wrap around if necessary
+ mNextServerIndex++;
+ if (mNextServerIndex == mXtraServers.length) {
+ mNextServerIndex = 0;
+ }
+ // break if we have tried all the servers
+ if (mNextServerIndex == startIndex) break;
+ }
+
+ return result;
+ }
+
+ protected static byte[] doDownload(String url, boolean isProxySet,
+ String proxyHost, int proxyPort) {
+ if (DEBUG) Log.d(TAG, "Downloading XTRA data from " + url);
+
+ AndroidHttpClient client = null;
+ try {
+ client = AndroidHttpClient.newInstance("Android");
+ HttpUriRequest req = new HttpGet(url);
+
+ if (isProxySet) {
+ HttpHost proxy = new HttpHost(proxyHost, proxyPort);
+ ConnRouteParams.setDefaultProxy(req.getParams(), proxy);
+ }
+
+ req.addHeader(
+ "Accept",
+ "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
+
+ req.addHeader(
+ "x-wap-profile",
+ "http://www.openmobilealliance.org/tech/profiles/UAPROF/ccppschema-20021212#");
+
+ HttpResponse response = client.execute(req);
+ StatusLine status = response.getStatusLine();
+ if (status.getStatusCode() != 200) { // HTTP 200 is success.
+ if (DEBUG) Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
+ return null;
+ }
+
+ HttpEntity entity = response.getEntity();
+ byte[] body = null;
+ if (entity != null) {
+ try {
+ if (entity.getContentLength() > 0) {
+ body = new byte[(int) entity.getContentLength()];
+ DataInputStream dis = new DataInputStream(entity.getContent());
+ try {
+ dis.readFully(body);
+ } finally {
+ try {
+ dis.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Unexpected IOException.", e);
+ }
+ }
+ }
+ } finally {
+ if (entity != null) {
+ entity.consumeContent();
+ }
+ }
+ }
+ return body;
+ } catch (Exception e) {
+ if (DEBUG) Log.d(TAG, "error " + e);
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
new file mode 100644
index 0000000..858a582
--- /dev/null
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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.location;
+
+import android.location.Criteria;
+import android.location.Location;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.WorkSource;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * {@hide}
+ */
+public interface LocationProviderInterface {
+ String getName();
+ boolean requiresNetwork();
+ boolean requiresSatellite();
+ boolean requiresCell();
+ boolean hasMonetaryCost();
+ boolean supportsAltitude();
+ boolean supportsSpeed();
+ boolean supportsBearing();
+ int getPowerRequirement();
+ boolean meetsCriteria(Criteria criteria);
+ int getAccuracy();
+ boolean isEnabled();
+ void enable();
+ void disable();
+ int getStatus(Bundle extras);
+ long getStatusUpdateTime();
+ void enableLocationTracking(boolean enable);
+ /* returns false if single shot is not supported */
+ boolean requestSingleShotFix();
+ String getInternalState();
+ void setMinTime(long minTime, WorkSource ws);
+ void updateNetworkState(int state, NetworkInfo info);
+ void updateLocation(Location location);
+ boolean sendExtraCommand(String command, Bundle extras);
+ void addListener(int uid);
+ void removeListener(int uid);
+}
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
new file mode 100644
index 0000000..1a1a170
--- /dev/null
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -0,0 +1,471 @@
+/*
+ * 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.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Criteria;
+import android.location.ILocationProvider;
+import android.location.Location;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.util.Log;
+
+import com.android.internal.location.DummyLocationProvider;
+
+/**
+ * A class for proxying location providers implemented as services.
+ *
+ * {@hide}
+ */
+public class LocationProviderProxy implements LocationProviderInterface {
+
+ private static final String TAG = "LocationProviderProxy";
+
+ private final Context mContext;
+ private final String mName;
+ private final Intent mIntent;
+ private final Handler mHandler;
+ private final Object mMutex = new Object(); // synchronizes access to non-final members
+ private Connection mServiceConnection = new Connection(); // never null
+
+ // cached values set by the location manager
+ private boolean mLocationTracking = false;
+ private boolean mEnabled = false;
+ private long mMinTime = -1;
+ private WorkSource mMinTimeSource = new WorkSource();
+ private int mNetworkState;
+ private NetworkInfo mNetworkInfo;
+
+ // constructor for proxying location providers implemented in a separate service
+ public LocationProviderProxy(Context context, String name, String serviceName,
+ Handler handler) {
+ mContext = context;
+ mName = name;
+ mIntent = new Intent(serviceName);
+ mHandler = handler;
+ mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * When unbundled NetworkLocationService package is updated, we
+ * need to unbind from the old version and re-bind to the new one.
+ */
+ public void reconnect() {
+ synchronized (mMutex) {
+ mContext.unbindService(mServiceConnection);
+ mServiceConnection = new Connection();
+ mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ private class Connection implements ServiceConnection, Runnable {
+
+ private ILocationProvider mProvider;
+
+ // for caching requiresNetwork, requiresSatellite, etc.
+ private DummyLocationProvider mCachedAttributes; // synchronized by mMutex
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className);
+ synchronized (this) {
+ mProvider = ILocationProvider.Stub.asInterface(service);
+ if (mProvider != null) {
+ mHandler.post(this);
+ }
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className);
+ synchronized (this) {
+ mProvider = null;
+ }
+ }
+
+ public synchronized ILocationProvider getProvider() {
+ return mProvider;
+ }
+
+ public synchronized DummyLocationProvider getCachedAttributes() {
+ return mCachedAttributes;
+ }
+
+ public void run() {
+ synchronized (mMutex) {
+ if (mServiceConnection != this) {
+ // This ServiceConnection no longer the one we want to bind to.
+ return;
+ }
+ ILocationProvider provider = getProvider();
+ if (provider == null) {
+ return;
+ }
+
+ // resend previous values from the location manager if the service has restarted
+ try {
+ if (mEnabled) {
+ provider.enable();
+ }
+ if (mLocationTracking) {
+ provider.enableLocationTracking(true);
+ }
+ if (mMinTime >= 0) {
+ provider.setMinTime(mMinTime, mMinTimeSource);
+ }
+ if (mNetworkInfo != null) {
+ provider.updateNetworkState(mNetworkState, mNetworkInfo);
+ }
+ } catch (RemoteException e) {
+ }
+
+ // init cache of parameters
+ if (mCachedAttributes == null) {
+ try {
+ mCachedAttributes = new DummyLocationProvider(mName, null);
+ mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
+ mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
+ mCachedAttributes.setRequiresCell(provider.requiresCell());
+ mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
+ mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
+ mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
+ mCachedAttributes.setSupportsBearing(provider.supportsBearing());
+ mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
+ mCachedAttributes.setAccuracy(provider.getAccuracy());
+ } catch (RemoteException e) {
+ mCachedAttributes = null;
+ }
+ }
+ }
+ }
+ };
+
+ public String getName() {
+ return mName;
+ }
+
+ private DummyLocationProvider getCachedAttributes() {
+ synchronized (mMutex) {
+ return mServiceConnection.getCachedAttributes();
+ }
+ }
+
+ public boolean requiresNetwork() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.requiresNetwork();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean requiresSatellite() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.requiresSatellite();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean requiresCell() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.requiresCell();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean hasMonetaryCost() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.hasMonetaryCost();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsAltitude() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.supportsAltitude();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsSpeed() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.supportsSpeed();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsBearing() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.supportsBearing();
+ } else {
+ return false;
+ }
+ }
+
+ public int getPowerRequirement() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.getPowerRequirement();
+ } else {
+ return -1;
+ }
+ }
+
+ public int getAccuracy() {
+ DummyLocationProvider cachedAttributes = getCachedAttributes();
+ if (cachedAttributes != null) {
+ return cachedAttributes.getAccuracy();
+ } else {
+ return -1;
+ }
+ }
+
+ public boolean meetsCriteria(Criteria criteria) {
+ synchronized (mMutex) {
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ return provider.meetsCriteria(criteria);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ // default implementation if we lost connection to the provider
+ if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
+ (criteria.getAccuracy() < getAccuracy())) {
+ return false;
+ }
+ int criteriaPower = criteria.getPowerRequirement();
+ if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
+ (criteriaPower < getPowerRequirement())) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !supportsAltitude()) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !supportsSpeed()) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !supportsBearing()) {
+ return false;
+ }
+ return true;
+ }
+
+ public void enable() {
+ synchronized (mMutex) {
+ mEnabled = true;
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ provider.enable();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public void disable() {
+ synchronized (mMutex) {
+ mEnabled = false;
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ provider.disable();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ synchronized (mMutex) {
+ return mEnabled;
+ }
+ }
+
+ public int getStatus(Bundle extras) {
+ ILocationProvider provider;
+ synchronized (mMutex) {
+ provider = mServiceConnection.getProvider();
+ }
+ if (provider != null) {
+ try {
+ return provider.getStatus(extras);
+ } catch (RemoteException e) {
+ }
+ }
+ return 0;
+ }
+
+ public long getStatusUpdateTime() {
+ ILocationProvider provider;
+ synchronized (mMutex) {
+ provider = mServiceConnection.getProvider();
+ }
+ if (provider != null) {
+ try {
+ return provider.getStatusUpdateTime();
+ } catch (RemoteException e) {
+ }
+ }
+ return 0;
+ }
+
+ public String getInternalState() {
+ ILocationProvider provider;
+ synchronized (mMutex) {
+ provider = mServiceConnection.getProvider();
+ }
+ if (provider != null) {
+ try {
+ return provider.getInternalState();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getInternalState failed", e);
+ }
+ }
+ return null;
+ }
+
+ public boolean isLocationTracking() {
+ synchronized (mMutex) {
+ return mLocationTracking;
+ }
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ synchronized (mMutex) {
+ mLocationTracking = enable;
+ if (!enable) {
+ mMinTime = -1;
+ mMinTimeSource.clear();
+ }
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ provider.enableLocationTracking(enable);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
+ public long getMinTime() {
+ synchronized (mMutex) {
+ return mMinTime;
+ }
+ }
+
+ public void setMinTime(long minTime, WorkSource ws) {
+ synchronized (mMutex) {
+ mMinTime = minTime;
+ mMinTimeSource.set(ws);
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ provider.setMinTime(minTime, ws);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ synchronized (mMutex) {
+ mNetworkState = state;
+ mNetworkInfo = info;
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ provider.updateNetworkState(state, info);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public void updateLocation(Location location) {
+ synchronized (mMutex) {
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ provider.updateLocation(location);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ synchronized (mMutex) {
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ return provider.sendExtraCommand(command, extras);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ return false;
+ }
+
+ public void addListener(int uid) {
+ synchronized (mMutex) {
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ provider.addListener(uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public void removeListener(int uid) {
+ synchronized (mMutex) {
+ ILocationProvider provider = mServiceConnection.getProvider();
+ if (provider != null) {
+ try {
+ provider.removeListener(uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
new file mode 100644
index 0000000..09d799f
--- /dev/null
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -0,0 +1,234 @@
+/*
+ * 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.location;
+
+import android.location.Criteria;
+import android.location.ILocationManager;
+import android.location.Location;
+import android.location.LocationProvider;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.WorkSource;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+
+import java.io.PrintWriter;
+
+/**
+ * A mock location provider used by LocationManagerService to implement test providers.
+ *
+ * {@hide}
+ */
+public class MockProvider implements LocationProviderInterface {
+ private final String mName;
+ private final ILocationManager mLocationManager;
+ private final boolean mRequiresNetwork;
+ private final boolean mRequiresSatellite;
+ private final boolean mRequiresCell;
+ private final boolean mHasMonetaryCost;
+ private final boolean mSupportsAltitude;
+ private final boolean mSupportsSpeed;
+ private final boolean mSupportsBearing;
+ private final int mPowerRequirement;
+ private final int mAccuracy;
+ private final Location mLocation;
+ private int mStatus;
+ private long mStatusUpdateTime;
+ private final Bundle mExtras = new Bundle();
+ private boolean mHasLocation;
+ private boolean mHasStatus;
+ private boolean mEnabled;
+
+ private static final String TAG = "MockProvider";
+
+ public MockProvider(String name, ILocationManager locationManager,
+ boolean requiresNetwork, boolean requiresSatellite,
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ mName = name;
+ mLocationManager = locationManager;
+ mRequiresNetwork = requiresNetwork;
+ mRequiresSatellite = requiresSatellite;
+ mRequiresCell = requiresCell;
+ mHasMonetaryCost = hasMonetaryCost;
+ mSupportsAltitude = supportsAltitude;
+ mSupportsBearing = supportsBearing;
+ mSupportsSpeed = supportsSpeed;
+ mPowerRequirement = powerRequirement;
+ mAccuracy = accuracy;
+ mLocation = new Location(name);
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void disable() {
+ mEnabled = false;
+ }
+
+ public void enable() {
+ mEnabled = true;
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public int getStatus(Bundle extras) {
+ if (mHasStatus) {
+ extras.clear();
+ extras.putAll(mExtras);
+ return mStatus;
+ } else {
+ return LocationProvider.AVAILABLE;
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return mStatusUpdateTime;
+ }
+
+ public int getAccuracy() {
+ return mAccuracy;
+ }
+
+ public int getPowerRequirement() {
+ return mPowerRequirement;
+ }
+
+ public boolean hasMonetaryCost() {
+ return mHasMonetaryCost;
+ }
+
+ public boolean requiresCell() {
+ return mRequiresCell;
+ }
+
+ public boolean requiresNetwork() {
+ return mRequiresNetwork;
+ }
+
+ public boolean requiresSatellite() {
+ return mRequiresSatellite;
+ }
+
+ public boolean supportsAltitude() {
+ return mSupportsAltitude;
+ }
+
+ public boolean supportsBearing() {
+ return mSupportsBearing;
+ }
+
+ public boolean supportsSpeed() {
+ return mSupportsSpeed;
+ }
+
+ public boolean meetsCriteria(Criteria criteria) {
+ if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
+ (criteria.getAccuracy() < mAccuracy)) {
+ return false;
+ }
+ int criteriaPower = criteria.getPowerRequirement();
+ if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
+ (criteriaPower < mPowerRequirement)) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !mSupportsAltitude) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !mSupportsSpeed) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !mSupportsBearing) {
+ return false;
+ }
+ return true;
+ }
+
+ public void setLocation(Location l) {
+ mLocation.set(l);
+ mHasLocation = true;
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+
+ public void clearLocation() {
+ mHasLocation = false;
+ }
+
+ public void setStatus(int status, Bundle extras, long updateTime) {
+ mStatus = status;
+ mStatusUpdateTime = updateTime;
+ mExtras.clear();
+ if (extras != null) {
+ mExtras.putAll(extras);
+ }
+ mHasStatus = true;
+ }
+
+ public void clearStatus() {
+ mHasStatus = false;
+ mStatusUpdateTime = 0;
+ }
+
+ public String getInternalState() {
+ return null;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ }
+
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
+ public void setMinTime(long minTime, WorkSource ws) {
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ }
+
+ public void updateLocation(Location location) {
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
+
+ public void addListener(int uid) {
+ }
+
+ public void removeListener(int uid) {
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + mName);
+ pw.println(prefix + "mHasLocation=" + mHasLocation);
+ pw.println(prefix + "mLocation:");
+ mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
+ pw.println(prefix + "mHasStatus=" + mHasStatus);
+ pw.println(prefix + "mStatus=" + mStatus);
+ pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
+ pw.println(prefix + "mExtras=" + mExtras);
+ }
+}
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
new file mode 100644
index 0000000..ea0d1b0
--- /dev/null
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2010 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.location;
+
+import android.location.Criteria;
+import android.location.ILocationManager;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.WorkSource;
+import android.util.Log;
+
+/**
+ * A passive location provider reports locations received from other providers
+ * for clients that want to listen passively without actually triggering
+ * location updates.
+ *
+ * {@hide}
+ */
+public class PassiveProvider implements LocationProviderInterface {
+
+ private static final String TAG = "PassiveProvider";
+
+ private final ILocationManager mLocationManager;
+ private boolean mTracking;
+
+ public PassiveProvider(ILocationManager locationManager) {
+ mLocationManager = locationManager;
+ }
+
+ public String getName() {
+ return LocationManager.PASSIVE_PROVIDER;
+ }
+
+ public boolean requiresNetwork() {
+ return false;
+ }
+
+ public boolean requiresSatellite() {
+ return false;
+ }
+
+ public boolean requiresCell() {
+ return false;
+ }
+
+ public boolean hasMonetaryCost() {
+ return false;
+ }
+
+ public boolean supportsAltitude() {
+ return false;
+ }
+
+ public boolean supportsSpeed() {
+ return false;
+ }
+
+ public boolean supportsBearing() {
+ return false;
+ }
+
+ public int getPowerRequirement() {
+ return -1;
+ }
+
+ public boolean meetsCriteria(Criteria criteria) {
+ // We do not want to match the special passive provider based on criteria.
+ return false;
+ }
+
+ public int getAccuracy() {
+ return -1;
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public void enable() {
+ }
+
+ public void disable() {
+ }
+
+ public int getStatus(Bundle extras) {
+ if (mTracking) {
+ return LocationProvider.AVAILABLE;
+ } else {
+ return LocationProvider.TEMPORARILY_UNAVAILABLE;
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return -1;
+ }
+
+ public String getInternalState() {
+ return null;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ mTracking = enable;
+ }
+
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
+ public void setMinTime(long minTime, WorkSource ws) {
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ }
+
+ public void updateLocation(Location location) {
+ if (mTracking) {
+ try {
+ // pass the location back to the location manager
+ mLocationManager.reportLocation(location, true);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
+
+ public void addListener(int uid) {
+ }
+
+ public void removeListener(int uid) {
+ }
+}
diff --git a/services/java/com/android/server/status/AnimatedImageView.java b/services/java/com/android/server/status/AnimatedImageView.java
deleted file mode 100644
index 97df065..0000000
--- a/services/java/com/android/server/status/AnimatedImageView.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.RemoteViews.RemoteView;
-
-@RemoteView
-public class AnimatedImageView extends ImageView {
- AnimationDrawable mAnim;
- boolean mAttached;
-
- public AnimatedImageView(Context context) {
- super(context);
- }
-
- public AnimatedImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- private void updateAnim() {
- Drawable drawable = getDrawable();
- if (mAttached && mAnim != null) {
- mAnim.stop();
- }
- if (drawable instanceof AnimationDrawable) {
- mAnim = (AnimationDrawable)drawable;
- if (mAttached) {
- mAnim.start();
- }
- } else {
- mAnim = null;
- }
- }
-
- @Override
- public void setImageDrawable(Drawable drawable) {
- super.setImageDrawable(drawable);
- updateAnim();
- }
-
- @Override
- @android.view.RemotableViewMethod
- public void setImageResource(int resid) {
- super.setImageResource(resid);
- updateAnim();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mAnim != null) {
- mAnim.start();
- }
- mAttached = true;
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAnim != null) {
- mAnim.stop();
- }
- mAttached = false;
- }
-}
-
diff --git a/services/java/com/android/server/status/CloseDragHandle.java b/services/java/com/android/server/status/CloseDragHandle.java
deleted file mode 100644
index ad1ac4d..0000000
--- a/services/java/com/android/server/status/CloseDragHandle.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.LinearLayout;
-
-
-public class CloseDragHandle extends LinearLayout {
- StatusBarService mService;
-
- public CloseDragHandle(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /**
- * Ensure that, if there is no target under us to receive the touch,
- * that we process it ourself. This makes sure that onInterceptTouchEvent()
- * is always called for the entire gesture.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() != MotionEvent.ACTION_DOWN) {
- mService.interceptTouchEvent(event);
- }
- return true;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return mService.interceptTouchEvent(event)
- ? true : super.onInterceptTouchEvent(event);
- }
-}
-
diff --git a/services/java/com/android/server/status/DateView.java b/services/java/com/android/server/status/DateView.java
deleted file mode 100644
index c04fb45..0000000
--- a/services/java/com/android/server/status/DateView.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.widget.TextView;
-import android.view.MotionEvent;
-
-import java.text.DateFormat;
-import java.util.Date;
-
-public final class DateView extends TextView {
- private static final String TAG = "DateView";
-
- private boolean mUpdating = false;
-
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_TIME_TICK)
- || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- updateClock();
- }
- }
- };
-
- public DateView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- setUpdates(false);
- }
-
- @Override
- protected int getSuggestedMinimumWidth() {
- // makes the large background bitmap not force us to full width
- return 0;
- }
-
- private final void updateClock() {
- Date now = new Date();
- setText(DateFormat.getDateInstance(DateFormat.LONG).format(now));
- }
-
- void setUpdates(boolean update) {
- if (update != mUpdating) {
- mUpdating = update;
- if (update) {
- // Register for Intent broadcasts for the clock and battery
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- mContext.registerReceiver(mIntentReceiver, filter, null, null);
- updateClock();
- } else {
- mContext.unregisterReceiver(mIntentReceiver);
- }
- }
- }
-}
-
diff --git a/services/java/com/android/server/status/ExpandedView.java b/services/java/com/android/server/status/ExpandedView.java
deleted file mode 100644
index cb37f90..0000000
--- a/services/java/com/android/server/status/ExpandedView.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.util.Slog;
-
-
-public class ExpandedView extends LinearLayout {
- StatusBarService mService;
- int mPrevHeight = -1;
-
- public ExpandedView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- }
-
- /** We want to shrink down to 0, and ignore the background. */
- @Override
- public int getSuggestedMinimumHeight() {
- return 0;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- int height = bottom - top;
- if (height != mPrevHeight) {
- //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight + " new=" + height);
- mPrevHeight = height;
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
- }
- }
-}
diff --git a/services/java/com/android/server/status/FixedSizeDrawable.java b/services/java/com/android/server/status/FixedSizeDrawable.java
deleted file mode 100644
index dbfcb2c..0000000
--- a/services/java/com/android/server/status/FixedSizeDrawable.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.graphics.drawable.Drawable;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.util.Slog;
-
-class FixedSizeDrawable extends Drawable {
- Drawable mDrawable;
- int mLeft;
- int mTop;
- int mRight;
- int mBottom;
-
- FixedSizeDrawable(Drawable that) {
- mDrawable = that;
- }
-
- public void setFixedBounds(int l, int t, int r, int b) {
- mLeft = l;
- mTop = t;
- mRight = r;
- mBottom = b;
- }
-
- public void setBounds(Rect bounds) {
- mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
- }
-
- public void setBounds(int l, int t, int r, int b) {
- mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
- }
-
- public void draw(Canvas canvas) {
- mDrawable.draw(canvas);
- }
-
- public int getOpacity() {
- return mDrawable.getOpacity();
- }
-
- public void setAlpha(int alpha) {
- mDrawable.setAlpha(alpha);
- }
-
- public void setColorFilter(ColorFilter cf) {
- mDrawable.setColorFilter(cf);
- }
-}
diff --git a/services/java/com/android/server/status/IconData.java b/services/java/com/android/server/status/IconData.java
deleted file mode 100644
index fd226f9..0000000
--- a/services/java/com/android/server/status/IconData.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.util.Slog;
-
-public class IconData {
- /**
- * Indicates ths item represents a piece of text.
- */
- public static final int TEXT = 1;
-
- /**
- * Indicates ths item represents an icon.
- */
- public static final int ICON = 2;
-
- /**
- * The type of this item. One of TEXT, ICON, or LEVEL_ICON.
- */
- public int type;
-
- /**
- * The slot that this icon will be in if it is not a notification
- */
- public String slot;
-
- /**
- * The package containting the icon to draw for this item. Valid if this is
- * an ICON type.
- */
- public String iconPackage;
-
- /**
- * The icon to draw for this item. Valid if this is an ICON type.
- */
- public int iconId;
-
- /**
- * The level associated with the icon. Valid if this is a LEVEL_ICON type.
- */
- public int iconLevel;
-
- /**
- * The "count" number.
- */
- public int number;
-
- /**
- * The text associated with the icon. Valid if this is a TEXT type.
- */
- public CharSequence text;
-
- private IconData() {
- }
-
- public static IconData makeIcon(String slot,
- String iconPackage, int iconId, int iconLevel, int number) {
- IconData data = new IconData();
- data.type = ICON;
- data.slot = slot;
- data.iconPackage = iconPackage;
- data.iconId = iconId;
- data.iconLevel = iconLevel;
- data.number = number;
- return data;
- }
-
- public static IconData makeText(String slot, CharSequence text) {
- IconData data = new IconData();
- data.type = TEXT;
- data.slot = slot;
- data.text = text;
- return data;
- }
-
- public void copyFrom(IconData that) {
- this.type = that.type;
- this.slot = that.slot;
- this.iconPackage = that.iconPackage;
- this.iconId = that.iconId;
- this.iconLevel = that.iconLevel;
- this.number = that.number;
- this.text = that.text; // should we clone this?
- }
-
- public IconData clone() {
- IconData that = new IconData();
- that.copyFrom(this);
- return that;
- }
-
- public String toString() {
- if (this.type == TEXT) {
- return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
- + " text='" + this.text + "')";
- }
- else if (this.type == ICON) {
- return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
- + " package=" + this.iconPackage
- + " iconId=" + Integer.toHexString(this.iconId)
- + " iconLevel=" + this.iconLevel + ")";
- }
- else {
- return "IconData(type=" + type + ")";
- }
- }
-}
diff --git a/services/java/com/android/server/status/IconMerger.java b/services/java/com/android/server/status/IconMerger.java
deleted file mode 100644
index aa702ae..0000000
--- a/services/java/com/android/server/status/IconMerger.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-
-public class IconMerger extends LinearLayout {
- StatusBarService service;
- StatusBarIcon moreIcon;
-
- public IconMerger(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
-
- final int maxWidth = r - l;
- final int N = getChildCount();
- int i;
-
- // get the rightmost one, and see if we even need to do anything
- int fitRight = -1;
- for (i=N-1; i>=0; i--) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- fitRight = child.getRight();
- break;
- }
- }
-
- // find the first visible one that isn't the more icon
- View moreView = null;
- int fitLeft = -1;
- int startIndex = -1;
- for (i=0; i<N; i++) {
- final View child = getChildAt(i);
- if (com.android.internal.R.drawable.stat_notify_more == child.getId()) {
- moreView = child;
- startIndex = i+1;
- }
- else if (child.getVisibility() != GONE) {
- fitLeft = child.getLeft();
- break;
- }
- }
-
- if (moreView == null || startIndex < 0) {
- throw new RuntimeException("Status Bar / IconMerger moreView == null");
- }
-
- // if it fits without the more icon, then hide the more icon and update fitLeft
- // so everything gets pushed left
- int adjust = 0;
- if (fitRight - fitLeft <= maxWidth) {
- adjust = fitLeft - moreView.getLeft();
- fitLeft -= adjust;
- fitRight -= adjust;
- moreView.layout(0, moreView.getTop(), 0, moreView.getBottom());
- }
- int extra = fitRight - r;
- int shift = -1;
-
- int breakingPoint = fitLeft + extra + adjust;
- int number = 0;
- for (i=startIndex; i<N; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- int childLeft = child.getLeft();
- int childRight = child.getRight();
- if (childLeft < breakingPoint) {
- // hide this one
- child.layout(0, child.getTop(), 0, child.getBottom());
- int n = this.service.getIconNumberForView(child);
- if (n == 0) {
- number += 1;
- } else if (n > 0) {
- number += n;
- }
- } else {
- // decide how much to shift by
- if (shift < 0) {
- shift = childLeft - fitLeft;
- }
- // shift this left by shift
- child.layout(childLeft-shift, child.getTop(),
- childRight-shift, child.getBottom());
- }
- }
- }
-
- // BUG: Updating the text during the layout here doesn't seem to cause
- // the view to be redrawn fully. The text view gets resized correctly, but the
- // text contents aren't drawn properly. To work around this, we post a message
- // and provide the value later. We're the only one changing this value show it
- // should be ordered correctly.
- if (false) {
- this.moreIcon.update(number);
- } else {
- mBugWorkaroundNumber = number;
- mBugWorkaroundHandler.post(mBugWorkaroundRunnable);
- }
- }
-
- private int mBugWorkaroundNumber;
- private Handler mBugWorkaroundHandler = new Handler();
- private Runnable mBugWorkaroundRunnable = new Runnable() {
- public void run() {
- IconMerger.this.moreIcon.update(mBugWorkaroundNumber);
- IconMerger.this.moreIcon.view.invalidate();
- }
- };
-}
diff --git a/services/java/com/android/server/status/LatestItemView.java b/services/java/com/android/server/status/LatestItemView.java
deleted file mode 100644
index fe8d164..0000000
--- a/services/java/com/android/server/status/LatestItemView.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-public class LatestItemView extends FrameLayout {
-
- public LatestItemView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public boolean dispatchTouchEvent(MotionEvent ev) {
- return onTouchEvent(ev);
- }
-}
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
deleted file mode 100644
index 71f01ca..0000000
--- a/services/java/com/android/server/status/NotificationData.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.app.PendingIntent;
-import android.widget.RemoteViews;
-
-public class NotificationData {
- public String pkg;
- public String tag;
- public int id;
- public CharSequence tickerText;
-
- public long when;
- public boolean ongoingEvent;
- public boolean clearable;
-
- public RemoteViews contentView;
- public PendingIntent contentIntent;
-
- public PendingIntent deleteIntent;
-
- public String toString() {
- return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText
- + " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
- + " deleteIntent=" + deleteIntent
- + " clearable=" + clearable
- + " contentView=" + contentView + " when=" + when + ")";
- }
-}
diff --git a/services/java/com/android/server/status/NotificationLinearLayout.java b/services/java/com/android/server/status/NotificationLinearLayout.java
deleted file mode 100644
index 2fdf956..0000000
--- a/services/java/com/android/server/status/NotificationLinearLayout.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-
-
-public class NotificationLinearLayout extends LinearLayout {
- public NotificationLinearLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-}
-
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
deleted file mode 100644
index 1bb56a7..0000000
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.os.IBinder;
-import android.util.Slog;
-import android.view.View;
-import java.util.ArrayList;
-
-class NotificationViewList {
- private ArrayList<StatusBarNotification> mOngoing = new ArrayList();
- private ArrayList<StatusBarNotification> mLatest = new ArrayList();
-
- NotificationViewList() {
- }
-
- private static final int indexInList(ArrayList<StatusBarNotification> list, NotificationData n){
- final int N = list.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification that = list.get(i);
- if (that.data == n) {
- return i;
- }
- }
- return -1;
- }
-
- int getIconIndex(NotificationData n) {
- final int ongoingSize = mOngoing.size();
- final int latestSize = mLatest.size();
- if (n.ongoingEvent) {
- int index = indexInList(mOngoing, n);
- if (index >= 0) {
- return latestSize + index + 1;
- } else {
- return -1;
- }
- } else {
- return indexInList(mLatest, n) + 1;
- }
- }
-
- void remove(StatusBarNotification notification) {
- NotificationData n = notification.data;
- int index;
- index = indexInList(mOngoing, n);
- if (index >= 0) {
- mOngoing.remove(index);
- return;
- }
- index = indexInList(mLatest, n);
- if (index >= 0) {
- mLatest.remove(index);
- return;
- }
- }
-
- ArrayList<StatusBarNotification> notificationsForPackage(String packageName) {
- ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>();
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- if (matchPackage(mOngoing.get(i), packageName)) {
- list.add(mOngoing.get(i));
- }
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- if (matchPackage(mLatest.get(i), packageName)) {
- list.add(mLatest.get(i));
- }
- }
- return list;
- }
-
- private final boolean matchPackage(StatusBarNotification snb, String packageName) {
- if (snb.data.contentIntent != null) {
- if (snb.data.contentIntent.getTargetPackage().equals(packageName)) {
- return true;
- }
- } else if (snb.data.pkg != null && snb.data.pkg.equals(packageName)) {
- return true;
- }
- return false;
- }
-
- private static final int indexForKey(ArrayList<StatusBarNotification> list, IBinder key) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- if (list.get(i).key == key) {
- return i;
- }
- }
- return -1;
- }
-
- StatusBarNotification get(IBinder key) {
- int index;
- index = indexForKey(mOngoing, key);
- if (index >= 0) {
- return mOngoing.get(index);
- }
- index = indexForKey(mLatest, key);
- if (index >= 0) {
- return mLatest.get(index);
- }
- return null;
- }
-
- // gets the index of the notification's view in its expanded parent view
- int getExpandedIndex(StatusBarNotification notification) {
- ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
- final IBinder key = notification.key;
- int index = 0;
- // (the view order is backwards from this list order)
- for (int i=list.size()-1; i>=0; i--) {
- StatusBarNotification item = list.get(i);
- if (item.key == key) {
- return index;
- }
- if (item.view != null) {
- index++;
- }
- }
- Slog.e(StatusBarService.TAG, "Couldn't find notification in NotificationViewList.");
- Slog.e(StatusBarService.TAG, "notification=" + notification);
- dump(notification);
- return 0;
- }
-
- void clearViews() {
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- mOngoing.get(i).view = null;
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- mLatest.get(i).view = null;
- }
- }
-
- int ongoingCount() {
- return mOngoing.size();
- }
-
- int latestCount() {
- return mLatest.size();
- }
-
- StatusBarNotification getOngoing(int index) {
- return mOngoing.get(index);
- }
-
- StatusBarNotification getLatest(int index) {
- return mLatest.get(index);
- }
-
- int size() {
- return mOngoing.size() + mLatest.size();
- }
-
- void add(StatusBarNotification notification) {
- if (StatusBarService.SPEW) {
- Slog.d(StatusBarService.TAG, "before add NotificationViewList"
- + " notification.data.ongoingEvent=" + notification.data.ongoingEvent);
- dump(notification);
- }
-
- ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
- long when = notification.data.when;
- final int N = list.size();
- int index = N;
- for (int i=0; i<N; i++) {
- StatusBarNotification that = list.get(i);
- if (that.data.when > when) {
- index = i;
- break;
- }
- }
- list.add(index, notification);
-
- if (StatusBarService.SPEW) {
- Slog.d(StatusBarService.TAG, "after add NotificationViewList index=" + index);
- dump(notification);
- }
- }
-
- void dump(StatusBarNotification notification) {
- if (StatusBarService.SPEW) {
- boolean showTime = false;
- String s = "";
- for (int i=0; i<mOngoing.size(); i++) {
- StatusBarNotification that = mOngoing.get(i);
- if (that.key == notification.key) {
- s += "[";
- }
- if (showTime) {
- s += that.data.when;
- } else {
- s += that.data.pkg + "/" + that.data.id + "/" + that.view;
- }
- if (that.key == notification.key) {
- s += "]";
- }
- s += " ";
- }
- Slog.d(StatusBarService.TAG, "NotificationViewList ongoing: " + s);
-
- s = "";
- for (int i=0; i<mLatest.size(); i++) {
- StatusBarNotification that = mLatest.get(i);
- if (that.key == notification.key) {
- s += "[";
- }
- if (showTime) {
- s += that.data.when;
- } else {
- s += that.data.pkg + "/" + that.data.id + "/" + that.view;
- }
- if (that.key == notification.key) {
- s += "]";
- }
- s += " ";
- }
- Slog.d(StatusBarService.TAG, "NotificationViewList latest: " + s);
- }
- }
-
- StatusBarNotification get(View view) {
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification notification = mOngoing.get(i);
- View v = notification.view;
- if (v == view) {
- return notification;
- }
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification notification = mLatest.get(i);
- View v = notification.view;
- if (v == view) {
- return notification;
- }
- }
- return null;
- }
-
- void update(StatusBarNotification notification) {
- remove(notification);
- add(notification);
- }
-
- boolean hasClearableItems() {
- int N = mLatest.size();
- for (int i=0; i<N; i++) {
- if (mLatest.get(i).data.clearable) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java
deleted file mode 100644
index 6f8b8a8..0000000
--- a/services/java/com/android/server/status/StatusBarIcon.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-class StatusBarIcon {
- // TODO: get this from a resource
- private static final int ICON_GAP = 8;
- private static final int ICON_WIDTH = 25;
- private static final int ICON_HEIGHT = 25;
-
- public View view;
-
- IconData mData;
-
- private TextView mTextView;
- private AnimatedImageView mImageView;
- private TextView mNumberView;
-
- public StatusBarIcon(Context context, IconData data, ViewGroup parent) {
- mData = data.clone();
-
- switch (data.type) {
- case IconData.TEXT: {
- TextView t;
- t = new TextView(context);
- mTextView = t;
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.MATCH_PARENT);
- t.setTextSize(16);
- t.setTextColor(0xff000000);
- t.setTypeface(Typeface.DEFAULT_BOLD);
- t.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
- t.setPadding(6, 0, 0, 0);
- t.setLayoutParams(layoutParams);
- t.setText(data.text);
- this.view = t;
- break;
- }
-
- case IconData.ICON: {
- // container
- LayoutInflater inflater = (LayoutInflater)context.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View v = inflater.inflate(com.android.internal.R.layout.status_bar_icon, parent, false);
- this.view = v;
-
- // icon
- AnimatedImageView im = (AnimatedImageView)v.findViewById(com.android.internal.R.id.image);
- im.setImageDrawable(getIcon(context, data));
- im.setImageLevel(data.iconLevel);
- mImageView = im;
-
- // number
- TextView nv = (TextView)v.findViewById(com.android.internal.R.id.number);
- mNumberView = nv;
- if (data.number > 0) {
- nv.setText("" + data.number);
- nv.setVisibility(View.VISIBLE);
- } else {
- nv.setVisibility(View.GONE);
- }
- break;
- }
- }
- }
-
- public void update(Context context, IconData data) throws StatusBarException {
- if (mData.type != data.type) {
- throw new StatusBarException("status bar entry type can't change");
- }
- switch (data.type) {
- case IconData.TEXT:
- if (!TextUtils.equals(mData.text, data.text)) {
- TextView tv = mTextView;
- tv.setText(data.text);
- }
- break;
- case IconData.ICON:
- if (((mData.iconPackage != null && data.iconPackage != null)
- && !mData.iconPackage.equals(data.iconPackage))
- || mData.iconId != data.iconId
- || mData.iconLevel != data.iconLevel) {
- ImageView im = mImageView;
- im.setImageDrawable(getIcon(context, data));
- im.setImageLevel(data.iconLevel);
- }
- if (mData.number != data.number) {
- TextView nv = mNumberView;
- if (data.number > 0) {
- nv.setText("" + data.number);
- } else {
- nv.setText("");
- }
- }
- break;
- }
- mData.copyFrom(data);
- }
-
- public void update(int number) {
- if (mData.number != number) {
- TextView nv = mNumberView;
- if (number > 0) {
- nv.setText("" + number);
- } else {
- nv.setText("");
- }
- }
- mData.number = number;
- }
-
-
- /**
- * Returns the right icon to use for this item, respecting the iconId and
- * iconPackage (if set)
- *
- * @param context Context to use to get resources if iconPackage is not set
- * @return Drawable for this item, or null if the package or item could not
- * be found
- */
- static Drawable getIcon(Context context, IconData data) {
-
- Resources r = null;
-
- if (data.iconPackage != null) {
- try {
- r = context.getPackageManager().getResourcesForApplication(data.iconPackage);
- } catch (PackageManager.NameNotFoundException ex) {
- Slog.e(StatusBarService.TAG, "Icon package not found: " + data.iconPackage, ex);
- return null;
- }
- } else {
- r = context.getResources();
- }
-
- if (data.iconId == 0) {
- Slog.w(StatusBarService.TAG, "No icon ID for slot " + data.slot);
- return null;
- }
-
- try {
- return r.getDrawable(data.iconId);
- } catch (RuntimeException e) {
- Slog.w(StatusBarService.TAG, "Icon not found in "
- + (data.iconPackage != null ? data.iconId : "<system>")
- + ": " + Integer.toHexString(data.iconId));
- }
-
- return null;
- }
-
- int getNumber() {
- return mData.number;
- }
-}
-
diff --git a/services/java/com/android/server/status/StatusBarNotification.java b/services/java/com/android/server/status/StatusBarNotification.java
deleted file mode 100644
index e5773f7..0000000
--- a/services/java/com/android/server/status/StatusBarNotification.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.os.IBinder;
-import android.view.View;
-
-class StatusBarNotification {
- IBinder key;
- NotificationData data;
- View view;
- View contentView;
-}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
deleted file mode 100644
index 3b0c436..0000000
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ /dev/null
@@ -1,1390 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.app.AlertDialog;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothPbap;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.NetworkInfo;
-import android.net.Uri;
-import android.net.wifi.WifiManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.storage.StorageManager;
-import android.provider.Settings;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
-import android.text.format.DateFormat;
-import android.text.style.RelativeSizeSpan;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.util.Slog;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.location.GpsLocationProvider;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.EriInfo;
-import com.android.internal.telephony.cdma.TtyIntent;
-import com.android.server.am.BatteryStatsService;
-
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.TimeZone;
-
-/**
- * This class contains all of the policy about which icons are installed in the status
- * bar at boot time. In reality, it should go into the android.policy package, but
- * putting it here is the first step from extracting it.
- */
-public class StatusBarPolicy {
- private static final String TAG = "StatusBarPolicy";
-
- private static StatusBarPolicy sInstance;
-
- // message codes for the handler
- private static final int EVENT_BATTERY_CLOSE = 4;
-
- private final Context mContext;
- private final StatusBarService mService;
- private final Handler mHandler = new StatusBarHandler();
- private final IBatteryStats mBatteryStats;
-
- // clock
- private Calendar mCalendar;
- private String mClockFormatString;
- private SimpleDateFormat mClockFormat;
- private IBinder mClockIcon;
- private IconData mClockData;
-
- // storage
- private StorageManager mStorageManager;
-
- // battery
- private IBinder mBatteryIcon;
- private IconData mBatteryData;
- private boolean mBatteryFirst = true;
- private boolean mBatteryPlugged;
- private int mBatteryLevel;
- private AlertDialog mLowBatteryDialog;
- private TextView mBatteryLevelTextView;
- private View mBatteryView;
- private int mBatteryViewSequence;
- private boolean mBatteryShowLowOnEndCall = false;
- private static final boolean SHOW_LOW_BATTERY_WARNING = true;
- private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
-
- // phone
- private TelephonyManager mPhone;
- private IBinder mPhoneIcon;
-
- //***** Signal strength icons
- private IconData mPhoneData;
- //GSM/UMTS
- private static final int[] sSignalImages = new int[] {
- com.android.internal.R.drawable.stat_sys_signal_0,
- com.android.internal.R.drawable.stat_sys_signal_1,
- com.android.internal.R.drawable.stat_sys_signal_2,
- com.android.internal.R.drawable.stat_sys_signal_3,
- com.android.internal.R.drawable.stat_sys_signal_4
- };
- private static final int[] sSignalImages_r = new int[] {
- com.android.internal.R.drawable.stat_sys_r_signal_0,
- com.android.internal.R.drawable.stat_sys_r_signal_1,
- com.android.internal.R.drawable.stat_sys_r_signal_2,
- com.android.internal.R.drawable.stat_sys_r_signal_3,
- com.android.internal.R.drawable.stat_sys_r_signal_4
- };
- private static final int[] sRoamingIndicatorImages_cdma = new int[] {
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
- // 1 is Standard Roaming Indicator OFF
- // TODO T: image never used, remove and put 0 instead?
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 2 is Standard Roaming Indicator FLASHING
- // TODO T: image never used, remove and put 0 instead?
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 3-12 Standard ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //3
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 13-63 Reserved for Standard ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //13
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 64-127 Reserved for Non Standard (Operator Specific) ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //64
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0 //83
-
- // 128-255 Reserved
- };
-
- //***** Data connection icons
- private int[] mDataIconList = sDataNetType_g;
- //GSM/UMTS
- private static final int[] sDataNetType_g = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_g,
- com.android.internal.R.drawable.stat_sys_data_in_g,
- com.android.internal.R.drawable.stat_sys_data_out_g,
- com.android.internal.R.drawable.stat_sys_data_inandout_g,
- };
- private static final int[] sDataNetType_3g = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_3g,
- com.android.internal.R.drawable.stat_sys_data_in_3g,
- com.android.internal.R.drawable.stat_sys_data_out_3g,
- com.android.internal.R.drawable.stat_sys_data_inandout_3g,
- };
- private static final int[] sDataNetType_e = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_e,
- com.android.internal.R.drawable.stat_sys_data_in_e,
- com.android.internal.R.drawable.stat_sys_data_out_e,
- com.android.internal.R.drawable.stat_sys_data_inandout_e,
- };
- //3.5G
- private static final int[] sDataNetType_h = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_h,
- com.android.internal.R.drawable.stat_sys_data_in_h,
- com.android.internal.R.drawable.stat_sys_data_out_h,
- com.android.internal.R.drawable.stat_sys_data_inandout_h,
- };
-
- //CDMA
- // Use 3G icons for EVDO data and 1x icons for 1XRTT data
- private static final int[] sDataNetType_1x = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_1x,
- com.android.internal.R.drawable.stat_sys_data_in_1x,
- com.android.internal.R.drawable.stat_sys_data_out_1x,
- com.android.internal.R.drawable.stat_sys_data_inandout_1x,
- };
-
- // Assume it's all good unless we hear otherwise. We don't always seem
- // to get broadcasts that it *is* there.
- IccCard.State mSimState = IccCard.State.READY;
- int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
- int mDataState = TelephonyManager.DATA_DISCONNECTED;
- int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
- ServiceState mServiceState;
- SignalStrength mSignalStrength;
-
- // data connection
- private IBinder mDataIcon;
- private IconData mDataData;
- private boolean mDataIconVisible;
- private boolean mHspaDataDistinguishable;
-
- // ringer volume
- private IBinder mVolumeIcon;
- private IconData mVolumeData;
- private boolean mVolumeVisible;
-
- // bluetooth device status
- private IBinder mBluetoothIcon;
- private IconData mBluetoothData;
- private int mBluetoothHeadsetState;
- private boolean mBluetoothA2dpConnected;
- private int mBluetoothPbapState;
- private boolean mBluetoothEnabled;
-
- // wifi
- private static final int[] sWifiSignalImages = new int[] {
- com.android.internal.R.drawable.stat_sys_wifi_signal_1,
- com.android.internal.R.drawable.stat_sys_wifi_signal_2,
- com.android.internal.R.drawable.stat_sys_wifi_signal_3,
- com.android.internal.R.drawable.stat_sys_wifi_signal_4,
- };
- private static final int sWifiTemporarilyNotConnectedImage =
- com.android.internal.R.drawable.stat_sys_wifi_signal_0;
-
- private int mLastWifiSignalLevel = -1;
- private boolean mIsWifiConnected = false;
- private IBinder mWifiIcon;
- private IconData mWifiData;
-
- // gps
- private IBinder mGpsIcon;
- private IconData mGpsEnabledIconData;
- private IconData mGpsFixIconData;
-
- // alarm clock
- // Icon lit when clock is set
- private IBinder mAlarmClockIcon;
- private IconData mAlarmClockIconData;
-
- // sync state
- // If sync is active the SyncActive icon is displayed. If sync is not active but
- // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
- private IBinder mSyncActiveIcon;
- private IBinder mSyncFailingIcon;
-
- // TTY mode
- // Icon lit when TTY mode is enabled
- private IBinder mTTYModeIcon;
- private IconData mTTYModeEnableIconData;
-
- // Cdma Roaming Indicator, ERI
- private IBinder mCdmaRoamingIndicatorIcon;
- private IconData mCdmaRoamingIndicatorIconData;
-
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_TIME_TICK)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- updateBattery(intent);
- }
- else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- String tz = intent.getStringExtra("time-zone");
- mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
- if (mClockFormat != null) {
- mClockFormat.setTimeZone(mCalendar.getTimeZone());
- }
- updateClock();
- }
- else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
- updateAlarm(intent);
- }
- else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
- updateSyncState(intent);
- }
- else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
- onBatteryLow(intent);
- }
- else if (action.equals(Intent.ACTION_BATTERY_OKAY)
- || action.equals(Intent.ACTION_POWER_CONNECTED)) {
- onBatteryOkay(intent);
- }
- else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
- action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) ||
- action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ||
- action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
- updateBluetooth(intent);
- }
- else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
- action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) ||
- action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- updateWifi(intent);
- }
- else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) ||
- action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION)) {
- updateGps(intent);
- }
- else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
- action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) {
- updateVolume();
- }
- else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
- updateSimState(intent);
- }
- else if (action.equals(TtyIntent.TTY_ENABLED_CHANGE_ACTION)) {
- updateTTY(intent);
- }
- }
- };
-
- private StatusBarPolicy(Context context, StatusBarService service) {
- mContext = context;
- mService = service;
- mSignalStrength = new SignalStrength();
- mBatteryStats = BatteryStatsService.getService();
-
- // clock
- mCalendar = Calendar.getInstance(TimeZone.getDefault());
- mClockData = IconData.makeText("clock", "");
- mClockIcon = service.addIcon(mClockData, null);
- updateClock();
-
- // storage
- mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
- mStorageManager.registerListener(
- new com.android.server.status.StorageNotification(context));
-
- // battery
- mBatteryData = IconData.makeIcon("battery",
- null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0);
- mBatteryIcon = service.addIcon(mBatteryData, null);
-
- // phone_signal
- mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
- mPhoneData = IconData.makeIcon("phone_signal",
- null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
- mPhoneIcon = service.addIcon(mPhoneData, null);
-
- // register for phone state notifications.
- ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_SERVICE_STATE
- | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
- | PhoneStateListener.LISTEN_CALL_STATE
- | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
- | PhoneStateListener.LISTEN_DATA_ACTIVITY);
-
- // data_connection
- mDataData = IconData.makeIcon("data_connection",
- null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
- mDataIcon = service.addIcon(mDataData, null);
- service.setIconVisibility(mDataIcon, false);
-
- // wifi
- mWifiData = IconData.makeIcon("wifi", null, sWifiSignalImages[0], 0, 0);
- mWifiIcon = service.addIcon(mWifiData, null);
- service.setIconVisibility(mWifiIcon, false);
- // wifi will get updated by the sticky intents
-
- // TTY status
- mTTYModeEnableIconData = IconData.makeIcon("tty",
- null, com.android.internal.R.drawable.stat_sys_tty_mode, 0, 0);
- mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null);
- service.setIconVisibility(mTTYModeIcon, false);
-
- // Cdma Roaming Indicator, ERI
- mCdmaRoamingIndicatorIconData = IconData.makeIcon("cdma_eri",
- null, com.android.internal.R.drawable.stat_sys_roaming_cdma_0, 0, 0);
- mCdmaRoamingIndicatorIcon = service.addIcon(mCdmaRoamingIndicatorIconData, null);
- service.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
-
- // bluetooth status
- mBluetoothData = IconData.makeIcon("bluetooth",
- null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
- mBluetoothIcon = service.addIcon(mBluetoothData, null);
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- mBluetoothEnabled = adapter.isEnabled();
- } else {
- mBluetoothEnabled = false;
- }
- mBluetoothA2dpConnected = false;
- mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
- mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
- mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
-
- // Gps status
- mGpsEnabledIconData = IconData.makeIcon("gps",
- null, com.android.internal.R.drawable.stat_sys_gps_acquiring_anim, 0, 0);
- mGpsFixIconData = IconData.makeIcon("gps",
- null, com.android.internal.R.drawable.stat_sys_gps_on, 0, 0);
- mGpsIcon = service.addIcon(mGpsEnabledIconData, null);
- service.setIconVisibility(mGpsIcon, false);
-
- // Alarm clock
- mAlarmClockIconData = IconData.makeIcon(
- "alarm_clock",
- null, com.android.internal.R.drawable.stat_notify_alarm, 0, 0);
- mAlarmClockIcon = service.addIcon(mAlarmClockIconData, null);
- service.setIconVisibility(mAlarmClockIcon, false);
-
- // Sync state
- mSyncActiveIcon = service.addIcon(IconData.makeIcon("sync_active",
- null, R.drawable.stat_notify_sync_anim0, 0, 0), null);
- mSyncFailingIcon = service.addIcon(IconData.makeIcon("sync_failing",
- null, R.drawable.stat_notify_sync_error, 0, 0), null);
- service.setIconVisibility(mSyncActiveIcon, false);
- service.setIconVisibility(mSyncFailingIcon, false);
-
- // volume
- mVolumeData = IconData.makeIcon("volume",
- null, com.android.internal.R.drawable.stat_sys_ringer_silent, 0, 0);
- mVolumeIcon = service.addIcon(mVolumeData, null);
- service.setIconVisibility(mVolumeIcon, false);
- updateVolume();
-
- IntentFilter filter = new IntentFilter();
-
- // Register for Intent broadcasts for...
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_LOW);
- filter.addAction(Intent.ACTION_BATTERY_OKAY);
- filter.addAction(Intent.ACTION_POWER_CONNECTED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_ALARM_CHANGED);
- filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
- filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- filter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
- filter.addAction(GpsLocationProvider.GPS_FIX_CHANGE_ACTION);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
- mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
-
- // load config to determine if to distinguish Hspa data icon
- try {
- mHspaDataDistinguishable = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_hspa_data_distinguishable);
- } catch (Exception e) {
- mHspaDataDistinguishable = false;
- }
- }
-
- public static void installIcons(Context context, StatusBarService service) {
- sInstance = new StatusBarPolicy(context, service);
- }
-
- private final CharSequence getSmallTime() {
- boolean b24 = DateFormat.is24HourFormat(mContext);
- int res;
-
- if (b24) {
- res = R.string.twenty_four_hour_time_format;
- } else {
- res = R.string.twelve_hour_time_format;
- }
-
- final char MAGIC1 = '\uEF00';
- final char MAGIC2 = '\uEF01';
-
- SimpleDateFormat sdf;
- String format = mContext.getString(res);
- if (!format.equals(mClockFormatString)) {
- /*
- * Search for an unquoted "a" in the format string, so we can
- * add dummy characters around it to let us find it again after
- * formatting and change its size.
- */
- int a = -1;
- boolean quoted = false;
- for (int i = 0; i < format.length(); i++) {
- char c = format.charAt(i);
-
- if (c == '\'') {
- quoted = !quoted;
- }
-
- if (!quoted && c == 'a') {
- a = i;
- break;
- }
- }
-
- if (a >= 0) {
- // Move a back so any whitespace before the AM/PM is also in the alternate size.
- final int b = a;
- while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
- a--;
- }
- format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
- + "a" + MAGIC2 + format.substring(b + 1);
- }
-
- mClockFormat = sdf = new SimpleDateFormat(format);
- mClockFormatString = format;
- } else {
- sdf = mClockFormat;
- }
- String result = sdf.format(mCalendar.getTime());
-
- int magic1 = result.indexOf(MAGIC1);
- int magic2 = result.indexOf(MAGIC2);
-
- if (magic1 >= 0 && magic2 > magic1) {
- SpannableStringBuilder formatted = new SpannableStringBuilder(result);
-
- formatted.setSpan(new RelativeSizeSpan(0.7f), magic1, magic2,
- Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
-
- formatted.delete(magic2, magic2 + 1);
- formatted.delete(magic1, magic1 + 1);
-
- return formatted;
- } else {
- return result;
- }
- }
-
- private final void updateClock() {
- mCalendar.setTimeInMillis(System.currentTimeMillis());
- mClockData.text = getSmallTime();
- mService.updateIcon(mClockIcon, mClockData, null);
- }
-
- private final void updateAlarm(Intent intent) {
- boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
- mService.setIconVisibility(mAlarmClockIcon, alarmSet);
- }
-
- private final void updateSyncState(Intent intent) {
- boolean isActive = intent.getBooleanExtra("active", false);
- boolean isFailing = intent.getBooleanExtra("failing", false);
- mService.setIconVisibility(mSyncActiveIcon, isActive);
- // Don't display sync failing icon: BUG 1297963 Set sync error timeout to "never"
- //mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
- }
-
- private final void updateBattery(Intent intent) {
- mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
- mBatteryData.iconLevel = intent.getIntExtra("level", 0);
- mService.updateIcon(mBatteryIcon, mBatteryData, null);
-
- boolean plugged = intent.getIntExtra("plugged", 0) != 0;
- int level = intent.getIntExtra("level", -1);
- if (false) {
- Slog.d(TAG, "updateBattery level=" + level
- + " plugged=" + plugged
- + " mBatteryPlugged=" + mBatteryPlugged
- + " mBatteryLevel=" + mBatteryLevel
- + " mBatteryFirst=" + mBatteryFirst);
- }
-
- boolean oldPlugged = mBatteryPlugged;
-
- mBatteryPlugged = plugged;
- mBatteryLevel = level;
-
- if (mBatteryFirst) {
- mBatteryFirst = false;
- }
- /*
- * No longer showing the battery view because it draws attention away
- * from the USB storage notification. We could still show it when
- * connected to a brick, but that could lead to the user into thinking
- * the device does not charge when plugged into USB (since he/she would
- * not see the same battery screen on USB as he sees on brick).
- */
- /* else {
- if (plugged && !oldPlugged) {
- showBatteryView();
- }
- }
- */
- if (false) {
- Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
- }
- }
-
- private void onBatteryLow(Intent intent) {
- if (SHOW_LOW_BATTERY_WARNING) {
- if (false) {
- Slog.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
-
- if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- showLowBatteryWarning();
- } else {
- mBatteryShowLowOnEndCall = true;
- }
- }
- }
-
- private void onBatteryOkay(Intent intent) {
- if (mLowBatteryDialog != null
- && SHOW_LOW_BATTERY_WARNING) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = false;
- }
- }
-
- private void showBatteryView() {
- closeLastBatteryView();
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- }
-
- int level = mBatteryLevel;
-
- View v = View.inflate(mContext, com.android.internal.R.layout.battery_status, null);
- mBatteryView = v;
- int pixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = v.getBackground();
- if (bg != null) {
- pixelFormat = bg.getOpacity();
- }
-
- int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
- }
-
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_TOAST,
- flags, pixelFormat);
-
- // Get the dim amount from the theme
- TypedArray a = mContext.obtainStyledAttributes(
- com.android.internal.R.styleable.Theme);
- lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
- a.recycle();
-
- lp.setTitle("Battery");
-
- TextView levelTextView = (TextView)v.findViewById(com.android.internal.R.id.level_percent);
- levelTextView.setText(mContext.getString(
- com.android.internal.R.string.battery_status_text_percent_format, level));
-
- setBatteryLevel(v, com.android.internal.R.id.spacer, 100-level, 0, 0);
- setBatteryLevel(v, com.android.internal.R.id.level, level,
- com.android.internal.R.drawable.battery_charge_fill, level);
-
- WindowManagerImpl.getDefault().addView(v, lp);
-
- scheduleCloseBatteryView();
- }
-
- private void setBatteryLevel(View parent, int id, int height, int background, int level) {
- ImageView v = (ImageView)parent.findViewById(id);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
- lp.weight = height;
- if (background != 0) {
- v.setBackgroundResource(background);
- Drawable bkg = v.getBackground();
- bkg.setLevel(level);
- }
- }
-
- private void showLowBatteryWarning() {
- closeLastBatteryView();
-
- // Show exact battery level.
- CharSequence levelText = mContext.getString(
- com.android.internal.R.string.battery_low_percent_format, mBatteryLevel);
-
- if (mBatteryLevelTextView != null) {
- mBatteryLevelTextView.setText(levelText);
- } else {
- View v = View.inflate(mContext, com.android.internal.R.layout.battery_low, null);
- mBatteryLevelTextView=(TextView)v.findViewById(com.android.internal.R.id.level_percent);
-
- mBatteryLevelTextView.setText(levelText);
-
- AlertDialog.Builder b = new AlertDialog.Builder(mContext);
- b.setCancelable(true);
- b.setTitle(com.android.internal.R.string.battery_low_title);
- b.setView(v);
- b.setIcon(android.R.drawable.ic_dialog_alert);
- b.setPositiveButton(android.R.string.ok, null);
-
- final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NO_HISTORY);
- if (intent.resolveActivity(mContext.getPackageManager()) != null) {
- b.setNegativeButton(com.android.internal.R.string.battery_low_why,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- mContext.startActivity(intent);
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- }
- }
- });
- }
-
- AlertDialog d = b.create();
- d.setOnDismissListener(mLowBatteryListener);
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- d.show();
- mLowBatteryDialog = d;
- }
-
- final ContentResolver cr = mContext.getContentResolver();
- if (Settings.System.getInt(cr,
- Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
- {
- final String soundPath = Settings.System.getString(cr,
- Settings.System.LOW_BATTERY_SOUND);
- if (soundPath != null) {
- final Uri soundUri = Uri.parse("file://" + soundPath);
- if (soundUri != null) {
- final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
- if (sfx != null) {
- sfx.setStreamType(AudioManager.STREAM_SYSTEM);
- sfx.play();
- }
- }
- }
- }
- }
-
- private final void updateCallState(int state) {
- mPhoneState = state;
- if (false) {
- Slog.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
- if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- if (mBatteryShowLowOnEndCall) {
- if (!mBatteryPlugged) {
- showLowBatteryWarning();
- }
- mBatteryShowLowOnEndCall = false;
- }
- } else {
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = true;
- }
- }
- }
-
- private DialogInterface.OnDismissListener mLowBatteryListener
- = new DialogInterface.OnDismissListener() {
- public void onDismiss(DialogInterface dialog) {
- mLowBatteryDialog = null;
- mBatteryLevelTextView = null;
- }
- };
-
- private void scheduleCloseBatteryView() {
- Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
- m.arg1 = (++mBatteryViewSequence);
- mHandler.sendMessageDelayed(m, 3000);
- }
-
- private void closeLastBatteryView() {
- if (mBatteryView != null) {
- //mBatteryView.debug();
- WindowManagerImpl.getDefault().removeView(mBatteryView);
- mBatteryView = null;
- }
- }
-
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- mSignalStrength = signalStrength;
- updateSignalStrength();
- }
-
- @Override
- public void onServiceStateChanged(ServiceState state) {
- mServiceState = state;
- updateSignalStrength();
- updateCdmaRoamingIcon(state);
- updateDataIcon();
- }
-
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- updateCallState(state);
- // In cdma, if a voice call is made, RSSI should switch to 1x.
- if (isCdma()) {
- updateSignalStrength();
- }
- }
-
- @Override
- public void onDataConnectionStateChanged(int state, int networkType) {
- mDataState = state;
- updateDataNetType(networkType);
- updateDataIcon();
- }
-
- @Override
- public void onDataActivity(int direction) {
- mDataActivity = direction;
- updateDataIcon();
- }
- };
-
- private final void updateSimState(Intent intent) {
- String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
- if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
- mSimState = IccCard.State.ABSENT;
- }
- else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
- mSimState = IccCard.State.READY;
- }
- else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
- final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
- if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
- mSimState = IccCard.State.PIN_REQUIRED;
- }
- else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
- mSimState = IccCard.State.PUK_REQUIRED;
- }
- else {
- mSimState = IccCard.State.NETWORK_LOCKED;
- }
- } else {
- mSimState = IccCard.State.UNKNOWN;
- }
- updateDataIcon();
- }
-
- private boolean isCdma() {
- return (mSignalStrength != null) && !mSignalStrength.isGsm();
- }
-
- private boolean isEvdo() {
- return ( (mServiceState != null)
- && ((mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
- || (mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
- || (mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
- }
-
- private boolean hasService() {
- if (mServiceState != null) {
- switch (mServiceState.getState()) {
- case ServiceState.STATE_OUT_OF_SERVICE:
- case ServiceState.STATE_POWER_OFF:
- return false;
- default:
- return true;
- }
- } else {
- return false;
- }
- }
-
- private final void updateSignalStrength() {
- int iconLevel = -1;
- int[] iconList;
-
- // Display signal strength while in "emergency calls only" mode
- if (!hasService() && !mServiceState.isEmergencyOnly()) {
- //Slog.d(TAG, "updateSignalStrength: no service");
- if (Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
- mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_flightmode;
- } else {
- mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
- }
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- return;
- }
-
- if (!isCdma()) {
- int asu = mSignalStrength.getGsmSignalStrength();
-
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- if (asu <= 2 || asu == 99) iconLevel = 0;
- else if (asu >= 12) iconLevel = 4;
- else if (asu >= 8) iconLevel = 3;
- else if (asu >= 5) iconLevel = 2;
- else iconLevel = 1;
-
- // Though mPhone is a Manager, this call is not an IPC
- if (mPhone.isNetworkRoaming()) {
- iconList = sSignalImages_r;
- } else {
- iconList = sSignalImages;
- }
- } else {
- iconList = this.sSignalImages;
-
- // If 3G(EV) and 1x network are available than 3G should be
- // displayed, displayed RSSI should be from the EV side.
- // If a voice call is made then RSSI should switch to 1x.
- if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
- iconLevel = getEvdoLevel();
- if (false) {
- Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel());
- }
- } else {
- iconLevel = getCdmaLevel();
- }
- }
- mPhoneData.iconId = iconList[iconLevel];
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- }
-
- private int getCdmaLevel() {
- final int cdmaDbm = mSignalStrength.getCdmaDbm();
- final int cdmaEcio = mSignalStrength.getCdmaEcio();
- int levelDbm = 0;
- int levelEcio = 0;
-
- if (cdmaDbm >= -75) levelDbm = 4;
- else if (cdmaDbm >= -85) levelDbm = 3;
- else if (cdmaDbm >= -95) levelDbm = 2;
- else if (cdmaDbm >= -100) levelDbm = 1;
- else levelDbm = 0;
-
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) levelEcio = 4;
- else if (cdmaEcio >= -110) levelEcio = 3;
- else if (cdmaEcio >= -130) levelEcio = 2;
- else if (cdmaEcio >= -150) levelEcio = 1;
- else levelEcio = 0;
-
- return (levelDbm < levelEcio) ? levelDbm : levelEcio;
- }
-
- private int getEvdoLevel() {
- int evdoDbm = mSignalStrength.getEvdoDbm();
- int evdoSnr = mSignalStrength.getEvdoSnr();
- int levelEvdoDbm = 0;
- int levelEvdoSnr = 0;
-
- if (evdoDbm >= -65) levelEvdoDbm = 4;
- else if (evdoDbm >= -75) levelEvdoDbm = 3;
- else if (evdoDbm >= -90) levelEvdoDbm = 2;
- else if (evdoDbm >= -105) levelEvdoDbm = 1;
- else levelEvdoDbm = 0;
-
- if (evdoSnr >= 7) levelEvdoSnr = 4;
- else if (evdoSnr >= 5) levelEvdoSnr = 3;
- else if (evdoSnr >= 3) levelEvdoSnr = 2;
- else if (evdoSnr >= 1) levelEvdoSnr = 1;
- else levelEvdoSnr = 0;
-
- return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
- }
-
- private final void updateDataNetType(int net) {
- switch (net) {
- case TelephonyManager.NETWORK_TYPE_EDGE:
- mDataIconList = sDataNetType_e;
- break;
- case TelephonyManager.NETWORK_TYPE_UMTS:
- mDataIconList = sDataNetType_3g;
- break;
- case TelephonyManager.NETWORK_TYPE_HSDPA:
- case TelephonyManager.NETWORK_TYPE_HSUPA:
- case TelephonyManager.NETWORK_TYPE_HSPA:
- if (mHspaDataDistinguishable) {
- mDataIconList = sDataNetType_h;
- } else {
- mDataIconList = sDataNetType_3g;
- }
- break;
- case TelephonyManager.NETWORK_TYPE_CDMA:
- // display 1xRTT for IS95A/B
- mDataIconList = this.sDataNetType_1x;
- break;
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- mDataIconList = this.sDataNetType_1x;
- break;
- case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
- case TelephonyManager.NETWORK_TYPE_EVDO_A:
- case TelephonyManager.NETWORK_TYPE_EVDO_B:
- mDataIconList = sDataNetType_3g;
- break;
- default:
- mDataIconList = sDataNetType_g;
- break;
- }
- }
-
- private final void updateDataIcon() {
- int iconId;
- boolean visible = true;
-
- if (!isCdma()) {
- // GSM case, we have to check also the sim state
- if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
- if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
- switch (mDataActivity) {
- case TelephonyManager.DATA_ACTIVITY_IN:
- iconId = mDataIconList[1];
- break;
- case TelephonyManager.DATA_ACTIVITY_OUT:
- iconId = mDataIconList[2];
- break;
- case TelephonyManager.DATA_ACTIVITY_INOUT:
- iconId = mDataIconList[3];
- break;
- default:
- iconId = mDataIconList[0];
- break;
- }
- mDataData.iconId = iconId;
- mService.updateIcon(mDataIcon, mDataData, null);
- } else {
- visible = false;
- }
- } else {
- mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim;
- mService.updateIcon(mDataIcon, mDataData, null);
- }
- } else {
- // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
- if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
- switch (mDataActivity) {
- case TelephonyManager.DATA_ACTIVITY_IN:
- iconId = mDataIconList[1];
- break;
- case TelephonyManager.DATA_ACTIVITY_OUT:
- iconId = mDataIconList[2];
- break;
- case TelephonyManager.DATA_ACTIVITY_INOUT:
- iconId = mDataIconList[3];
- break;
- case TelephonyManager.DATA_ACTIVITY_DORMANT:
- default:
- iconId = mDataIconList[0];
- break;
- }
- mDataData.iconId = iconId;
- mService.updateIcon(mDataIcon, mDataData, null);
- } else {
- visible = false;
- }
- }
-
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- if (mDataIconVisible != visible) {
- mService.setIconVisibility(mDataIcon, visible);
- mDataIconVisible = visible;
- }
- }
-
- private final void updateVolume() {
- AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- final int ringerMode = audioManager.getRingerMode();
- final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
- ringerMode == AudioManager.RINGER_MODE_VIBRATE;
- final int iconId = (ringerMode == AudioManager.RINGER_MODE_VIBRATE)
- ? com.android.internal.R.drawable.stat_sys_ringer_vibrate
- : com.android.internal.R.drawable.stat_sys_ringer_silent;
-
- if (visible) {
- mVolumeData.iconId = iconId;
- mService.updateIcon(mVolumeIcon, mVolumeData, null);
- }
- if (visible != mVolumeVisible) {
- mService.setIconVisibility(mVolumeIcon, visible);
- mVolumeVisible = visible;
- }
- }
-
- private final void updateBluetooth(Intent intent) {
- int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
- String action = intent.getAction();
- if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
- mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
- } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
- mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
- BluetoothHeadset.STATE_ERROR);
- } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
- BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
- if (a2dp.getConnectedSinks().size() != 0) {
- mBluetoothA2dpConnected = true;
- } else {
- mBluetoothA2dpConnected = false;
- }
- } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
- mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
- BluetoothPbap.STATE_DISCONNECTED);
- } else {
- return;
- }
-
- if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected ||
- mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
- iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected;
- }
-
- mBluetoothData.iconId = iconId;
- mService.updateIcon(mBluetoothIcon, mBluetoothData, null);
- mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
- }
-
- private final void updateWifi(Intent intent) {
- final String action = intent.getAction();
- if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-
- final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-
- if (!enabled) {
- // If disabled, hide the icon. (We show icon when connected.)
- mService.setIconVisibility(mWifiIcon, false);
- }
-
- } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
- final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED,
- false);
- if (!enabled) {
- mService.setIconVisibility(mWifiIcon, false);
- }
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-
- final NetworkInfo networkInfo = (NetworkInfo)
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-
- int iconId;
- if (networkInfo != null && networkInfo.isConnected()) {
- mIsWifiConnected = true;
- if (mLastWifiSignalLevel == -1) {
- iconId = sWifiSignalImages[0];
- } else {
- iconId = sWifiSignalImages[mLastWifiSignalLevel];
- }
-
- // Show the icon since wi-fi is connected
- mService.setIconVisibility(mWifiIcon, true);
-
- } else {
- mLastWifiSignalLevel = -1;
- mIsWifiConnected = false;
- iconId = sWifiSignalImages[0];
-
- // Hide the icon since we're not connected
- mService.setIconVisibility(mWifiIcon, false);
- }
-
- mWifiData.iconId = iconId;
- mService.updateIcon(mWifiIcon, mWifiData, null);
- } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
- sWifiSignalImages.length);
- if (newSignalLevel != mLastWifiSignalLevel) {
- mLastWifiSignalLevel = newSignalLevel;
- if (mIsWifiConnected) {
- mWifiData.iconId = sWifiSignalImages[newSignalLevel];
- } else {
- mWifiData.iconId = sWifiTemporarilyNotConnectedImage;
- }
- mService.updateIcon(mWifiIcon, mWifiData, null);
- }
- }
- }
-
- private final void updateGps(Intent intent) {
- final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false);
-
- if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) {
- // GPS is getting fixes
- mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
- mService.setIconVisibility(mGpsIcon, true);
- } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
- // GPS is off
- mService.setIconVisibility(mGpsIcon, false);
- } else {
- // GPS is on, but not receiving fixes
- mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null);
- mService.setIconVisibility(mGpsIcon, true);
- }
- }
-
- private final void updateTTY(Intent intent) {
- final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
-
- if (false) Slog.v(TAG, "updateTTY: enabled: " + enabled);
-
- if (enabled) {
- // TTY is on
- if (false) Slog.v(TAG, "updateTTY: set TTY on");
- mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null);
- mService.setIconVisibility(mTTYModeIcon, true);
- } else {
- // TTY is off
- if (false) Slog.v(TAG, "updateTTY: set TTY off");
- mService.setIconVisibility(mTTYModeIcon, false);
- }
- }
-
- private final void updateCdmaRoamingIcon(ServiceState state) {
- if (!hasService()) {
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- if (!isCdma()) {
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- int[] iconList = sRoamingIndicatorImages_cdma;
- int iconIndex = state.getCdmaEriIconIndex();
- int iconMode = state.getCdmaEriIconMode();
-
- if (iconIndex == -1) {
- Slog.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update");
- return;
- }
-
- if (iconMode == -1) {
- Slog.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update");
- return;
- }
-
- if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) {
- if (false) Slog.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- switch (iconMode) {
- case EriInfo.ROAMING_ICON_MODE_NORMAL:
- mCdmaRoamingIndicatorIconData.iconId = iconList[iconIndex];
- mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
- break;
- case EriInfo.ROAMING_ICON_MODE_FLASH:
- mCdmaRoamingIndicatorIconData.iconId =
- com.android.internal.R.drawable.stat_sys_roaming_cdma_flash;
- mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
- break;
-
- }
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- }
-
-
- private class StatusBarHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_BATTERY_CLOSE:
- if (msg.arg1 == mBatteryViewSequence) {
- closeLastBatteryView();
- }
- break;
- }
- }
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
deleted file mode 100644
index b9a57d6..0000000
--- a/services/java/com/android/server/status/StatusBarService.java
+++ /dev/null
@@ -1,1884 +0,0 @@
-/*
- * Copyright (C) 2007 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.status;
-
-import com.android.internal.R;
-import com.android.internal.util.CharSequences;
-
-import android.app.ActivityManagerNative;
-import android.app.Dialog;
-import android.app.IStatusBar;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Telephony;
-import android.util.Slog;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.FrameLayout;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
-
-
-/**
- * The public (ok, semi-public) service for the status bar.
- * <p>
- * This interesting thing to note about this class is that most of the methods that
- * are called from other classes just post a message, and everything else is batched
- * and coalesced into a series of calls to methods that all start with "perform."
- * There are two reasons for this. The first is that some of the methods (activate/deactivate)
- * are on IStatusBar, so they're called from the thread pool and they need to make their
- * way onto the UI thread. The second is that the message queue is stopped while animations
- * are happening in order to make for smoother transitions.
- * <p>
- * Each icon is either an icon or an icon and a notification. They're treated mostly
- * separately throughout the code, although they both use the same key, which is assigned
- * when they are created.
- */
-public class StatusBarService extends IStatusBar.Stub
-{
- static final String TAG = "StatusBar";
- static final boolean SPEW = false;
-
- static final int EXPANDED_LEAVE_ALONE = -10000;
- static final int EXPANDED_FULL_OPEN = -10001;
-
- private static final int MSG_ANIMATE = 1000;
- private static final int MSG_ANIMATE_REVEAL = 1001;
-
- private static final int OP_ADD_ICON = 1;
- private static final int OP_UPDATE_ICON = 2;
- private static final int OP_REMOVE_ICON = 3;
- private static final int OP_SET_VISIBLE = 4;
- private static final int OP_EXPAND = 5;
- private static final int OP_TOGGLE = 6;
- private static final int OP_DISABLE = 7;
- private class PendingOp {
- IBinder key;
- int code;
- IconData iconData;
- NotificationData notificationData;
- boolean visible;
- int integer;
- }
-
- private class DisableRecord implements IBinder.DeathRecipient {
- String pkg;
- int what;
- IBinder token;
-
- public void binderDied() {
- Slog.i(TAG, "binder died for pkg=" + pkg);
- disable(0, token, pkg);
- token.unlinkToDeath(this, 0);
- }
- }
-
- public interface NotificationCallbacks {
- void onSetDisabled(int status);
- void onClearAll();
- void onNotificationClick(String pkg, String tag, int id);
- void onPanelRevealed();
- }
-
- private class ExpandedDialog extends Dialog {
- ExpandedDialog(Context context) {
- super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (!down) {
- StatusBarService.this.deactivate();
- }
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
- }
-
- final Context mContext;
- final Display mDisplay;
- StatusBarView mStatusBarView;
- int mPixelFormat;
- H mHandler = new H();
- Object mQueueLock = new Object();
- ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
- NotificationCallbacks mNotificationCallbacks;
-
- // All accesses to mIconMap and mNotificationData are syncronized on those objects,
- // but this is only so dump() can work correctly. Modifying these outside of the UI
- // thread will not work, there are places in the code that unlock and reaquire between
- // reads and require them to not be modified.
-
- // icons
- HashMap<IBinder,StatusBarIcon> mIconMap = new HashMap<IBinder,StatusBarIcon>();
- ArrayList<StatusBarIcon> mIconList = new ArrayList<StatusBarIcon>();
- String[] mRightIconSlots;
- StatusBarIcon[] mRightIcons;
- LinearLayout mIcons;
- IconMerger mNotificationIcons;
- LinearLayout mStatusIcons;
- StatusBarIcon mMoreIcon;
- private UninstallReceiver mUninstallReceiver;
-
- // expanded notifications
- NotificationViewList mNotificationData = new NotificationViewList();
- Dialog mExpandedDialog;
- ExpandedView mExpandedView;
- WindowManager.LayoutParams mExpandedParams;
- ScrollView mScrollView;
- View mNotificationLinearLayout;
- TextView mOngoingTitle;
- LinearLayout mOngoingItems;
- TextView mLatestTitle;
- LinearLayout mLatestItems;
- TextView mNoNotificationsTitle;
- TextView mSpnLabel;
- TextView mPlmnLabel;
- TextView mClearButton;
- View mExpandedContents;
- CloseDragHandle mCloseView;
- int[] mPositionTmp = new int[2];
- boolean mExpanded;
- boolean mExpandedVisible;
-
- // the date view
- DateView mDateView;
-
- // the tracker view
- TrackingView mTrackingView;
- WindowManager.LayoutParams mTrackingParams;
- int mTrackingPosition; // the position of the top of the tracking view.
-
- // ticker
- private Ticker mTicker;
- private View mTickerView;
- private boolean mTicking;
-
- // Tracking finger for opening/closing.
- int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
- boolean mTracking;
- VelocityTracker mVelocityTracker;
-
- static final int ANIM_FRAME_DURATION = (1000/60);
-
- boolean mAnimating;
- long mCurAnimationTime;
- float mDisplayHeight;
- float mAnimY;
- float mAnimVel;
- float mAnimAccel;
- long mAnimLastTime;
- boolean mAnimatingReveal = false;
- int mViewDelta;
- int[] mAbsPos = new int[2];
-
- // for disabling the status bar
- ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
- int mDisabled = 0;
-
- /**
- * Construct the service, add the status bar view to the window manager
- */
- public StatusBarService(Context context) {
- mContext = context;
- mDisplay = ((WindowManager)context.getSystemService(
- Context.WINDOW_SERVICE)).getDefaultDisplay();
- makeStatusBarView(context);
- mUninstallReceiver = new UninstallReceiver();
- }
-
- public void setNotificationCallbacks(NotificationCallbacks listener) {
- mNotificationCallbacks = listener;
- }
-
- // ================================================================================
- // Constructing the view
- // ================================================================================
- private void makeStatusBarView(Context context) {
- Resources res = context.getResources();
- mRightIconSlots = res.getStringArray(com.android.internal.R.array.status_bar_icon_order);
- mRightIcons = new StatusBarIcon[mRightIconSlots.length];
-
- ExpandedView expanded = (ExpandedView)View.inflate(context,
- com.android.internal.R.layout.status_bar_expanded, null);
- expanded.mService = this;
- StatusBarView sb = (StatusBarView)View.inflate(context,
- com.android.internal.R.layout.status_bar, null);
- sb.mService = this;
-
- // figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = sb.getBackground();
- if (bg != null) {
- mPixelFormat = bg.getOpacity();
- }
-
- mStatusBarView = sb;
- mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
- mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
- mNotificationIcons.service = this;
- mIcons = (LinearLayout)sb.findViewById(R.id.icons);
- mTickerView = sb.findViewById(R.id.ticker);
- mDateView = (DateView)sb.findViewById(R.id.date);
-
- mExpandedDialog = new ExpandedDialog(context);
- mExpandedView = expanded;
- mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
- mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
- mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
- mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
- mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
- mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
- mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
- mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel);
- mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel);
- mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
- mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
-
- mExpandedView.setVisibility(View.GONE);
- mOngoingTitle.setVisibility(View.GONE);
- mLatestTitle.setVisibility(View.GONE);
-
- mTicker = new MyTicker(context, sb);
-
- TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
- tickerView.mTicker = mTicker;
-
- mTrackingView = (TrackingView)View.inflate(context,
- com.android.internal.R.layout.status_bar_tracking, null);
- mTrackingView.mService = this;
- mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
- mCloseView.mService = this;
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- // add the more icon for the notifications
- IconData moreData = IconData.makeIcon(null, context.getPackageName(),
- R.drawable.stat_notify_more, 0, 42);
- mMoreIcon = new StatusBarIcon(context, moreData, mNotificationIcons);
- mMoreIcon.view.setId(R.drawable.stat_notify_more);
- mNotificationIcons.moreIcon = mMoreIcon;
- mNotificationIcons.addView(mMoreIcon.view);
-
- // set the inital view visibility
- setAreThereNotifications();
- mDateView.setVisibility(View.INVISIBLE);
-
- // before we register for broadcasts
- mPlmnLabel.setText(R.string.lockscreen_carrier_default);
- mPlmnLabel.setVisibility(View.VISIBLE);
- mSpnLabel.setText("");
- mSpnLabel.setVisibility(View.GONE);
-
- // receive broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
- context.registerReceiver(mBroadcastReceiver, filter);
- }
-
- public void systemReady() {
- final StatusBarView view = mStatusBarView;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- view.getContext().getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height),
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
- mPixelFormat);
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("StatusBar");
- lp.windowAnimations = R.style.Animation_StatusBar;
-
- WindowManagerImpl.getDefault().addView(view, lp);
- }
-
- // ================================================================================
- // From IStatusBar
- // ================================================================================
- public void activate() {
- enforceExpandStatusBar();
- addPendingOp(OP_EXPAND, null, true);
- }
-
- public void deactivate() {
- enforceExpandStatusBar();
- addPendingOp(OP_EXPAND, null, false);
- }
-
- public void toggle() {
- enforceExpandStatusBar();
- addPendingOp(OP_TOGGLE, null, false);
- }
-
- public void disable(int what, IBinder token, String pkg) {
- enforceStatusBar();
- synchronized (mNotificationCallbacks) {
- // This is a little gross, but I think it's safe as long as nobody else
- // synchronizes on mNotificationCallbacks. It's important that the the callback
- // and the pending op get done in the correct order and not interleaved with
- // other calls, otherwise they'll get out of sync.
- int net;
- synchronized (mDisableRecords) {
- manageDisableListLocked(what, token, pkg);
- net = gatherDisableActionsLocked();
- mNotificationCallbacks.onSetDisabled(net);
- }
- addPendingOp(OP_DISABLE, net);
- }
- }
-
- public IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel) {
- enforceStatusBar();
- return addIcon(IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
- }
-
- public void updateIcon(IBinder key,
- String slot, String iconPackage, int iconId, int iconLevel) {
- enforceStatusBar();
- updateIcon(key, IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
- }
-
- public void removeIcon(IBinder key) {
- enforceStatusBar();
- addPendingOp(OP_REMOVE_ICON, key, null, null, -1);
- }
-
- private void enforceStatusBar() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.STATUS_BAR,
- "StatusBarService");
- }
-
- private void enforceExpandStatusBar() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.EXPAND_STATUS_BAR,
- "StatusBarService");
- }
-
- // ================================================================================
- // Can be called from any thread
- // ================================================================================
- public IBinder addIcon(IconData data, NotificationData n) {
- int slot;
- // assert early-on if they using a slot that doesn't exist.
- if (data != null && n == null) {
- slot = getRightIconIndex(data.slot);
- if (slot < 0) {
- throw new SecurityException("invalid status bar icon slot: "
- + (data.slot != null ? "'" + data.slot + "'" : "null"));
- }
- } else {
- slot = -1;
- }
- IBinder key = new Binder();
- addPendingOp(OP_ADD_ICON, key, data, n, -1);
- return key;
- }
-
- public void updateIcon(IBinder key, IconData data, NotificationData n) {
- addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
- }
-
- public void setIconVisibility(IBinder key, boolean visible) {
- addPendingOp(OP_SET_VISIBLE, key, visible);
- }
-
- private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.key = key;
- op.code = code;
- op.iconData = data == null ? null : data.clone();
- op.notificationData = n;
- op.integer = i;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(2);
- }
- }
- }
-
- private void addPendingOp(int code, IBinder key, boolean visible) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.key = key;
- op.code = code;
- op.visible = visible;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-
- private void addPendingOp(int code, int integer) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.code = code;
- op.integer = integer;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-
- // lock on mDisableRecords
- void manageDisableListLocked(int what, IBinder token, String pkg) {
- if (SPEW) {
- Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what)
- + " pkg=" + pkg);
- }
- // update the list
- synchronized (mDisableRecords) {
- final int N = mDisableRecords.size();
- DisableRecord tok = null;
- int i;
- for (i=0; i<N; i++) {
- DisableRecord t = mDisableRecords.get(i);
- if (t.token == token) {
- tok = t;
- break;
- }
- }
- if (what == 0 || !token.isBinderAlive()) {
- if (tok != null) {
- mDisableRecords.remove(i);
- tok.token.unlinkToDeath(tok, 0);
- }
- } else {
- if (tok == null) {
- tok = new DisableRecord();
- try {
- token.linkToDeath(tok, 0);
- }
- catch (RemoteException ex) {
- return; // give up
- }
- mDisableRecords.add(tok);
- }
- tok.what = what;
- tok.token = token;
- tok.pkg = pkg;
- }
- }
- }
-
- // lock on mDisableRecords
- int gatherDisableActionsLocked() {
- final int N = mDisableRecords.size();
- // gather the new net flags
- int net = 0;
- for (int i=0; i<N; i++) {
- net |= mDisableRecords.get(i).what;
- }
- return net;
- }
-
- private int getRightIconIndex(String slot) {
- final int N = mRightIconSlots.length;
- for (int i=0; i<N; i++) {
- if (mRightIconSlots[i].equals(slot)) {
- return i;
- }
- }
- return -1;
- }
-
- // ================================================================================
- // Always called from UI thread
- // ================================================================================
- /**
- * All changes to the status bar and notifications funnel through here and are batched.
- */
- private class H extends Handler {
- public void handleMessage(Message m) {
- if (m.what == MSG_ANIMATE) {
- doAnimation();
- return;
- }
- if (m.what == MSG_ANIMATE_REVEAL) {
- doRevealAnimation();
- return;
- }
-
- ArrayList<PendingOp> queue;
- synchronized (mQueueLock) {
- queue = mQueue;
- mQueue = new ArrayList<PendingOp>();
- }
-
- boolean wasExpanded = mExpanded;
-
- // for each one in the queue, find all of the ones with the same key
- // and collapse that down into a final op and/or call to setVisibility, etc
- boolean expand = wasExpanded;
- boolean doExpand = false;
- boolean doDisable = false;
- int disableWhat = 0;
- int N = queue.size();
- while (N > 0) {
- PendingOp op = queue.get(0);
- boolean doOp = false;
- boolean visible = false;
- boolean doVisibility = false;
- if (op.code == OP_SET_VISIBLE) {
- doVisibility = true;
- visible = op.visible;
- }
- else if (op.code == OP_EXPAND) {
- doExpand = true;
- expand = op.visible;
- }
- else if (op.code == OP_TOGGLE) {
- doExpand = true;
- expand = !expand;
- }
- else {
- doOp = true;
- }
-
- if (alwaysHandle(op.code)) {
- // coalesce these
- for (int i=1; i<N; i++) {
- PendingOp o = queue.get(i);
- if (!alwaysHandle(o.code) && o.key == op.key) {
- if (o.code == OP_SET_VISIBLE) {
- visible = o.visible;
- doVisibility = true;
- }
- else if (o.code == OP_EXPAND) {
- expand = o.visible;
- doExpand = true;
- }
- else {
- op.code = o.code;
- op.iconData = o.iconData;
- op.notificationData = o.notificationData;
- }
- queue.remove(i);
- i--;
- N--;
- }
- }
- }
-
- queue.remove(0);
- N--;
-
- if (doOp) {
- switch (op.code) {
- case OP_ADD_ICON:
- case OP_UPDATE_ICON:
- performAddUpdateIcon(op.key, op.iconData, op.notificationData);
- break;
- case OP_REMOVE_ICON:
- performRemoveIcon(op.key);
- break;
- case OP_DISABLE:
- doDisable = true;
- disableWhat = op.integer;
- break;
- }
- }
- if (doVisibility && op.code != OP_REMOVE_ICON) {
- performSetIconVisibility(op.key, visible);
- }
- }
-
- if (queue.size() != 0) {
- throw new RuntimeException("Assertion failed: queue.size=" + queue.size());
- }
- if (doExpand) {
- // this is last so that we capture all of the pending changes before doing it
- if (expand) {
- animateExpand();
- } else {
- animateCollapse();
- }
- }
- if (doDisable) {
- performDisableActions(disableWhat);
- }
- }
- }
-
- private boolean alwaysHandle(int code) {
- return code == OP_DISABLE;
- }
-
- /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
- throws StatusBarException {
- if (SPEW) {
- Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
- }
- // notification
- if (n != null) {
- StatusBarNotification notification = getNotification(key);
- NotificationData oldData = null;
- if (notification == null) {
- // add
- notification = new StatusBarNotification();
- notification.key = key;
- notification.data = n;
- synchronized (mNotificationData) {
- mNotificationData.add(notification);
- }
- addNotificationView(notification);
- setAreThereNotifications();
- } else {
- // update
- oldData = notification.data;
- notification.data = n;
- updateNotificationView(notification, oldData);
- }
- // Show the ticker if one is requested, and the text is different
- // than the currently displayed ticker. Also don't do this
- // until status bar window is attached to the window manager,
- // because... well, what's the point otherwise? And trying to
- // run a ticker without being attached will crash!
- if (n.tickerText != null && mStatusBarView.getWindowToken() != null
- && (oldData == null
- || oldData.tickerText == null
- || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
- if (0 == (mDisabled &
- (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
- mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
- }
- }
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- }
-
- // icon
- synchronized (mIconMap) {
- StatusBarIcon icon = mIconMap.get(key);
- if (icon == null) {
- // add
- LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
-
- icon = new StatusBarIcon(mContext, data, v);
- mIconMap.put(key, icon);
- mIconList.add(icon);
-
- if (n == null) {
- int slotIndex = getRightIconIndex(data.slot);
- StatusBarIcon[] rightIcons = mRightIcons;
- if (rightIcons[slotIndex] == null) {
- int pos = 0;
- for (int i=mRightIcons.length-1; i>slotIndex; i--) {
- StatusBarIcon ic = rightIcons[i];
- if (ic != null) {
- pos++;
- }
- }
- rightIcons[slotIndex] = icon;
- mStatusIcons.addView(icon.view, pos);
- } else {
- Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
- mIconMap.remove(key);
- mIconList.remove(icon);
- return ;
- }
- } else {
- int iconIndex = mNotificationData.getIconIndex(n);
- mNotificationIcons.addView(icon.view, iconIndex);
- }
- } else {
- if (n == null) {
- // right hand side icons -- these don't reorder
- icon.update(mContext, data);
- } else {
- // remove old
- ViewGroup parent = (ViewGroup)icon.view.getParent();
- parent.removeView(icon.view);
- // add new
- icon.update(mContext, data);
- int iconIndex = mNotificationData.getIconIndex(n);
- mNotificationIcons.addView(icon.view, iconIndex);
- }
- }
- }
- }
-
- /* private */ void performSetIconVisibility(IBinder key, boolean visible) {
- synchronized (mIconMap) {
- if (SPEW) {
- Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
- }
- StatusBarIcon icon = mIconMap.get(key);
- icon.view.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
- }
-
- /* private */ void performRemoveIcon(IBinder key) {
- synchronized (this) {
- if (SPEW) {
- Slog.d(TAG, "performRemoveIcon key=" + key);
- }
- StatusBarIcon icon = mIconMap.remove(key);
- mIconList.remove(icon);
- if (icon != null) {
- ViewGroup parent = (ViewGroup)icon.view.getParent();
- parent.removeView(icon.view);
- int slotIndex = getRightIconIndex(icon.mData.slot);
- if (slotIndex >= 0) {
- mRightIcons[slotIndex] = null;
- }
- }
- StatusBarNotification notification = getNotification(key);
- if (notification != null) {
- removeNotificationView(notification);
- synchronized (mNotificationData) {
- mNotificationData.remove(notification);
- }
- setAreThereNotifications();
- }
- }
- }
-
- int getIconNumberForView(View v) {
- synchronized (mIconMap) {
- StatusBarIcon icon = null;
- final int N = mIconList.size();
- for (int i=0; i<N; i++) {
- StatusBarIcon ic = mIconList.get(i);
- if (ic.view == v) {
- icon = ic;
- break;
- }
- }
- if (icon != null) {
- return icon.getNumber();
- } else {
- return -1;
- }
- }
- }
-
-
- StatusBarNotification getNotification(IBinder key) {
- synchronized (mNotificationData) {
- return mNotificationData.get(key);
- }
- }
-
- View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
- public void onFocusChange(View v, boolean hasFocus) {
- // Because 'v' is a ViewGroup, all its children will be (un)selected
- // too, which allows marqueeing to work.
- v.setSelected(hasFocus);
- }
- };
-
- View makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
- NotificationData n = notification.data;
- RemoteViews remoteViews = n.contentView;
- if (remoteViews == null) {
- return null;
- }
-
- // create the row view
- LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event, parent, false);
-
- // bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content);
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- content.setOnFocusChangeListener(mFocusChangeListener);
- PendingIntent contentIntent = n.contentIntent;
- if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
- }
-
- View child = null;
- Exception exception = null;
- try {
- child = remoteViews.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (child == null) {
- Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception);
- return null;
- }
- content.addView(child);
-
- row.setDrawingCacheEnabled(true);
-
- notification.view = row;
- notification.contentView = child;
-
- return row;
- }
-
- void addNotificationView(StatusBarNotification notification) {
- if (notification.view != null) {
- throw new RuntimeException("Assertion failed: notification.view="
- + notification.view);
- }
-
- LinearLayout parent = notification.data.ongoingEvent ? mOngoingItems : mLatestItems;
-
- View child = makeNotificationView(notification, parent);
- if (child == null) {
- return ;
- }
-
- int index = mNotificationData.getExpandedIndex(notification);
- parent.addView(child, index);
- }
-
- /**
- * Remove the old one and put the new one in its place.
- * @param notification the notification
- */
- void updateNotificationView(StatusBarNotification notification, NotificationData oldData) {
- NotificationData n = notification.data;
- if (oldData != null && n != null
- && n.when == oldData.when
- && n.ongoingEvent == oldData.ongoingEvent
- && n.contentView != null && oldData.contentView != null
- && n.contentView.getPackage() != null
- && oldData.contentView.getPackage() != null
- && oldData.contentView.getPackage().equals(n.contentView.getPackage())
- && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
- && notification.view != null) {
- mNotificationData.update(notification);
- try {
- n.contentView.reapply(mContext, notification.contentView);
-
- // update the contentIntent
- ViewGroup content = (ViewGroup)notification.view.findViewById(
- com.android.internal.R.id.content);
- PendingIntent contentIntent = n.contentIntent;
- if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
- }
- }
- catch (RuntimeException e) {
- // It failed to add cleanly. Log, and remove the view from the panel.
- Slog.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e);
- removeNotificationView(notification);
- }
- } else {
- mNotificationData.update(notification);
- removeNotificationView(notification);
- addNotificationView(notification);
- }
- setAreThereNotifications();
- }
-
- void removeNotificationView(StatusBarNotification notification) {
- View v = notification.view;
- if (v != null) {
- ViewGroup parent = (ViewGroup)v.getParent();
- parent.removeView(v);
- notification.view = null;
- }
- }
-
- private void setAreThereNotifications() {
- boolean ongoing = mOngoingItems.getChildCount() != 0;
- boolean latest = mLatestItems.getChildCount() != 0;
-
- if (mNotificationData.hasClearableItems()) {
- mClearButton.setVisibility(View.VISIBLE);
- } else {
- mClearButton.setVisibility(View.INVISIBLE);
- }
-
- mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
- mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
-
- if (ongoing || latest) {
- mNoNotificationsTitle.setVisibility(View.GONE);
- } else {
- mNoNotificationsTitle.setVisibility(View.VISIBLE);
- }
- }
-
- private void makeExpandedVisible() {
- if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
- if (mExpandedVisible) {
- return;
- }
- mExpandedVisible = true;
- panelSlightlyVisible(true);
-
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- mExpandedView.requestFocus(View.FOCUS_FORWARD);
- mTrackingView.setVisibility(View.VISIBLE);
- mExpandedView.setVisibility(View.VISIBLE);
-
- if (!mTicking) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
-
- void animateExpand() {
- if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return ;
- }
- if (mExpanded) {
- return;
- }
-
- prepareTracking(0, true);
- performFling(0, 2000.0f, true);
- }
-
- void animateCollapse() {
- if (SPEW) {
- Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible
- + " mExpanded=" + mExpanded
- + " mAnimating=" + mAnimating
- + " mAnimY=" + mAnimY
- + " mAnimVel=" + mAnimVel);
- }
-
- if (!mExpandedVisible) {
- return;
- }
-
- int y;
- if (mAnimating) {
- y = (int)mAnimY;
- } else {
- y = mDisplay.getHeight()-1;
- }
- // Let the fling think that we're open so it goes in the right direction
- // and doesn't try to re-open the windowshade.
- mExpanded = true;
- prepareTracking(y, false);
- performFling(y, -2000.0f, true);
- }
-
- void performExpand() {
- if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return ;
- }
- if (mExpanded) {
- return;
- }
-
- // It seems strange to sometimes not expand...
- if (false) {
- synchronized (mNotificationData) {
- if (mNotificationData.size() == 0) {
- return;
- }
- }
- }
-
- mExpanded = true;
- makeExpandedVisible();
- updateExpandedViewPos(EXPANDED_FULL_OPEN);
-
- if (false) postStartTracing();
- }
-
- void performCollapse() {
- if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible);
-
- if (!mExpandedVisible) {
- return;
- }
- mExpandedVisible = false;
- panelSlightlyVisible(false);
- mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- mTrackingView.setVisibility(View.GONE);
- mExpandedView.setVisibility(View.GONE);
-
- if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
-
- if (!mExpanded) {
- return;
- }
- mExpanded = false;
- }
-
- void doAnimation() {
- if (mAnimating) {
- if (SPEW) Slog.d(TAG, "doAnimation");
- if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
- incrementAnim();
- if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
- if (mAnimY >= mDisplay.getHeight()-1) {
- if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
- mAnimating = false;
- updateExpandedViewPos(EXPANDED_FULL_OPEN);
- performExpand();
- }
- else if (mAnimY < mStatusBarView.getHeight()) {
- if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
- mAnimating = false;
- updateExpandedViewPos(0);
- performCollapse();
- }
- else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- }
- }
- }
-
- void stopTracking() {
- mTracking = false;
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- void incrementAnim() {
- long now = SystemClock.uptimeMillis();
- float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
- final float y = mAnimY;
- final float v = mAnimVel; // px/s
- final float a = mAnimAccel; // px/s/s
- mAnimY = y + (v*t) + (0.5f*a*t*t); // px
- mAnimVel = v + (a*t); // px/s
- mAnimLastTime = now; // ms
- //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
- // + " mAnimAccel=" + mAnimAccel);
- }
-
- void doRevealAnimation() {
- final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
- if (mAnimatingReveal && mAnimating && mAnimY < h) {
- incrementAnim();
- if (mAnimY >= h) {
- mAnimY = h;
- updateExpandedViewPos((int)mAnimY);
- } else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- }
- }
- }
-
- void prepareTracking(int y, boolean opening) {
- mTracking = true;
- mVelocityTracker = VelocityTracker.obtain();
- if (opening) {
- mAnimAccel = 2000.0f;
- mAnimVel = 200;
- mAnimY = mStatusBarView.getHeight();
- updateExpandedViewPos((int)mAnimY);
- mAnimating = true;
- mAnimatingReveal = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- makeExpandedVisible();
- } else {
- // it's open, close it?
- if (mAnimating) {
- mAnimating = false;
- mHandler.removeMessages(MSG_ANIMATE);
- }
- updateExpandedViewPos(y + mViewDelta);
- }
- }
-
- void performFling(int y, float vel, boolean always) {
- mAnimatingReveal = false;
- mDisplayHeight = mDisplay.getHeight();
-
- mAnimY = y;
- mAnimVel = vel;
-
- //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
- if (mExpanded) {
- if (!always && (
- vel > 200.0f
- || (y > (mDisplayHeight-25) && vel > -200.0f))) {
- // We are expanded, but they didn't move sufficiently to cause
- // us to retract. Animate back to the expanded position.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are expanded and are now going to animate away.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- } else {
- if (always || (
- vel > 200.0f
- || (y > (mDisplayHeight/2) && vel > -200.0f))) {
- // We are collapsed, and they moved enough to allow us to
- // expand. Animate in the notifications.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are collapsed, but they didn't move sufficiently to cause
- // us to retract. Animate back to the collapsed position.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- }
- //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
- // + " mAnimAccel=" + mAnimAccel);
-
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- stopTracking();
- }
-
- boolean interceptTouchEvent(MotionEvent event) {
- if (SPEW) {
- Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
- + mDisabled);
- }
-
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return false;
- }
-
- final int statusBarSize = mStatusBarView.getHeight();
- final int hitSize = statusBarSize*2;
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final int y = (int)event.getRawY();
-
- if (!mExpanded) {
- mViewDelta = statusBarSize - y;
- } else {
- mTrackingView.getLocationOnScreen(mAbsPos);
- mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
- }
- if ((!mExpanded && y < hitSize) ||
- (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
-
- // We drop events at the edge of the screen to make the windowshade come
- // down by accident less, especially when pushing open a device with a keyboard
- // that rotates (like g1 and droid)
- int x = (int)event.getRawX();
- final int edgeBorder = mEdgeBorder;
- if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
- prepareTracking(y, !mExpanded);// opening if we're not already fully visible
- mVelocityTracker.addMovement(event);
- }
- }
- } else if (mTracking) {
- mVelocityTracker.addMovement(event);
- final int minY = statusBarSize + mCloseView.getHeight();
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- int y = (int)event.getRawY();
- if (mAnimatingReveal && y < minY) {
- // nothing
- } else {
- mAnimatingReveal = false;
- updateExpandedViewPos(y + mViewDelta);
- }
- } else if (event.getAction() == MotionEvent.ACTION_UP) {
- mVelocityTracker.computeCurrentVelocity(1000);
-
- float yVel = mVelocityTracker.getYVelocity();
- boolean negative = yVel < 0;
-
- float xVel = mVelocityTracker.getXVelocity();
- if (xVel < 0) {
- xVel = -xVel;
- }
- if (xVel > 150.0f) {
- xVel = 150.0f; // limit how much we care about the x axis
- }
-
- float vel = (float)Math.hypot(yVel, xVel);
- if (negative) {
- vel = -vel;
- }
-
- performFling((int)event.getRawY(), vel, false);
- }
-
- }
- return false;
- }
-
- private class Launcher implements View.OnClickListener {
- private PendingIntent mIntent;
- private String mPkg;
- private String mTag;
- private int mId;
-
- Launcher(PendingIntent intent, String pkg, String tag, int id) {
- mIntent = intent;
- mPkg = pkg;
- mTag = tag;
- mId = id;
- }
-
- public void onClick(View v) {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManagerNative.getDefault().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- Intent overlay = new Intent();
- overlay.setSourceBounds(
- new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
- try {
- mIntent.send(mContext, 0, overlay);
- mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId);
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here. Just log the exception message.
- Slog.w(TAG, "Sending contentIntent failed: " + e);
- }
- deactivate();
- }
- }
-
- private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
- super(context, sb);
- }
-
- @Override
- void tickerStarting() {
- mTicking = true;
- mIcons.setVisibility(View.GONE);
- mTickerView.setVisibility(View.VISIBLE);
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
- if (mExpandedVisible) {
- setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
- }
- }
-
- @Override
- void tickerDone() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
- }
- }
-
- void tickerHalting() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- }
-
- Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
- public void onAnimationEnd(Animation animation) {
- mTicking = false;
- }
- public void onAnimationRepeat(Animation animation) {
- }
- public void onAnimationStart(Animation animation) {
- }
- };
-
- private Animation loadAnim(int id, Animation.AnimationListener listener) {
- Animation anim = AnimationUtils.loadAnimation(mContext, id);
- if (listener != null) {
- anim.setAnimationListener(listener);
- }
- return anim;
- }
-
- public String viewInfo(View v) {
- return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
- + " " + v.getWidth() + "x" + v.getHeight() + ")";
- }
-
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump StatusBar from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mQueueLock) {
- pw.println("Current Status Bar state:");
- pw.println(" mExpanded=" + mExpanded
- + ", mExpandedVisible=" + mExpandedVisible);
- pw.println(" mTicking=" + mTicking);
- pw.println(" mTracking=" + mTracking);
- pw.println(" mAnimating=" + mAnimating
- + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
- + ", mAnimAccel=" + mAnimAccel);
- pw.println(" mCurAnimationTime=" + mCurAnimationTime
- + " mAnimLastTime=" + mAnimLastTime);
- pw.println(" mDisplayHeight=" + mDisplayHeight
- + " mAnimatingReveal=" + mAnimatingReveal
- + " mViewDelta=" + mViewDelta);
- pw.println(" mDisplayHeight=" + mDisplayHeight);
- final int N = mQueue.size();
- pw.println(" mQueue.size=" + N);
- for (int i=0; i<N; i++) {
- PendingOp op = mQueue.get(i);
- pw.println(" [" + i + "] key=" + op.key + " code=" + op.code + " visible="
- + op.visible);
- pw.println(" iconData=" + op.iconData);
- pw.println(" notificationData=" + op.notificationData);
- }
- pw.println(" mExpandedParams: " + mExpandedParams);
- pw.println(" mExpandedView: " + viewInfo(mExpandedView));
- pw.println(" mExpandedDialog: " + mExpandedDialog);
- pw.println(" mTrackingParams: " + mTrackingParams);
- pw.println(" mTrackingView: " + viewInfo(mTrackingView));
- pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
- pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
- pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
- pw.println(" mLatestItems: " + viewInfo(mLatestItems));
- pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
- pw.println(" mCloseView: " + viewInfo(mCloseView));
- pw.println(" mTickerView: " + viewInfo(mTickerView));
- pw.println(" mScrollView: " + viewInfo(mScrollView)
- + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
- pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
- }
- synchronized (mIconMap) {
- final int N = mIconMap.size();
- pw.println(" mIconMap.size=" + N);
- Set<IBinder> keys = mIconMap.keySet();
- int i=0;
- for (IBinder key: keys) {
- StatusBarIcon icon = mIconMap.get(key);
- pw.println(" [" + i + "] key=" + key);
- pw.println(" data=" + icon.mData);
- i++;
- }
- }
- synchronized (mNotificationData) {
- int N = mNotificationData.ongoingCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i<N; i++) {
- StatusBarNotification n = mNotificationData.getOngoing(i);
- pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
- pw.println(" data=" + n.data);
- }
- N = mNotificationData.latestCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i<N; i++) {
- StatusBarNotification n = mNotificationData.getLatest(i);
- pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
- pw.println(" data=" + n.data);
- }
- }
- synchronized (mDisableRecords) {
- final int N = mDisableRecords.size();
- pw.println(" mDisableRecords.size=" + N
- + " mDisabled=0x" + Integer.toHexString(mDisabled));
- for (int i=0; i<N; i++) {
- DisableRecord tok = mDisableRecords.get(i);
- pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what)
- + " pkg=" + tok.pkg + " token=" + tok.token);
- }
- }
-
- if (false) {
- pw.println("see the logcat for a dump of the views we have created.");
- // must happen on ui thread
- mHandler.post(new Runnable() {
- public void run() {
- mStatusBarView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mStatusBarView.getWidth() + "x"
- + mStatusBarView.getHeight());
- mStatusBarView.debug();
-
- mExpandedView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mExpandedView.getWidth() + "x"
- + mExpandedView.getHeight());
- mExpandedView.debug();
-
- mTrackingView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mTrackingView.getWidth() + "x"
- + mTrackingView.getHeight());
- mTrackingView.debug();
- }
- });
- }
- }
-
- void onBarViewAttached() {
- WindowManager.LayoutParams lp;
- int pixelFormat;
- Drawable bg;
-
- /// ---------- Tracking View --------------
- pixelFormat = PixelFormat.RGBX_8888;
- bg = mTrackingView.getBackground();
- if (bg != null) {
- pixelFormat = bg.getOpacity();
- }
-
- lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- pixelFormat);
-// lp.token = mStatusBarView.getWindowToken();
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("TrackingView");
- lp.y = mTrackingPosition;
- mTrackingParams = lp;
-
- WindowManagerImpl.getDefault().addView(mTrackingView, lp);
- }
-
- void onTrackingViewAttached() {
- WindowManager.LayoutParams lp;
- int pixelFormat;
- Drawable bg;
-
- /// ---------- Expanded View --------------
- pixelFormat = PixelFormat.TRANSLUCENT;
-
- final int disph = mDisplay.getHeight();
- lp = mExpandedDialog.getWindow().getAttributes();
- lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
- lp.height = getExpandedHeight();
- lp.x = 0;
- mTrackingPosition = lp.y = -disph; // sufficiently large negative
- lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
- lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_DITHER
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- lp.format = pixelFormat;
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("StatusBarExpanded");
- mExpandedDialog.getWindow().setAttributes(lp);
- mExpandedDialog.getWindow().setFormat(pixelFormat);
- mExpandedParams = lp;
-
- mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
- mExpandedDialog.setContentView(mExpandedView,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- mExpandedDialog.getWindow().setBackgroundDrawable(null);
- mExpandedDialog.show();
- FrameLayout hack = (FrameLayout)mExpandedView.getParent();
- }
-
- void setDateViewVisibility(boolean visible, int anim) {
- mDateView.setUpdates(visible);
- mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- mDateView.startAnimation(loadAnim(anim, null));
- }
-
- void setNotificationIconVisibility(boolean visible, int anim) {
- int old = mNotificationIcons.getVisibility();
- int v = visible ? View.VISIBLE : View.INVISIBLE;
- if (old != v) {
- mNotificationIcons.setVisibility(v);
- mNotificationIcons.startAnimation(loadAnim(anim, null));
- }
- }
-
- void updateExpandedViewPos(int expandedPosition) {
- if (SPEW) {
- Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
- + " mTrackingParams.y=" + mTrackingParams.y
- + " mTrackingPosition=" + mTrackingPosition);
- }
-
- int h = mStatusBarView.getHeight();
- int disph = mDisplay.getHeight();
-
- // If the expanded view is not visible, make sure they're still off screen.
- // Maybe the view was resized.
- if (!mExpandedVisible) {
- if (mTrackingView != null) {
- mTrackingPosition = -disph;
- if (mTrackingParams != null) {
- mTrackingParams.y = mTrackingPosition;
- WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
- }
- }
- if (mExpandedParams != null) {
- mExpandedParams.y = -disph;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
- return;
- }
-
- // tracking view...
- int pos;
- if (expandedPosition == EXPANDED_FULL_OPEN) {
- pos = h;
- }
- else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
- pos = mTrackingPosition;
- }
- else {
- if (expandedPosition <= disph) {
- pos = expandedPosition;
- } else {
- pos = disph;
- }
- pos -= disph-h;
- }
- mTrackingPosition = mTrackingParams.y = pos;
- mTrackingParams.height = disph-h;
- WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-
- if (mExpandedParams != null) {
- mCloseView.getLocationInWindow(mPositionTmp);
- final int closePos = mPositionTmp[1];
-
- mExpandedContents.getLocationInWindow(mPositionTmp);
- final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
-
- mExpandedParams.y = pos + mTrackingView.getHeight()
- - (mTrackingParams.height-closePos) - contentsBottom;
- int max = h;
- if (mExpandedParams.y > max) {
- mExpandedParams.y = max;
- }
- int min = mTrackingPosition;
- if (mExpandedParams.y < min) {
- mExpandedParams.y = min;
- }
-
- boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
- if (!visible) {
- // if the contents aren't visible, move the expanded view way off screen
- // because the window itself extends below the content view.
- mExpandedParams.y = -disph;
- }
- panelSlightlyVisible(visible);
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
-
- if (SPEW) {
- Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition
- + " mTrackingParams.y=" + mTrackingParams.y
- + " mTrackingPosition=" + mTrackingPosition
- + " mExpandedParams.y=" + mExpandedParams.y
- + " mExpandedParams.height=" + mExpandedParams.height);
- }
- }
-
- int getExpandedHeight() {
- return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
- }
-
- void updateExpandedHeight() {
- if (mExpandedView != null) {
- mExpandedParams.height = getExpandedHeight();
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
- }
-
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- private boolean mPanelSlightlyVisible;
- void panelSlightlyVisible(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- if (visible) {
- // tell the notification manager to turn off the lights.
- mNotificationCallbacks.onPanelRevealed();
- }
- }
- }
-
- void performDisableActions(int net) {
- int old = mDisabled;
- int diff = net ^ old;
- mDisabled = net;
-
- // act accordingly
- if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
- if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
- Slog.d(TAG, "DISABLE_EXPAND: yes");
- animateCollapse();
- }
- }
- if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
- if (mTicking) {
- mNotificationIcons.setVisibility(View.INVISIBLE);
- mTicker.halt();
- } else {
- setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
- }
- } else {
- Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
- if (!mExpandedVisible) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- mTicker.halt();
- }
- }
- }
-
- private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
- public void onClick(View v) {
- mNotificationCallbacks.onClearAll();
- addPendingOp(OP_EXPAND, null, false);
- }
- };
-
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)) {
- deactivate();
- }
- else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
- updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
- intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
- intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
- intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
- }
- else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- updateResources();
- }
- }
- };
-
- void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
- if (false) {
- Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn
- + " showPlmn=" + showPlmn + " plmn=" + plmn);
- }
- boolean something = false;
- if (showPlmn) {
- mPlmnLabel.setVisibility(View.VISIBLE);
- if (plmn != null) {
- mPlmnLabel.setText(plmn);
- } else {
- mPlmnLabel.setText(R.string.lockscreen_carrier_default);
- }
- } else {
- mPlmnLabel.setText("");
- mPlmnLabel.setVisibility(View.GONE);
- }
- if (showSpn && spn != null) {
- mSpnLabel.setText(spn);
- mSpnLabel.setVisibility(View.VISIBLE);
- something = true;
- } else {
- mSpnLabel.setText("");
- mSpnLabel.setVisibility(View.GONE);
- }
- }
-
- /**
- * Reload some of our resources when the configuration changes.
- *
- * We don't reload everything when the configuration changes -- we probably
- * should, but getting that smooth is tough. Someday we'll fix that. In the
- * meantime, just update the things that we know change.
- */
- void updateResources() {
- Resources res = mContext.getResources();
-
- mClearButton.setText(mContext.getText(R.string.status_bar_clear_all_button));
- mOngoingTitle.setText(mContext.getText(R.string.status_bar_ongoing_events_title));
- mLatestTitle.setText(mContext.getText(R.string.status_bar_latest_events_title));
- mNoNotificationsTitle.setText(mContext.getText(R.string.status_bar_no_notifications_title));
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- if (false) Slog.v(TAG, "updateResources");
- }
-
- //
- // tracing
- //
-
- void postStartTracing() {
- mHandler.postDelayed(mStartTracing, 3000);
- }
-
- void vibrate() {
- android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
- Context.VIBRATOR_SERVICE);
- vib.vibrate(250);
- }
-
- Runnable mStartTracing = new Runnable() {
- public void run() {
- vibrate();
- SystemClock.sleep(250);
- Slog.d(TAG, "startTracing");
- android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
- mHandler.postDelayed(mStopTracing, 10000);
- }
- };
-
- Runnable mStopTracing = new Runnable() {
- public void run() {
- android.os.Debug.stopMethodTracing();
- Slog.d(TAG, "stopTracing");
- vibrate();
- }
- };
-
- class UninstallReceiver extends BroadcastReceiver {
- public UninstallReceiver() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- filter.addDataScheme("package");
- mContext.registerReceiver(this, filter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(this, sdFilter);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String pkgList[] = null;
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else {
- Uri data = intent.getData();
- if (data != null) {
- String pkg = data.getSchemeSpecificPart();
- if (pkg != null) {
- pkgList = new String[]{pkg};
- }
- }
- }
- ArrayList<StatusBarNotification> list = null;
- if (pkgList != null) {
- synchronized (StatusBarService.this) {
- for (String pkg : pkgList) {
- list = mNotificationData.notificationsForPackage(pkg);
- }
- }
- }
-
- if (list != null) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- removeIcon(list.get(i).key);
- }
- }
- }
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarView.java b/services/java/com/android/server/status/StatusBarView.java
deleted file mode 100644
index 5e1f572..0000000
--- a/services/java/com/android/server/status/StatusBarView.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Canvas;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-
-public class StatusBarView extends FrameLayout {
- private static final String TAG = "StatusBarView";
-
- static final int DIM_ANIM_TIME = 400;
-
- StatusBarService mService;
- boolean mTracking;
- int mStartX, mStartY;
- ViewGroup mNotificationIcons;
- ViewGroup mStatusIcons;
- View mDate;
- FixedSizeDrawable mBackground;
-
- boolean mNightMode = false;
- int mStartAlpha = 0, mEndAlpha = 0;
- long mEndTime = 0;
-
- public StatusBarView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mNotificationIcons = (ViewGroup)findViewById(R.id.notificationIcons);
- mStatusIcons = (ViewGroup)findViewById(R.id.statusIcons);
- mDate = findViewById(R.id.date);
-
- mBackground = new FixedSizeDrawable(mDate.getBackground());
- mBackground.setFixedBounds(0, 0, 0, 0);
- mDate.setBackgroundDrawable(mBackground);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mService.onBarViewAttached();
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- if (mNightMode != nightMode) {
- mNightMode = nightMode;
- mStartAlpha = getCurAlpha();
- mEndAlpha = mNightMode ? 0x80 : 0x00;
- mEndTime = SystemClock.uptimeMillis() + DIM_ANIM_TIME;
- invalidate();
- }
- }
-
- int getCurAlpha() {
- long time = SystemClock.uptimeMillis();
- if (time > mEndTime) {
- return mEndAlpha;
- }
- return mEndAlpha
- - (int)(((mEndAlpha-mStartAlpha) * (mEndTime-time) / DIM_ANIM_TIME));
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
-
- // put the date date view quantized to the icons
- int oldDateRight = mDate.getRight();
- int newDateRight;
-
- newDateRight = getDateSize(mNotificationIcons, oldDateRight,
- getViewOffset(mNotificationIcons));
- if (newDateRight < 0) {
- int offset = getViewOffset(mStatusIcons);
- if (oldDateRight < offset) {
- newDateRight = oldDateRight;
- } else {
- newDateRight = getDateSize(mStatusIcons, oldDateRight, offset);
- if (newDateRight < 0) {
- newDateRight = r;
- }
- }
- }
- int max = r - getPaddingRight();
- if (newDateRight > max) {
- newDateRight = max;
- }
-
- mDate.layout(mDate.getLeft(), mDate.getTop(), newDateRight, mDate.getBottom());
- mBackground.setFixedBounds(-mDate.getLeft(), -mDate.getTop(), (r-l), (b-t));
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- int alpha = getCurAlpha();
- if (alpha != 0) {
- canvas.drawARGB(alpha, 0, 0, 0);
- }
- if (alpha != mEndAlpha) {
- invalidate();
- }
- }
-
- /**
- * Gets the left position of v in this view. Throws if v is not
- * a child of this.
- */
- private int getViewOffset(View v) {
- int offset = 0;
- while (v != this) {
- offset += v.getLeft();
- ViewParent p = v.getParent();
- if (v instanceof View) {
- v = (View)p;
- } else {
- throw new RuntimeException(v + " is not a child of " + this);
- }
- }
- return offset;
- }
-
- private int getDateSize(ViewGroup g, int w, int offset) {
- final int N = g.getChildCount();
- for (int i=0; i<N; i++) {
- View v = g.getChildAt(i);
- int l = v.getLeft() + offset;
- int r = v.getRight() + offset;
- if (w >= l && w <= r) {
- return r;
- }
- }
- return -1;
- }
-
- /**
- * Ensure that, if there is no target under us to receive the touch,
- * that we process it ourself. This makes sure that onInterceptTouchEvent()
- * is always called for the entire gesture.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() != MotionEvent.ACTION_DOWN) {
- mService.interceptTouchEvent(event);
- }
- return true;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return mService.interceptTouchEvent(event)
- ? true : super.onInterceptTouchEvent(event);
- }
-}
-
diff --git a/services/java/com/android/server/status/StorageNotification.java b/services/java/com/android/server/status/StorageNotification.java
deleted file mode 100644
index 8da8cd3..0000000
--- a/services/java/com/android/server/status/StorageNotification.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.status;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.storage.IMountService;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.storage.StorageEventListener;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageResultCode;
-import android.provider.Settings;
-import android.util.Slog;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class StorageNotification extends StorageEventListener {
- private static final String TAG = "StorageNotification";
-
- private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true;
-
- /**
- * Binder context for this service
- */
- private Context mContext;
-
- /**
- * The notification that is shown when a USB mass storage host
- * is connected.
- * <p>
- * This is lazily created, so use {@link #setUsbStorageNotification()}.
- */
- private Notification mUsbStorageNotification;
-
- /**
- * The notification that is shown when the following media events occur:
- * - Media is being checked
- * - Media is blank (or unknown filesystem)
- * - Media is corrupt
- * - Media is safe to unmount
- * - Media is missing
- * <p>
- * This is lazily created, so use {@link #setMediaStorageNotification()}.
- */
- private Notification mMediaStorageNotification;
- private boolean mUmsAvailable;
- private StorageManager mStorageManager;
-
- public StorageNotification(Context context) {
- mContext = context;
-
- mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
- mUmsAvailable = mStorageManager.isUsbMassStorageConnected();
- Slog.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable,
- Environment.getExternalStorageState()));
- }
-
- /*
- * @override com.android.os.storage.StorageEventListener
- */
- @Override
- public void onUsbMassStorageConnectionChanged(boolean connected) {
- mUmsAvailable = connected;
- /*
- * Even though we may have a UMS host connected, we the SD card
- * may not be in a state for export.
- */
- String st = Environment.getExternalStorageState();
-
- Slog.i(TAG, String.format("UMS connection changed to %s (media state %s)", connected, st));
-
- if (connected && (st.equals(
- Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) {
- /*
- * No card or card being checked = don't display
- */
- connected = false;
- }
- updateUsbMassStorageNotification(connected);
- }
-
- /*
- * @override com.android.os.storage.StorageEventListener
- */
- @Override
- public void onStorageStateChanged(String path, String oldState, String newState) {
- Slog.i(TAG, String.format(
- "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
- if (newState.equals(Environment.MEDIA_SHARED)) {
- /*
- * Storage is now shared. Modify the UMS notification
- * for stopping UMS.
- */
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
- setUsbStorageNotification(
- com.android.internal.R.string.usb_storage_stop_notification_title,
- com.android.internal.R.string.usb_storage_stop_notification_message,
- com.android.internal.R.drawable.stat_sys_warning, false, true, pi);
- } else if (newState.equals(Environment.MEDIA_CHECKING)) {
- /*
- * Storage is now checking. Update media notification and disable
- * UMS notification.
- */
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_checking_notification_title,
- com.android.internal.R.string.ext_media_checking_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null);
- updateUsbMassStorageNotification(false);
- } else if (newState.equals(Environment.MEDIA_MOUNTED)) {
- /*
- * Storage is now mounted. Dismiss any media notifications,
- * and enable UMS notification if connected.
- */
- setMediaStorageNotification(0, 0, 0, false, false, null);
- updateUsbMassStorageNotification(mUmsAvailable);
- } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) {
- /*
- * Storage is now unmounted. We may have been unmounted
- * because the user is enabling/disabling UMS, in which case we don't
- * want to display the 'safe to unmount' notification.
- */
- if (!mStorageManager.isUsbMassStorageEnabled()) {
- if (oldState.equals(Environment.MEDIA_SHARED)) {
- /*
- * The unmount was due to UMS being enabled. Dismiss any
- * media notifications, and enable UMS notification if connected
- */
- setMediaStorageNotification(0, 0, 0, false, false, null);
- updateUsbMassStorageNotification(mUmsAvailable);
- } else {
- /*
- * Show safe to unmount media notification, and enable UMS
- * notification if connected.
- */
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_safe_unmount_notification_title,
- com.android.internal.R.string.ext_media_safe_unmount_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
- updateUsbMassStorageNotification(mUmsAvailable);
- }
- } else {
- /*
- * The unmount was due to UMS being enabled. Dismiss any
- * media notifications, and disable the UMS notification
- */
- setMediaStorageNotification(0, 0, 0, false, false, null);
- updateUsbMassStorageNotification(false);
- }
- } else if (newState.equals(Environment.MEDIA_NOFS)) {
- /*
- * Storage has no filesystem. Show blank media notification,
- * and enable UMS notification if connected.
- */
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_nofs_notification_title,
- com.android.internal.R.string.ext_media_nofs_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
- updateUsbMassStorageNotification(mUmsAvailable);
- } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) {
- /*
- * Storage is corrupt. Show corrupt media notification,
- * and enable UMS notification if connected.
- */
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_unmountable_notification_title,
- com.android.internal.R.string.ext_media_unmountable_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
- updateUsbMassStorageNotification(mUmsAvailable);
- } else if (newState.equals(Environment.MEDIA_REMOVED)) {
- /*
- * Storage has been removed. Show nomedia media notification,
- * and disable UMS notification regardless of connection state.
- */
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_nomedia_notification_title,
- com.android.internal.R.string.ext_media_nomedia_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_usb,
- true, false, null);
- updateUsbMassStorageNotification(false);
- } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) {
- /*
- * Storage has been removed unsafely. Show bad removal media notification,
- * and disable UMS notification regardless of connection state.
- */
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_badremoval_notification_title,
- com.android.internal.R.string.ext_media_badremoval_notification_message,
- com.android.internal.R.drawable.stat_sys_warning,
- true, true, null);
- updateUsbMassStorageNotification(false);
- } else {
- Slog.w(TAG, String.format("Ignoring unknown state {%s}", newState));
- }
- }
-
- /**
- * Update the state of the USB mass storage notification
- */
- void updateUsbMassStorageNotification(boolean available) {
-
- if (available) {
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- final boolean adbOn = 1 == Settings.Secure.getInt(
- mContext.getContentResolver(),
- Settings.Secure.ADB_ENABLED,
- 0);
-
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
- setUsbStorageNotification(
- com.android.internal.R.string.usb_storage_notification_title,
- com.android.internal.R.string.usb_storage_notification_message,
- com.android.internal.R.drawable.stat_sys_data_usb,
- false, true, pi);
-
- if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
- // We assume that developers don't want to enable UMS every
- // time they attach a device to a USB host. The average user,
- // however, is looking to charge the phone (in which case this
- // is harmless) or transfer files (in which case this coaches
- // the user about how to complete that task and saves several
- // steps).
- mContext.startActivity(intent);
- }
- } else {
- setUsbStorageNotification(0, 0, 0, false, false, null);
- }
- }
-
- /**
- * Sets the USB storage notification.
- */
- private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon,
- boolean sound, boolean visible, PendingIntent pi) {
-
- if (!visible && mUsbStorageNotification == null) {
- return;
- }
-
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (notificationManager == null) {
- return;
- }
-
- if (visible) {
- Resources r = Resources.getSystem();
- CharSequence title = r.getText(titleId);
- CharSequence message = r.getText(messageId);
-
- if (mUsbStorageNotification == null) {
- mUsbStorageNotification = new Notification();
- mUsbStorageNotification.icon = icon;
- mUsbStorageNotification.when = 0;
- }
-
- if (sound) {
- mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
- } else {
- mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
- }
-
- mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
-
- mUsbStorageNotification.tickerText = title;
- if (pi == null) {
- Intent intent = new Intent();
- pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- }
-
- mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
- }
-
- final int notificationId = mUsbStorageNotification.icon;
- if (visible) {
- notificationManager.notify(notificationId, mUsbStorageNotification);
- } else {
- notificationManager.cancel(notificationId);
- }
- }
-
- private synchronized boolean getMediaStorageNotificationDismissable() {
- if ((mMediaStorageNotification != null) &&
- ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
- Notification.FLAG_AUTO_CANCEL))
- return true;
-
- return false;
- }
-
- /**
- * Sets the media storage notification.
- */
- private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
- boolean dismissable, PendingIntent pi) {
-
- if (!visible && mMediaStorageNotification == null) {
- return;
- }
-
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (notificationManager == null) {
- return;
- }
-
- if (mMediaStorageNotification != null && visible) {
- /*
- * Dismiss the previous notification - we're about to
- * re-use it.
- */
- final int notificationId = mMediaStorageNotification.icon;
- notificationManager.cancel(notificationId);
- }
-
- if (visible) {
- Resources r = Resources.getSystem();
- CharSequence title = r.getText(titleId);
- CharSequence message = r.getText(messageId);
-
- if (mMediaStorageNotification == null) {
- mMediaStorageNotification = new Notification();
- mMediaStorageNotification.when = 0;
- }
-
- mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
-
- if (dismissable) {
- mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
- } else {
- mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
- }
-
- mMediaStorageNotification.tickerText = title;
- if (pi == null) {
- Intent intent = new Intent();
- pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- }
-
- mMediaStorageNotification.icon = icon;
- mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
- }
-
- final int notificationId = mMediaStorageNotification.icon;
- if (visible) {
- notificationManager.notify(notificationId, mMediaStorageNotification);
- } else {
- notificationManager.cancel(notificationId);
- }
- }
-}
diff --git a/services/java/com/android/server/status/Ticker.java b/services/java/com/android/server/status/Ticker.java
deleted file mode 100644
index e47185b..0000000
--- a/services/java/com/android/server/status/Ticker.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import com.android.internal.R;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.text.StaticLayout;
-import android.text.Layout.Alignment;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.TextSwitcher;
-import android.widget.TextView;
-import android.widget.ImageSwitcher;
-
-import java.util.ArrayList;
-
-
-abstract class Ticker {
- private static final int TICKER_SEGMENT_DELAY = 3000;
-
- private final class Segment {
- NotificationData notificationData;
- Drawable icon;
- CharSequence text;
- int current;
- int next;
- boolean first;
-
- StaticLayout getLayout(CharSequence substr) {
- int w = mTextSwitcher.getWidth() - mTextSwitcher.getPaddingLeft()
- - mTextSwitcher.getPaddingRight();
- return new StaticLayout(substr, mPaint, w, Alignment.ALIGN_NORMAL, 1, 0, true);
- }
-
- CharSequence rtrim(CharSequence substr, int start, int end) {
- while (end > start && !TextUtils.isGraphic(substr.charAt(end-1))) {
- end--;
- }
- if (end > start) {
- return substr.subSequence(start, end);
- }
- return null;
- }
-
- /** returns null if there is no more text */
- CharSequence getText() {
- if (this.current > this.text.length()) {
- return null;
- }
- CharSequence substr = this.text.subSequence(this.current, this.text.length());
- StaticLayout l = getLayout(substr);
- int lineCount = l.getLineCount();
- if (lineCount > 0) {
- int start = l.getLineStart(0);
- int end = l.getLineEnd(0);
- this.next = this.current + end;
- return rtrim(substr, start, end);
- } else {
- throw new RuntimeException("lineCount=" + lineCount + " current=" + current +
- " text=" + text);
- }
- }
-
- /** returns null if there is no more text */
- CharSequence advance() {
- this.first = false;
- int index = this.next;
- final int len = this.text.length();
- while (index < len && !TextUtils.isGraphic(this.text.charAt(index))) {
- index++;
- }
- if (index >= len) {
- return null;
- }
-
- CharSequence substr = this.text.subSequence(index, this.text.length());
- StaticLayout l = getLayout(substr);
- final int lineCount = l.getLineCount();
- int i;
- for (i=0; i<lineCount; i++) {
- int start = l.getLineStart(i);
- int end = l.getLineEnd(i);
- if (i == lineCount-1) {
- this.next = len;
- } else {
- this.next = index + l.getLineStart(i+1);
- }
- CharSequence result = rtrim(substr, start, end);
- if (result != null) {
- this.current = index + start;
- return result;
- }
- }
- this.current = len;
- return null;
- }
-
- Segment(NotificationData n, Drawable icon, CharSequence text) {
- this.notificationData = n;
- this.icon = icon;
- this.text = text;
- int index = 0;
- final int len = text.length();
- while (index < len && !TextUtils.isGraphic(text.charAt(index))) {
- index++;
- }
- this.current = index;
- this.next = index;
- this.first = true;
- }
- };
-
- private Handler mHandler = new Handler();
- private ArrayList<Segment> mSegments = new ArrayList();
- private TextPaint mPaint;
- private View mTickerView;
- private ImageSwitcher mIconSwitcher;
- private TextSwitcher mTextSwitcher;
-
- Ticker(Context context, StatusBarView sb) {
- mTickerView = sb.findViewById(R.id.ticker);
-
- mIconSwitcher = (ImageSwitcher)sb.findViewById(R.id.tickerIcon);
- mIconSwitcher.setInAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
- mIconSwitcher.setOutAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
-
- mTextSwitcher = (TextSwitcher)sb.findViewById(R.id.tickerText);
- mTextSwitcher.setInAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
- mTextSwitcher.setOutAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
-
- // Copy the paint style of one of the TextSwitchers children to use later for measuring
- TextView text = (TextView)mTextSwitcher.getChildAt(0);
- mPaint = text.getPaint();
- }
-
- void addEntry(NotificationData n, Drawable icon, CharSequence text) {
- int initialCount = mSegments.size();
-
- Segment newSegment = new Segment(n, icon, text);
-
- // prune out any preexisting ones for this notification, but not the current one.
- // let that finish, even if it's the same id
- for (int i=1; i<initialCount; i++) {
- Segment seg = mSegments.get(i);
- if (n.id == seg.notificationData.id && n.pkg.equals(seg.notificationData.pkg)) {
- // just update that one to use this new data instead
- mSegments.set(i, newSegment);
- // and since we know initialCount != 0, just return
- return ;
- }
- }
-
- mSegments.add(newSegment);
-
- if (initialCount == 0 && mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
- seg.first = false;
-
- mIconSwitcher.setAnimateFirstView(false);
- mIconSwitcher.reset();
- mIconSwitcher.setImageDrawable(seg.icon);
-
- mTextSwitcher.setAnimateFirstView(false);
- mTextSwitcher.reset();
- mTextSwitcher.setText(seg.getText());
-
- tickerStarting();
- scheduleAdvance();
- }
- }
-
- void halt() {
- mHandler.removeCallbacks(mAdvanceTicker);
- mSegments.clear();
- tickerHalting();
- }
-
- void reflowText() {
- if (mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
- CharSequence text = seg.getText();
- mTextSwitcher.setCurrentText(text);
- }
- }
-
- private Runnable mAdvanceTicker = new Runnable() {
- public void run() {
- while (mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
-
- if (seg.first) {
- // this makes the icon slide in for the first one for a given
- // notification even if there are two notifications with the
- // same icon in a row
- mIconSwitcher.setImageDrawable(seg.icon);
- }
- CharSequence text = seg.advance();
- if (text == null) {
- mSegments.remove(0);
- continue;
- }
- mTextSwitcher.setText(text);
-
- scheduleAdvance();
- break;
- }
- if (mSegments.size() == 0) {
- tickerDone();
- }
- }
- };
-
- private void scheduleAdvance() {
- mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
- }
-
- abstract void tickerStarting();
- abstract void tickerDone();
- abstract void tickerHalting();
-}
-
diff --git a/services/java/com/android/server/status/TickerView.java b/services/java/com/android/server/status/TickerView.java
deleted file mode 100644
index 099dffb..0000000
--- a/services/java/com/android/server/status/TickerView.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.TextSwitcher;
-
-
-public class TickerView extends TextSwitcher
-{
- Ticker mTicker;
-
- public TickerView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mTicker.reflowText();
- }
-}
-
diff --git a/services/java/com/android/server/status/TrackingPatternView.java b/services/java/com/android/server/status/TrackingPatternView.java
deleted file mode 100644
index 2c91aa4..0000000
--- a/services/java/com/android/server/status/TrackingPatternView.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.graphics.BitmapFactory;
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.graphics.Canvas;
-
-public class TrackingPatternView extends View {
- private Bitmap mTexture;
- private Paint mPaint;
- private int mTextureWidth;
- private int mTextureHeight;
-
- public TrackingPatternView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mTexture = BitmapFactory.decodeResource(getResources(),
- com.android.internal.R.drawable.status_bar_background);
- mTextureWidth = mTexture.getWidth();
- mTextureHeight = mTexture.getHeight();
-
- mPaint = new Paint();
- mPaint.setDither(false);
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- final Bitmap texture = mTexture;
- final Paint paint = mPaint;
-
- final int width = getWidth();
- final int height = getHeight();
-
- final int textureWidth = mTextureWidth;
- final int textureHeight = mTextureHeight;
-
- int x = 0;
- int y;
-
- while (x < width) {
- y = 0;
- while (y < height) {
- canvas.drawBitmap(texture, x, y, paint);
- y += textureHeight;
- }
- x += textureWidth;
- }
- }
-}
diff --git a/services/java/com/android/server/status/TrackingView.java b/services/java/com/android/server/status/TrackingView.java
deleted file mode 100644
index 8ec39c0..0000000
--- a/services/java/com/android/server/status/TrackingView.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-
-
-public class TrackingView extends LinearLayout {
- final Display mDisplay;
- StatusBarService mService;
- boolean mTracking;
- int mStartX, mStartY;
-
- public TrackingView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mDisplay = ((WindowManager)context.getSystemService(
- Context.WINDOW_SERVICE)).getDefaultDisplay();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mService.updateExpandedHeight();
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (down) {
- mService.deactivate();
- }
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mService.onTrackingViewAttached();
- }
-}
diff --git a/services/java/com/android/server/status/UsbStorageActivity.java b/services/java/com/android/server/status/UsbStorageActivity.java
deleted file mode 100644
index e8631c5..0000000
--- a/services/java/com/android/server/status/UsbStorageActivity.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2007 Google Inc.
- *
- * 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.status;
-
-import com.android.internal.R;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.storage.IMountService;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageEventListener;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.widget.ImageView;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.view.View;
-import android.view.Window;
-import android.util.Log;
-
-import java.util.List;
-
-/**
- * This activity is shown to the user for him/her to enable USB mass storage
- * on-demand (that is, when the USB cable is connected). It uses the alert
- * dialog style. It will be launched from a notification.
- */
-public class UsbStorageActivity extends Activity
- implements View.OnClickListener, OnCancelListener {
- private static final String TAG = "UsbStorageActivity";
-
- private Button mMountButton;
- private Button mUnmountButton;
- private ProgressBar mProgressBar;
- private TextView mBanner;
- private TextView mMessage;
- private ImageView mIcon;
- private StorageManager mStorageManager = null;
- private static final int DLG_CONFIRM_KILL_STORAGE_USERS = 1;
- private static final int DLG_ERROR_SHARING = 2;
- static final boolean localLOGV = false;
-
- /** Used to detect when the USB cable is unplugged, so we can call finish() */
- private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) {
- handleBatteryChanged(intent);
- }
- }
- };
-
- private StorageEventListener mStorageListener = new StorageEventListener() {
- @Override
- public void onStorageStateChanged(String path, String oldState, String newState) {
- final boolean on = newState.equals(Environment.MEDIA_SHARED);
- switchDisplay(on);
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (mStorageManager == null) {
- mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
- if (mStorageManager == null) {
- Log.w(TAG, "Failed to get StorageManager");
- }
- }
-
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setProgressBarIndeterminateVisibility(true);
-
- setTitle(getString(com.android.internal.R.string.usb_storage_activity_title));
-
- setContentView(com.android.internal.R.layout.usb_storage_activity);
-
- mIcon = (ImageView) findViewById(com.android.internal.R.id.icon);
- mBanner = (TextView) findViewById(com.android.internal.R.id.banner);
- mMessage = (TextView) findViewById(com.android.internal.R.id.message);
-
- mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button);
- mMountButton.setOnClickListener(this);
- mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button);
- mUnmountButton.setOnClickListener(this);
- mProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress);
- }
-
- private void switchDisplay(boolean usbStorageInUse) {
- if (usbStorageInUse) {
- mProgressBar.setVisibility(View.GONE);
- mUnmountButton.setVisibility(View.VISIBLE);
- mMountButton.setVisibility(View.GONE);
- mIcon.setImageResource(com.android.internal.R.drawable.usb_android_connected);
- mBanner.setText(com.android.internal.R.string.usb_storage_stop_title);
- mMessage.setText(com.android.internal.R.string.usb_storage_stop_message);
- } else {
- mProgressBar.setVisibility(View.GONE);
- mUnmountButton.setVisibility(View.GONE);
- mMountButton.setVisibility(View.VISIBLE);
- mIcon.setImageResource(com.android.internal.R.drawable.usb_android);
- mBanner.setText(com.android.internal.R.string.usb_storage_title);
- mMessage.setText(com.android.internal.R.string.usb_storage_message);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- mStorageManager.registerListener(mStorageListener);
- registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
- try {
- switchDisplay(mStorageManager.isUsbMassStorageEnabled());
- } catch (Exception ex) {
- Log.e(TAG, "Failed to read UMS enable state", ex);
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- unregisterReceiver(mBatteryReceiver);
- if (mStorageManager == null && mStorageListener != null) {
- mStorageManager.unregisterListener(mStorageListener);
- }
- }
-
- private void handleBatteryChanged(Intent intent) {
- int pluggedType = intent.getIntExtra("plugged", 0);
- if (pluggedType == 0) {
- // It was disconnected from the plug, so finish
- finish();
- }
- }
-
- private IMountService getMountService() {
- IBinder service = ServiceManager.getService("mount");
- if (service != null) {
- return IMountService.Stub.asInterface(service);
- }
- return null;
- }
-
- @Override
- public Dialog onCreateDialog(int id, Bundle args) {
- switch (id) {
- case DLG_CONFIRM_KILL_STORAGE_USERS:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.dlg_confirm_kill_storage_users_title)
- .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- switchUsbMassStorageAsync(true);
- }})
- .setNegativeButton(R.string.cancel, null)
- .setMessage(R.string.dlg_confirm_kill_storage_users_text)
- .setOnCancelListener(this)
- .create();
- case DLG_ERROR_SHARING:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.dlg_error_title)
- .setNeutralButton(R.string.dlg_ok, null)
- .setMessage(R.string.usb_storage_error_message)
- .setOnCancelListener(this)
- .create();
- }
- return null;
- }
-
- private void showDialogInner(int id) {
- removeDialog(id);
- showDialog(id);
- }
-
- private void switchUsbMassStorageAsync(boolean on) {
- mUnmountButton.setVisibility(View.GONE);
- mMountButton.setVisibility(View.GONE);
-
- mProgressBar.setVisibility(View.VISIBLE);
- // will be hidden once USB mass storage kicks in (or fails)
-
- final boolean _on = on;
- new Thread() {
- public void run() {
- if (_on) {
- mStorageManager.enableUsbMassStorage();
- } else {
- mStorageManager.disableUsbMassStorage();
- }
- }
- }.start();
- }
-
- private void checkStorageUsers() {
- IMountService ims = getMountService();
- if (ims == null) {
- // Display error dialog
- showDialogInner(DLG_ERROR_SHARING);
- }
- String extStoragePath = Environment.getExternalStorageDirectory().toString();
- boolean showDialog = false;
- try {
- int[] stUsers = ims.getStorageUsers(extStoragePath);
- if (stUsers != null && stUsers.length > 0) {
- showDialog = true;
- } else {
- // List of applications on sdcard.
- ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- List<ApplicationInfo> infoList = am.getRunningExternalApplications();
- if (infoList != null && infoList.size() > 0) {
- showDialog = true;
- }
- }
- } catch (RemoteException e) {
- // Display error dialog
- showDialogInner(DLG_ERROR_SHARING);
- }
- if (showDialog) {
- // Display dialog to user
- showDialogInner(DLG_CONFIRM_KILL_STORAGE_USERS);
- } else {
- if (localLOGV) Log.i(TAG, "Enabling UMS");
- switchUsbMassStorageAsync(true);
- }
- }
-
- public void onClick(View v) {
- if (v == mMountButton) {
- // Check for list of storage users and display dialog if needed.
- checkStorageUsers();
- } else if (v == mUnmountButton) {
- if (localLOGV) Log.i(TAG, "Disabling UMS");
- switchUsbMassStorageAsync(false);
- }
- }
-
- public void onCancel(DialogInterface dialog) {
- finish();
- }
-
-}
diff --git a/services/java/com/android/server/status/package.html b/services/java/com/android/server/status/package.html
deleted file mode 100755
index c9f96a6..0000000
--- a/services/java/com/android/server/status/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>