summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-11-12 18:45:53 -0800
committerJean-Baptiste Queru <jbq@google.com>2009-11-13 13:53:39 -0800
commit9db3d07b9620b4269ab33f78604a36327e536ce1 (patch)
tree41e294f34b9695187af098cd42167489fb0c8fb0 /services/java/com/android/server
parent6c63ee4fc4acae4bbbbd2a49e0a68206221f0de0 (diff)
downloadframeworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.zip
frameworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.tar.gz
frameworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.tar.bz2
eclair snapshot
Diffstat (limited to 'services/java/com/android/server')
-rw-r--r--services/java/com/android/server/AccessibilityManagerService.java5
-rw-r--r--services/java/com/android/server/AppWidgetService.java38
-rw-r--r--services/java/com/android/server/BackupManagerService.java1274
-rw-r--r--services/java/com/android/server/BatteryService.java103
-rw-r--r--services/java/com/android/server/ConnectivityService.java1067
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java104
-rw-r--r--services/java/com/android/server/DockObserver.java184
-rwxr-xr-xservices/java/com/android/server/HardwareService.java327
-rw-r--r--services/java/com/android/server/HeadsetObserver.java48
-rw-r--r--services/java/com/android/server/InputDevice.java787
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java115
-rw-r--r--services/java/com/android/server/JournaledFile.java107
-rw-r--r--services/java/com/android/server/KeyInputQueue.java747
-rw-r--r--services/java/com/android/server/LocationManagerService.java193
-rw-r--r--services/java/com/android/server/MasterClearReceiver.java4
-rw-r--r--services/java/com/android/server/MountService.java11
-rwxr-xr-x[-rw-r--r--]services/java/com/android/server/NotificationManagerService.java174
-rw-r--r--services/java/com/android/server/PackageManagerBackupAgent.java17
-rw-r--r--services/java/com/android/server/PackageManagerService.java648
-rw-r--r--services/java/com/android/server/PowerManagerService.java625
-rw-r--r--services/java/com/android/server/ProcessStats.java82
-rw-r--r--services/java/com/android/server/RandomBlock.java5
-rw-r--r--services/java/com/android/server/SensorService.java21
-rw-r--r--services/java/com/android/server/ShutdownActivity.java46
-rw-r--r--services/java/com/android/server/SystemBackupAgent.java77
-rw-r--r--services/java/com/android/server/SystemServer.java205
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java38
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java702
-rw-r--r--services/java/com/android/server/WallpaperService.java203
-rw-r--r--services/java/com/android/server/WifiService.java481
-rw-r--r--services/java/com/android/server/WindowManagerService.java2308
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java1082
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java13
-rw-r--r--services/java/com/android/server/am/BroadcastRecord.java11
-rw-r--r--services/java/com/android/server/am/ConnectionRecord.java8
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java18
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java4
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java134
-rw-r--r--[-rwxr-xr-x]services/java/com/android/server/am/UsageStatsService.java14
-rw-r--r--services/java/com/android/server/status/NotificationData.java4
-rw-r--r--services/java/com/android/server/status/StatusBarIcon.java5
-rw-r--r--services/java/com/android/server/status/StatusBarPolicy.java408
-rw-r--r--services/java/com/android/server/status/StatusBarService.java96
-rw-r--r--services/java/com/android/server/status/TrackingPatternView.java2
44 files changed, 9700 insertions, 2845 deletions
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index 55007ba..f67a7ae 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -25,6 +25,7 @@ import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.IEventListener;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -615,6 +616,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mId = sIdCounter++;
mComponentName = componentName;
mIntent = new Intent().setComponent(mComponentName);
+ mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.accessibility_binding_label);
+ mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
}
/**
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 5439f8b..6bf7102 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -51,6 +51,7 @@ import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.HashMap;
import java.util.HashSet;
@@ -105,6 +106,7 @@ class AppWidgetService extends IAppWidgetService.Stub
}
Context mContext;
+ Locale mLocale;
PackageManager mPackageManager;
AlarmManager mAlarmManager;
ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
@@ -131,6 +133,11 @@ class AppWidgetService extends IAppWidgetService.Stub
mContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+ // Register for configuration changes so we can update the names
+ // of the widgets when the locale changes.
+ mContext.registerReceiver(mBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+
// Register for broadcasts about package install, etc., so we can
// update the provider list.
IntentFilter filter = new IntentFilter();
@@ -473,8 +480,10 @@ class AppWidgetService extends IAppWidgetService.Stub
public void stopListening(int hostId) {
synchronized (mAppWidgetIds) {
Host host = lookupHostLocked(getCallingUid(), hostId);
- host.callbacks = null;
- pruneHostLocked(host);
+ if (host != null) {
+ host.callbacks = null;
+ pruneHostLocked(host);
+ }
}
}
@@ -814,7 +823,10 @@ class AppWidgetService extends IAppWidgetService.Stub
temp.delete();
}
- writeStateToFileLocked(temp);
+ if (!writeStateToFileLocked(temp)) {
+ Log.w(TAG, "Failed to persist new settings");
+ return;
+ }
//noinspection ResultOfMethodCallIgnored
real.delete();
@@ -822,7 +834,7 @@ class AppWidgetService extends IAppWidgetService.Stub
temp.renameTo(real);
}
- void writeStateToFileLocked(File file) {
+ boolean writeStateToFileLocked(File file) {
FileOutputStream stream = null;
int N;
@@ -875,6 +887,7 @@ class AppWidgetService extends IAppWidgetService.Stub
out.endDocument();
stream.close();
+ return true;
} catch (IOException e) {
try {
if (stream != null) {
@@ -887,6 +900,7 @@ class AppWidgetService extends IAppWidgetService.Stub
//noinspection ResultOfMethodCallIgnored
file.delete();
}
+ return false;
}
}
@@ -1039,6 +1053,22 @@ class AppWidgetService extends IAppWidgetService.Stub
//Log.d(TAG, "received " + action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
sendInitialBroadcasts();
+ } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ Locale revised = Locale.getDefault();
+ if (revised == null || mLocale == null ||
+ !(revised.equals(mLocale))) {
+ mLocale = revised;
+
+ synchronized (mAppWidgetIds) {
+ int N = mInstalledProviders.size();
+ for (int i=N-1; i>=0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ String pkgName = p.info.provider.getPackageName();
+ updateProvidersForPackageLocked(pkgName);
+ }
+ saveStateLocked();
+ }
+ }
} else {
Uri uri = intent.getData();
if (uri == null) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6e28515..c3b591e 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -46,6 +46,8 @@ import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
@@ -54,6 +56,7 @@ import android.backup.IRestoreObserver;
import android.backup.IRestoreSession;
import android.backup.RestoreSet;
+import com.android.internal.backup.BackupConstants;
import com.android.internal.backup.LocalTransport;
import com.android.internal.backup.IBackupTransport;
@@ -63,6 +66,7 @@ import com.android.server.PackageManagerBackupAgent.Metadata;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
@@ -72,24 +76,47 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Random;
class BackupManagerService extends IBackupManager.Stub {
private static final String TAG = "BackupManagerService";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
// How often we perform a backup pass. Privileged external callers can
// trigger an immediate pass.
private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
+ // Random variation in backup scheduling time to avoid server load spikes
+ private static final int FUZZ_MILLIS = 5 * 60 * 1000;
+
// The amount of time between the initial provisioning of the device and
// the first backup pass.
private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
- private static final String RUN_BACKUP_ACTION = "_backup_run_";
+ private static final String RUN_BACKUP_ACTION = "android.backup.intent.RUN";
+ private static final String RUN_INITIALIZE_ACTION = "android.backup.intent.INIT";
+ private static final String RUN_CLEAR_ACTION = "android.backup.intent.CLEAR";
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
private static final int MSG_RUN_RESTORE = 3;
private static final int MSG_RUN_CLEAR = 4;
+ private static final int MSG_RUN_INITIALIZE = 5;
+
+ // Event tags -- see system/core/logcat/event-log-tags
+ private static final int BACKUP_DATA_CHANGED_EVENT = 2820;
+ private static final int BACKUP_START_EVENT = 2821;
+ private static final int BACKUP_TRANSPORT_FAILURE_EVENT = 2822;
+ private static final int BACKUP_AGENT_FAILURE_EVENT = 2823;
+ private static final int BACKUP_PACKAGE_EVENT = 2824;
+ private static final int BACKUP_SUCCESS_EVENT = 2825;
+ private static final int BACKUP_RESET_EVENT = 2826;
+ private static final int BACKUP_INITIALIZE_EVENT = 2827;
+
+ private static final int RESTORE_START_EVENT = 2830;
+ private static final int RESTORE_TRANSPORT_FAILURE_EVENT = 2831;
+ private static final int RESTORE_AGENT_FAILURE_EVENT = 2832;
+ private static final int RESTORE_PACKAGE_EVENT = 2833;
+ private static final int RESTORE_SUCCESS_EVENT = 2834;
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -100,18 +127,17 @@ class BackupManagerService extends IBackupManager.Stub {
private PowerManager mPowerManager;
private AlarmManager mAlarmManager;
- private boolean mEnabled; // access to this is synchronized on 'this'
- private boolean mProvisioned;
- private PowerManager.WakeLock mWakelock;
- private final BackupHandler mBackupHandler = new BackupHandler();
- private PendingIntent mRunBackupIntent;
- private BroadcastReceiver mRunBackupReceiver;
- private IntentFilter mRunBackupFilter;
+ boolean mEnabled; // access to this is synchronized on 'this'
+ boolean mProvisioned;
+ PowerManager.WakeLock mWakelock;
+ final BackupHandler mBackupHandler = new BackupHandler();
+ PendingIntent mRunBackupIntent, mRunInitIntent;
+ BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
// map UIDs to the set of backup client services within that UID's app set
- private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+ final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
= new SparseArray<HashSet<ApplicationInfo>>();
// set of backup services that have pending changes
- private class BackupRequest {
+ class BackupRequest {
public ApplicationInfo appInfo;
public boolean fullBackup;
@@ -125,35 +151,38 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
// Backups that we haven't started yet.
- private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
+ HashMap<ApplicationInfo,BackupRequest> mPendingBackups
= new HashMap<ApplicationInfo,BackupRequest>();
// Pseudoname that we use for the Package Manager metadata "package"
- private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+ static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
// locking around the pending-backup management
- private final Object mQueueLock = new Object();
+ final Object mQueueLock = new Object();
// The thread performing the sequence of queued backups binds to each app's agent
// in succession. Bind notifications are asynchronously delivered through the
// Activity Manager; use this lock object to signal when a requested binding has
// completed.
- private final Object mAgentConnectLock = new Object();
- private IBackupAgent mConnectedAgent;
- private volatile boolean mConnecting;
+ final Object mAgentConnectLock = new Object();
+ IBackupAgent mConnectedAgent;
+ volatile boolean mConnecting;
+ volatile boolean mBackupOrRestoreInProgress = false;
+ volatile long mLastBackupPass;
+ volatile long mNextBackupPass;
- // A similar synchronicity mechanism around clearing apps' data for restore
- private final Object mClearDataLock = new Object();
- private volatile boolean mClearingData;
+ // A similar synchronization mechanism around clearing apps' data for restore
+ final Object mClearDataLock = new Object();
+ volatile boolean mClearingData;
// Transport bookkeeping
- private final HashMap<String,IBackupTransport> mTransports
+ final HashMap<String,IBackupTransport> mTransports
= new HashMap<String,IBackupTransport>();
- private String mCurrentTransport;
- private IBackupTransport mLocalTransport, mGoogleTransport;
- private RestoreSession mActiveRestoreSession;
+ String mCurrentTransport;
+ IBackupTransport mLocalTransport, mGoogleTransport;
+ RestoreSession mActiveRestoreSession;
- private class RestoreParams {
+ class RestoreParams {
public IBackupTransport transport;
public IRestoreObserver observer;
public long token;
@@ -165,7 +194,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
- private class ClearParams {
+ class ClearParams {
public IBackupTransport transport;
public PackageInfo packageInfo;
@@ -176,11 +205,19 @@ class BackupManagerService extends IBackupManager.Stub {
}
// Where we keep our journal files and other bookkeeping
- private File mBaseStateDir;
- private File mDataDir;
- private File mJournalDir;
- private File mJournal;
- private RandomAccessFile mJournalStream;
+ File mBaseStateDir;
+ File mDataDir;
+ File mJournalDir;
+ File mJournal;
+
+ // Keep a log of all the apps we've ever backed up
+ private File mEverStored;
+ HashSet<String> mEverStoredApps = new HashSet<String>();
+
+ // Persistently track the need to do a full init
+ static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
+ HashSet<String> mPendingInits = new HashSet<String>(); // transport names
+ volatile boolean mInitInProgress = false;
public BackupManagerService(Context context) {
mContext = context;
@@ -193,27 +230,39 @@ class BackupManagerService extends IBackupManager.Stub {
// Set up our bookkeeping
boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.BACKUP_ENABLED, 0) != 0;
- // !!! TODO: mProvisioned needs to default to 0, not 1.
mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.BACKUP_PROVISIONED, 1) != 0;
+ Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
mDataDir = Environment.getDownloadCacheDirectory();
+ // Alarm receivers for scheduled backups & initialization operations
mRunBackupReceiver = new RunBackupReceiver();
- mRunBackupFilter = new IntentFilter();
- mRunBackupFilter.addAction(RUN_BACKUP_ACTION);
- context.registerReceiver(mRunBackupReceiver, mRunBackupFilter);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(RUN_BACKUP_ACTION);
+ context.registerReceiver(mRunBackupReceiver, filter,
+ android.Manifest.permission.BACKUP, null);
+
+ mRunInitReceiver = new RunInitializeReceiver();
+ filter = new IntentFilter();
+ filter.addAction(RUN_INITIALIZE_ACTION);
+ context.registerReceiver(mRunInitReceiver, filter,
+ android.Manifest.permission.BACKUP, null);
Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
- // !!! TODO: restrict delivery to our receiver; the naive setClass() doesn't seem to work
- //backupIntent.setClass(context, mRunBackupReceiver.getClass());
backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
+ Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
+ backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
+
// Set up the backup-request journaling
mJournalDir = new File(mBaseStateDir, "pending");
mJournalDir.mkdirs(); // creates mBaseStateDir along the way
- makeJournalLocked(); // okay because no other threads are running yet
+ mJournal = null; // will be created on first use
+
+ // Set up the various sorts of package tracking we do
+ initPackageTracking();
// Build our mapping of uid to backup client services. This implicitly
// schedules a backup pass on the Package Manager metadata the first
@@ -249,14 +298,6 @@ class BackupManagerService extends IBackupManager.Stub {
// leftover journal files into the pending backup set
parseLeftoverJournals();
- // Register for broadcasts about package install, etc., so we can
- // update the provider list.
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
-
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
@@ -267,55 +308,206 @@ class BackupManagerService extends IBackupManager.Stub {
private class RunBackupReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
- if (DEBUG) Log.v(TAG, "Running a backup pass");
+ synchronized (mQueueLock) {
+ if (mPendingInits.size() > 0) {
+ // If there are pending init operations, we process those
+ // and then settle into the usual periodic backup schedule.
+ if (DEBUG) Log.v(TAG, "Init pending at scheduled backup");
+ try {
+ mAlarmManager.cancel(mRunInitIntent);
+ mRunInitIntent.send();
+ } catch (PendingIntent.CanceledException ce) {
+ Log.e(TAG, "Run init intent cancelled");
+ // can't really do more than bail here
+ }
+ } else {
+ // Don't run backups now if we're disabled, not yet
+ // fully set up, in the middle of a backup already,
+ // or racing with an initialize pass.
+ if (mEnabled && mProvisioned
+ && !mBackupOrRestoreInProgress && !mInitInProgress) {
+ if (DEBUG) Log.v(TAG, "Running a backup pass");
+ mBackupOrRestoreInProgress = true;
+
+ // Acquire the wakelock and pass it to the backup thread. it will
+ // be released once backup concludes.
+ mWakelock.acquire();
+
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
+ mBackupHandler.sendMessage(msg);
+ } else {
+ Log.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned
+ + " b=" + mBackupOrRestoreInProgress + " i=" + mInitInProgress);
+ }
+ }
+ }
+ }
+ }
+ }
+ private class RunInitializeReceiver extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
synchronized (mQueueLock) {
- // acquire a wakelock and pass it to the backup thread. it will
- // be released once backup concludes.
+ if (DEBUG) Log.v(TAG, "Running a device init");
+ mInitInProgress = true;
+
+ // Acquire the wakelock and pass it to the init thread. it will
+ // be released once init concludes.
mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
mBackupHandler.sendMessage(msg);
}
}
}
}
- private void makeJournalLocked() {
- try {
- mJournal = File.createTempFile("journal", null, mJournalDir);
- mJournalStream = new RandomAccessFile(mJournal, "rwd");
- } catch (IOException e) {
- Log.e(TAG, "Unable to write backup journals");
- mJournal = null;
- mJournalStream = null;
+ private void initPackageTracking() {
+ if (DEBUG) Log.v(TAG, "Initializing package tracking");
+
+ // Keep a log of what apps we've ever backed up. Because we might have
+ // rebooted in the middle of an operation that was removing something from
+ // this log, we sanity-check its contents here and reconstruct it.
+ mEverStored = new File(mBaseStateDir, "processed");
+ File tempProcessedFile = new File(mBaseStateDir, "processed.new");
+
+ // If we were in the middle of removing something from the ever-backed-up
+ // file, there might be a transient "processed.new" file still present.
+ // Ignore it -- we'll validate "processed" against the current package set.
+ if (tempProcessedFile.exists()) {
+ tempProcessedFile.delete();
}
+
+ // If there are previous contents, parse them out then start a new
+ // file to continue the recordkeeping.
+ if (mEverStored.exists()) {
+ RandomAccessFile temp = null;
+ RandomAccessFile in = null;
+
+ try {
+ temp = new RandomAccessFile(tempProcessedFile, "rws");
+ in = new RandomAccessFile(mEverStored, "r");
+
+ while (true) {
+ PackageInfo info;
+ String pkg = in.readUTF();
+ try {
+ info = mPackageManager.getPackageInfo(pkg, 0);
+ mEverStoredApps.add(pkg);
+ temp.writeUTF(pkg);
+ if (DEBUG) Log.v(TAG, " + " + pkg);
+ } catch (NameNotFoundException e) {
+ // nope, this package was uninstalled; don't include it
+ if (DEBUG) Log.v(TAG, " - " + pkg);
+ }
+ }
+ } catch (EOFException e) {
+ // Once we've rewritten the backup history log, atomically replace the
+ // old one with the new one then reopen the file for continuing use.
+ if (!tempProcessedFile.renameTo(mEverStored)) {
+ Log.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error in processed file", e);
+ } finally {
+ try { if (temp != null) temp.close(); } catch (IOException e) {}
+ try { if (in != null) in.close(); } catch (IOException e) {}
+ }
+ }
+
+ // Register for broadcasts about package install, etc., so we can
+ // update the provider list.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
}
private void parseLeftoverJournals() {
- if (mJournal != null) {
- File[] allJournals = mJournalDir.listFiles();
- for (File f : allJournals) {
- if (f.compareTo(mJournal) != 0) {
- // This isn't the current journal, so it must be a leftover. Read
- // out the package names mentioned there and schedule them for
- // backup.
- try {
- Log.i(TAG, "Found stale backup journal, scheduling:");
- RandomAccessFile in = new RandomAccessFile(f, "r");
- while (true) {
- String packageName = in.readUTF();
- Log.i(TAG, " + " + packageName);
- dataChanged(packageName);
- }
- } catch (EOFException e) {
- // no more data; we're done
- } catch (Exception e) {
- // can't read it or other error; just skip it
- } finally {
- // close/delete the file
- f.delete();
+ for (File f : mJournalDir.listFiles()) {
+ if (mJournal == null || f.compareTo(mJournal) != 0) {
+ // This isn't the current journal, so it must be a leftover. Read
+ // out the package names mentioned there and schedule them for
+ // backup.
+ RandomAccessFile in = null;
+ try {
+ Log.i(TAG, "Found stale backup journal, scheduling:");
+ in = new RandomAccessFile(f, "r");
+ while (true) {
+ String packageName = in.readUTF();
+ Log.i(TAG, " + " + packageName);
+ dataChanged(packageName);
}
+ } catch (EOFException e) {
+ // no more data; we're done
+ } catch (Exception e) {
+ Log.e(TAG, "Can't read " + f, e);
+ } finally {
+ // close/delete the file
+ try { if (in != null) in.close(); } catch (IOException e) {}
+ f.delete();
+ }
+ }
+ }
+ }
+
+ // Maintain persistent state around whether need to do an initialize operation.
+ // Must be called with the queue lock held.
+ void recordInitPendingLocked(boolean isPending, String transportName) {
+ if (DEBUG) Log.i(TAG, "recordInitPendingLocked: " + isPending
+ + " on transport " + transportName);
+ try {
+ IBackupTransport transport = getTransport(transportName);
+ String transportDirName = transport.transportDirName();
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+
+ if (isPending) {
+ // We need an init before we can proceed with sending backup data.
+ // Record that with an entry in our set of pending inits, as well as
+ // journaling it via creation of a sentinel file.
+ mPendingInits.add(transportName);
+ try {
+ (new FileOutputStream(initPendingFile)).close();
+ } catch (IOException ioe) {
+ // Something is badly wrong with our permissions; just try to move on
+ }
+ } else {
+ // No more initialization needed; wipe the journal and reset our state.
+ initPendingFile.delete();
+ mPendingInits.remove(transportName);
+ }
+ } catch (RemoteException e) {
+ // can't happen; the transport is local
+ }
+ }
+
+ // Reset all of our bookkeeping, in response to having been told that
+ // the backend data has been wiped [due to idle expiry, for example],
+ // so we must re-upload all saved settings.
+ void resetBackupState(File stateFileDir) {
+ synchronized (mQueueLock) {
+ // Wipe the "what we've ever backed up" tracking
+ mEverStoredApps.clear();
+ mEverStored.delete();
+
+ // Remove all the state files
+ for (File sf : stateFileDir.listFiles()) {
+ // ... but don't touch the needs-init sentinel
+ if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
+ sf.delete();
+ }
+ }
+
+ // Enqueue a new backup of every participant
+ int N = mBackupParticipants.size();
+ for (int i=0; i<N; i++) {
+ int uid = mBackupParticipants.keyAt(i);
+ HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
+ for (ApplicationInfo app: participants) {
+ dataChanged(app.packageName);
}
}
}
@@ -327,6 +519,29 @@ class BackupManagerService extends IBackupManager.Stub {
if (DEBUG) Log.v(TAG, "Registering transport " + name + " = " + transport);
mTransports.put(name, transport);
}
+
+ // If the init sentinel file exists, we need to be sure to perform the init
+ // as soon as practical. We also create the state directory at registration
+ // time to ensure it's present from the outset.
+ try {
+ String transportName = transport.transportDirName();
+ File stateDir = new File(mBaseStateDir, transportName);
+ stateDir.mkdirs();
+
+ File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+ if (initSentinel.exists()) {
+ synchronized (mQueueLock) {
+ mPendingInits.add(transportName);
+
+ // TODO: pick a better starting time than now + 1 minute
+ long delay = 1000 * 60; // one minute, in milliseconds
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay, mRunInitIntent);
+ }
+ }
+ } catch (RemoteException e) {
+ // can't happen, the transport is local
+ }
}
// ----- Track installation/removal of packages -----
@@ -392,16 +607,21 @@ class BackupManagerService extends IBackupManager.Stub {
switch (msg.what) {
case MSG_RUN_BACKUP:
{
+ mLastBackupPass = System.currentTimeMillis();
+ mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
+
IBackupTransport transport = getTransport(mCurrentTransport);
if (transport == null) {
Log.v(TAG, "Backup requested but no transport available");
+ synchronized (mQueueLock) {
+ mBackupOrRestoreInProgress = false;
+ }
mWakelock.release();
break;
}
// snapshot the pending-backup set and work on that
ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
- File oldJournal = mJournal;
synchronized (mQueueLock) {
// Do we have any work to do?
if (mPendingBackups.size() > 0) {
@@ -412,14 +632,8 @@ class BackupManagerService extends IBackupManager.Stub {
mPendingBackups.clear();
// Start a new backup-queue journal file too
- if (mJournalStream != null) {
- try {
- mJournalStream.close();
- } catch (IOException e) {
- // don't need to do anything
- }
- makeJournalLocked();
- }
+ File oldJournal = mJournal;
+ mJournal = null;
// At this point, we have started a new journal file, and the old
// file identity is being passed to the backup processing thread.
@@ -429,6 +643,9 @@ class BackupManagerService extends IBackupManager.Stub {
(new PerformBackupThread(transport, queue, oldJournal)).start();
} else {
Log.v(TAG, "Backup requested but nothing pending");
+ synchronized (mQueueLock) {
+ mBackupOrRestoreInProgress = false;
+ }
mWakelock.release();
}
}
@@ -453,6 +670,20 @@ class BackupManagerService extends IBackupManager.Stub {
(new PerformClearThread(params.transport, params.packageInfo)).start();
break;
}
+
+ case MSG_RUN_INITIALIZE:
+ {
+ HashSet<String> queue;
+
+ // Snapshot the pending-init queue and work on that
+ synchronized (mQueueLock) {
+ queue = new HashSet<String>(mPendingInits);
+ mPendingInits.clear();
+ }
+
+ (new PerformInitializeThread(queue)).start();
+ break;
+ }
}
}
}
@@ -472,7 +703,12 @@ class BackupManagerService extends IBackupManager.Stub {
Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
for (PackageInfo p : targetPkgs) {
Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName
- + " uid=" + p.applicationInfo.uid);
+ + " uid=" + p.applicationInfo.uid
+ + " killAfterRestore="
+ + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false")
+ + " restoreNeedsApplication="
+ + (((p.applicationInfo.flags & ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION) != 0) ? "true" : "false")
+ );
}
}
@@ -485,6 +721,13 @@ class BackupManagerService extends IBackupManager.Stub {
mBackupParticipants.put(uid, set);
}
set.add(pkg.applicationInfo);
+
+ // If we've never seen this app before, schedule a backup for it
+ if (!mEverStoredApps.contains(pkg.packageName)) {
+ if (DEBUG) Log.i(TAG, "New app " + pkg.packageName
+ + " never backed up; scheduling");
+ dataChanged(pkg.packageName);
+ }
}
}
}
@@ -528,6 +771,7 @@ class BackupManagerService extends IBackupManager.Stub {
for (ApplicationInfo entry: set) {
if (entry.packageName.equals(pkg.packageName)) {
set.remove(entry);
+ removeEverBackedUp(pkg.packageName);
break;
}
}
@@ -540,15 +784,28 @@ class BackupManagerService extends IBackupManager.Stub {
}
// Returns the set of all applications that define an android:backupAgent attribute
- private List<PackageInfo> allAgentPackages() {
+ List<PackageInfo> allAgentPackages() {
// !!! TODO: cache this and regenerate only when necessary
int flags = PackageManager.GET_SIGNATURES;
List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
int N = packages.size();
for (int a = N-1; a >= 0; a--) {
- ApplicationInfo app = packages.get(a).applicationInfo;
- if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
- || app.backupAgentName == null) {
+ PackageInfo pkg = packages.get(a);
+ try {
+ ApplicationInfo app = pkg.applicationInfo;
+ if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+ || app.backupAgentName == null
+ || (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
+ pkg.packageName) != PackageManager.PERMISSION_GRANTED)) {
+ packages.remove(a);
+ }
+ else {
+ // we will need the shared library path, so look that up and store it here
+ app = mPackageManager.getApplicationInfo(pkg.packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES);
+ pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
+ }
+ } catch (NameNotFoundException e) {
packages.remove(a);
}
}
@@ -570,6 +827,64 @@ class BackupManagerService extends IBackupManager.Stub {
addPackageParticipantsLockedInner(packageName, allApps);
}
+ // Called from the backup thread: record that the given app has been successfully
+ // backed up at least once
+ void logBackupComplete(String packageName) {
+ if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
+
+ synchronized (mEverStoredApps) {
+ if (!mEverStoredApps.add(packageName)) return;
+
+ RandomAccessFile out = null;
+ try {
+ out = new RandomAccessFile(mEverStored, "rws");
+ out.seek(out.length());
+ out.writeUTF(packageName);
+ } catch (IOException e) {
+ Log.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
+ } finally {
+ try { if (out != null) out.close(); } catch (IOException e) {}
+ }
+ }
+ }
+
+ // Remove our awareness of having ever backed up the given package
+ void removeEverBackedUp(String packageName) {
+ if (DEBUG) Log.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:");
+
+ synchronized (mEverStoredApps) {
+ // Rewrite the file and rename to overwrite. If we reboot in the middle,
+ // we'll recognize on initialization time that the package no longer
+ // exists and fix it up then.
+ File tempKnownFile = new File(mBaseStateDir, "processed.new");
+ RandomAccessFile known = null;
+ try {
+ known = new RandomAccessFile(tempKnownFile, "rws");
+ mEverStoredApps.remove(packageName);
+ for (String s : mEverStoredApps) {
+ known.writeUTF(s);
+ if (DEBUG) Log.v(TAG, " " + s);
+ }
+ known.close();
+ known = null;
+ if (!tempKnownFile.renameTo(mEverStored)) {
+ throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
+ }
+ } catch (IOException e) {
+ // Bad: we couldn't create the new copy. For safety's sake we
+ // abandon the whole process and remove all what's-backed-up
+ // state entirely, meaning we'll force a backup pass for every
+ // participant on the next boot or [re]install.
+ Log.w(TAG, "Error rewriting " + mEverStored, e);
+ mEverStoredApps.clear();
+ tempKnownFile.delete();
+ mEverStored.delete();
+ } finally {
+ try { if (known != null) known.close(); } catch (IOException e) {}
+ }
+ }
+ }
+
// Return the given transport
private IBackupTransport getTransport(String transportName) {
synchronized (mTransports) {
@@ -637,6 +952,14 @@ class BackupManagerService extends IBackupManager.Stub {
synchronized(mClearDataLock) {
mClearingData = true;
+ /* This is causing some critical processes to be killed during setup.
+ Temporarily revert this change until we find a better solution.
+ try {
+ mActivityManager.clearApplicationUserData(packageName, observer);
+ } catch (RemoteException e) {
+ // can't happen because the activity manager is in this process
+ }
+ */
mPackageManager.clearApplicationUserData(packageName, observer);
// only wait 10 seconds for the clear data to happen
@@ -653,8 +976,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
class ClearDataObserver extends IPackageDataObserver.Stub {
- public void onRemoveCompleted(String packageName, boolean succeeded)
- throws android.os.RemoteException {
+ public void onRemoveCompleted(String packageName, boolean succeeded) {
synchronized(mClearDataLock) {
mClearingData = false;
mClearDataLock.notifyAll();
@@ -682,54 +1004,129 @@ class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) {
// can't happen; the transport is local
}
- mStateDir.mkdirs();
}
@Override
public void run() {
+ int status = BackupConstants.TRANSPORT_OK;
+ long startRealtime = SystemClock.elapsedRealtime();
if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
// Backups run at background priority
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- // The package manager doesn't have a proper <application> etc, but since
- // it's running here in the system process we can just set up its agent
- // directly and use a synthetic BackupRequest. We always run this pass
- // because it's cheap and this way we guarantee that we don't get out of
- // step even if we're selecting among various transports at run time.
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentPackages());
- BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
- pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
- processOneBackup(pmRequest,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()),
- mTransport);
-
- // Now run all the backups in our queue
- doQueuedBackups(mTransport);
-
- // Finally, tear down the transport
try {
- if (!mTransport.finishBackup()) {
- // STOPSHIP TODO: handle errors
- Log.e(TAG, "Backup failure in finishBackup()");
+ EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName());
+
+ // If we haven't stored package manager metadata yet, we must init the transport.
+ File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
+ if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
+ Log.i(TAG, "Initializing (wiping) backup state and transport storage");
+ resetBackupState(mStateDir); // Just to make sure.
+ status = mTransport.initializeDevice();
+ if (status == BackupConstants.TRANSPORT_OK) {
+ EventLog.writeEvent(BACKUP_INITIALIZE_EVENT);
+ } else {
+ EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "(initialize)");
+ Log.e(TAG, "Transport error in initializeDevice()");
+ }
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error in finishBackup()", e);
- }
- if (!mJournal.delete()) {
- Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath());
- }
+ // The package manager doesn't have a proper <application> etc, but since
+ // it's running here in the system process we can just set up its agent
+ // directly and use a synthetic BackupRequest. We always run this pass
+ // because it's cheap and this way we guarantee that we don't get out of
+ // step even if we're selecting among various transports at run time.
+ if (status == BackupConstants.TRANSPORT_OK) {
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, allAgentPackages());
+ BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+ pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
+ status = processOneBackup(pmRequest,
+ IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
+ }
+
+ if (status == BackupConstants.TRANSPORT_OK) {
+ // Now run all the backups in our queue
+ status = doQueuedBackups(mTransport);
+ }
- // Only once we're entirely finished do we release the wakelock
- mWakelock.release();
+ if (status == BackupConstants.TRANSPORT_OK) {
+ // Tell the transport to finish everything it has buffered
+ status = mTransport.finishBackup();
+ if (status == BackupConstants.TRANSPORT_OK) {
+ int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+ EventLog.writeEvent(BACKUP_SUCCESS_EVENT, mQueue.size(), millis);
+ } else {
+ EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "(finish)");
+ Log.e(TAG, "Transport error in finishBackup()");
+ }
+ }
+
+ if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ // The backend reports that our dataset has been wiped. We need to
+ // reset all of our bookkeeping and instead run a new backup pass for
+ // everything. This must come after mBackupOrRestoreInProgress is cleared.
+ EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName());
+ resetBackupState(mStateDir);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in backup thread", e);
+ status = BackupConstants.TRANSPORT_ERROR;
+ } finally {
+ // If things went wrong, we need to re-stage the apps we had expected
+ // to be backing up in this pass. This journals the package names in
+ // the current active pending-backup file, not in the we are holding
+ // here in mJournal.
+ if (status != BackupConstants.TRANSPORT_OK) {
+ Log.w(TAG, "Backup pass unsuccessful, restaging");
+ for (BackupRequest req : mQueue) {
+ dataChanged(req.appInfo.packageName);
+ }
+
+ // We also want to reset the backup schedule based on whatever
+ // the transport suggests by way of retry/backoff time.
+ try {
+ startBackupAlarmsLocked(mTransport.requestBackupTime());
+ } catch (RemoteException e) { /* cannot happen */ }
+ }
+
+ // Either backup was successful, in which case we of course do not need
+ // this pass's journal any more; or it failed, in which case we just
+ // re-enqueued all of these packages in the current active journal.
+ // Either way, we no longer need this pass's journal.
+ if (mJournal != null && !mJournal.delete()) {
+ Log.e(TAG, "Unable to remove backup journal file " + mJournal);
+ }
+
+ // Only once we're entirely finished do we indicate our completion
+ // and release the wakelock
+ synchronized (mQueueLock) {
+ mBackupOrRestoreInProgress = false;
+ }
+
+ if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ // This must come after mBackupOrRestoreInProgress is cleared.
+ backupNow();
+ }
+
+ mWakelock.release();
+ }
}
- private void doQueuedBackups(IBackupTransport transport) {
+ private int doQueuedBackups(IBackupTransport transport) {
for (BackupRequest request : mQueue) {
Log.d(TAG, "starting agent for backup of " + request);
+ // Don't run backup, even if requested, if the target app does not have
+ // the requisite permission
+ if (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
+ request.appInfo.packageName) != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "Skipping backup of unprivileged package "
+ + request.appInfo.packageName);
+ continue;
+ }
+
IBackupAgent agent = null;
int mode = (request.fullBackup)
? IApplicationThread.BACKUP_MODE_FULL
@@ -737,31 +1134,40 @@ class BackupManagerService extends IBackupManager.Stub {
try {
agent = bindToAgentSynchronous(request.appInfo, mode);
if (agent != null) {
- processOneBackup(request, agent, transport);
+ int result = processOneBackup(request, agent, transport);
+ if (result != BackupConstants.TRANSPORT_OK) return result;
}
-
- // unbind even on timeout, just in case
- mActivityManager.unbindBackupAgent(request.appInfo);
} catch (SecurityException ex) {
// Try for the next one.
Log.d(TAG, "error in bind/backup", ex);
- } catch (RemoteException e) {
- Log.v(TAG, "bind/backup threw");
- e.printStackTrace();
+ } finally {
+ try { // unbind even on timeout, just in case
+ mActivityManager.unbindBackupAgent(request.appInfo);
+ } catch (RemoteException e) {}
}
-
}
+
+ return BackupConstants.TRANSPORT_OK;
}
- void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
+ private int processOneBackup(BackupRequest request, IBackupAgent agent,
+ IBackupTransport transport) {
final String packageName = request.appInfo.packageName;
- Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+ if (DEBUG) Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+
+ File savedStateName = new File(mStateDir, packageName);
+ File backupDataName = new File(mDataDir, packageName + ".data");
+ File newStateName = new File(mStateDir, packageName + ".new");
+ ParcelFileDescriptor savedState = null;
+ ParcelFileDescriptor backupData = null;
+ ParcelFileDescriptor newState = null;
+
+ PackageInfo packInfo;
try {
// Look up the package info & signatures. This is first so that if it
// throws an exception, there's no file setup yet that would need to
// be unraveled.
- PackageInfo packInfo;
if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
// The metadata 'package' is synthetic
packInfo = new PackageInfo();
@@ -771,78 +1177,103 @@ class BackupManagerService extends IBackupManager.Stub {
PackageManager.GET_SIGNATURES);
}
- // !!! TODO: get the state file dir from the transport
- File savedStateName = new File(mStateDir, packageName);
- File backupDataName = new File(mDataDir, packageName + ".data");
- File newStateName = new File(mStateDir, packageName + ".new");
-
// In a full backup, we pass a null ParcelFileDescriptor as
// the saved-state "file"
- ParcelFileDescriptor savedState = (request.fullBackup) ? null
- : ParcelFileDescriptor.open(savedStateName,
+ if (!request.fullBackup) {
+ savedState = ParcelFileDescriptor.open(savedStateName,
ParcelFileDescriptor.MODE_READ_ONLY |
- ParcelFileDescriptor.MODE_CREATE);
+ ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
+ }
- backupDataName.delete();
- ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(backupDataName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
+ backupData = ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
- newStateName.delete();
- ParcelFileDescriptor newState =
- ParcelFileDescriptor.open(newStateName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
+ newState = ParcelFileDescriptor.open(newStateName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
// Run the target's backup pass
- boolean success = false;
- try {
- agent.doBackup(savedState, backupData, newState);
- success = true;
- } finally {
- if (savedState != null) {
- savedState.close();
+ agent.doBackup(savedState, backupData, newState);
+ logBackupComplete(packageName);
+ if (DEBUG) Log.v(TAG, "doBackup() success");
+ } catch (Exception e) {
+ Log.e(TAG, "Error backing up " + packageName, e);
+ EventLog.writeEvent(BACKUP_AGENT_FAILURE_EVENT, packageName, e.toString());
+ backupDataName.delete();
+ newStateName.delete();
+ return BackupConstants.TRANSPORT_ERROR;
+ } finally {
+ try { if (savedState != null) savedState.close(); } catch (IOException e) {}
+ try { if (backupData != null) backupData.close(); } catch (IOException e) {}
+ try { if (newState != null) newState.close(); } catch (IOException e) {}
+ savedState = backupData = newState = null;
+ }
+
+ // Now propagate the newly-backed-up data to the transport
+ int result = BackupConstants.TRANSPORT_OK;
+ try {
+ int size = (int) backupDataName.length();
+ if (size > 0) {
+ if (result == BackupConstants.TRANSPORT_OK) {
+ backupData = ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ result = transport.performBackup(packInfo, backupData);
}
- backupData.close();
- newState.close();
- }
-
- // Now propagate the newly-backed-up data to the transport
- if (success) {
- if (DEBUG) Log.v(TAG, "doBackup() success");
- if (backupDataName.length() > 0) {
- backupData =
- ParcelFileDescriptor.open(backupDataName,
- ParcelFileDescriptor.MODE_READ_ONLY);
- if (!transport.performBackup(packInfo, backupData)) {
- // STOPSHIP TODO: handle errors
- Log.e(TAG, "Backup failure in performBackup()");
- }
- } else {
- if (DEBUG) {
- Log.i(TAG, "no backup data written; not calling transport");
- }
+
+ // TODO - We call finishBackup() for each application backed up, because
+ // we need to know now whether it succeeded or failed. Instead, we should
+ // hold off on finishBackup() until the end, which implies holding off on
+ // renaming *all* the output state files (see below) until that happens.
+
+ if (result == BackupConstants.TRANSPORT_OK) {
+ result = transport.finishBackup();
}
+ } else {
+ if (DEBUG) Log.i(TAG, "no backup data written; not calling transport");
+ }
- // After successful transport, delete the now-stale data
- // and juggle the files so that next time we supply the agent
- // with the new state file it just created.
+ // After successful transport, delete the now-stale data
+ // and juggle the files so that next time we supply the agent
+ // with the new state file it just created.
+ if (result == BackupConstants.TRANSPORT_OK) {
backupDataName.delete();
newStateName.renameTo(savedStateName);
+ EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size);
+ } else {
+ EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
}
} catch (Exception e) {
- Log.e(TAG, "Error backing up " + packageName, e);
+ Log.e(TAG, "Transport error backing up " + packageName, e);
+ EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
+ result = BackupConstants.TRANSPORT_ERROR;
+ } finally {
+ try { if (backupData != null) backupData.close(); } catch (IOException e) {}
}
+
+ return result;
}
}
// ----- Restore handling -----
- private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
+ private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
+ // If the target resides on the system partition, we allow it to restore
+ // data from the like-named package in a restore set even if the signatures
+ // do not match. (Unlike general applications, those flashed to the system
+ // partition will be signed with the device's platform certificate, so on
+ // different phones the same system app will have different signatures.)
+ if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (DEBUG) Log.v(TAG, "System app " + target.packageName + " - skipping sig check");
+ return true;
+ }
+
// Allow unsigned apps, but not signed on one device and unsigned on the other
// !!! TODO: is this the right policy?
+ Signature[] deviceSigs = target.signatures;
if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs
+ " device=" + deviceSigs);
if ((storedSigs == null || storedSigs.length == 0)
@@ -878,7 +1309,6 @@ class BackupManagerService extends IBackupManager.Stub {
private IBackupTransport mTransport;
private IRestoreObserver mObserver;
private long mToken;
- private RestoreSet mImage;
private File mStateDir;
class RestoreRequest {
@@ -903,19 +1333,19 @@ class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) {
// can't happen; the transport is local
}
- mStateDir.mkdirs();
}
@Override
public void run() {
+ long startRealtime = SystemClock.elapsedRealtime();
if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
- + " mObserver=" + mObserver + " mToken=" + mToken);
+ + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken));
/**
* Restore sequence:
*
* 1. get the restore set description for our identity
* 2. for each app in the restore set:
- * 3.a. if it's restorable on this device, add it to the restore queue
+ * 2.a. if it's restorable on this device, add it to the restore queue
* 3. for each app in the restore queue:
* 3.a. clear the app data
* 3.b. get the restore data for the app from the transport
@@ -923,25 +1353,18 @@ class BackupManagerService extends IBackupManager.Stub {
* 3.d. agent.doRestore() with the data from the server
* 3.e. unbind the agent [and kill the app?]
* 4. shut down the transport
+ *
+ * On errors, we try our best to recover and move on to the next
+ * application, but if necessary we abort the whole operation --
+ * the user is waiting, after al.
*/
int error = -1; // assume error
// build the set of apps to restore
try {
- RestoreSet[] images = mTransport.getAvailableRestoreSets();
- if (images == null) {
- // STOPSHIP TODO: Handle the failure somehow?
- Log.e(TAG, "Error getting restore sets");
- return;
- }
-
- if (images.length == 0) {
- Log.i(TAG, "No restore sets available");
- return;
- }
-
- mImage = images[0];
+ // TODO: Log this before getAvailableRestoreSets, somehow
+ EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName(), mToken);
// Get the list of all packages which have backup enabled.
// (Include the Package Manager metadata pseudo-package first.)
@@ -965,23 +1388,28 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
- if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) {
- // STOPSHIP TODO: Handle the failure somehow?
+ if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
+ BackupConstants.TRANSPORT_OK) {
Log.e(TAG, "Error starting restore operation");
+ EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
return;
}
String packageName = mTransport.nextRestorePackage();
if (packageName == null) {
- // STOPSHIP TODO: Handle the failure somehow?
Log.e(TAG, "Error getting first restore package");
+ EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
return;
} else if (packageName.equals("")) {
Log.i(TAG, "No restore data available");
+ int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+ EventLog.writeEvent(RESTORE_SUCCESS_EVENT, 0, millis);
return;
} else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
+ "\", found only \"" + packageName + "\"");
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL,
+ "Package manager data missing");
return;
}
@@ -994,23 +1422,25 @@ class BackupManagerService extends IBackupManager.Stub {
// signature/version verification etc, so we simply do not proceed with
// the restore operation.
if (!pmAgent.hasMetadata()) {
- Log.i(TAG, "No restore metadata available, so not restoring settings");
+ Log.e(TAG, "No restore metadata available, so not restoring settings");
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL,
+ "Package manager restore metadata missing");
return;
}
int count = 0;
for (;;) {
packageName = mTransport.nextRestorePackage();
+
if (packageName == null) {
- // STOPSHIP TODO: Handle the failure somehow?
Log.e(TAG, "Error getting next restore package");
+ EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
return;
} else if (packageName.equals("")) {
break;
}
if (mObserver != null) {
- ++count;
try {
mObserver.onUpdate(count);
} catch (RemoteException e) {
@@ -1022,21 +1452,34 @@ class BackupManagerService extends IBackupManager.Stub {
Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
if (metaInfo == null) {
Log.e(TAG, "Missing metadata for " + packageName);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+ "Package metadata missing");
+ continue;
+ }
+
+ PackageInfo packageInfo;
+ try {
+ int flags = PackageManager.GET_SIGNATURES;
+ packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Invalid package restoring data", e);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+ "Package missing on device");
continue;
}
- int flags = PackageManager.GET_SIGNATURES;
- PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
if (metaInfo.versionCode > packageInfo.versionCode) {
- Log.w(TAG, "Package " + packageName
- + " restore version [" + metaInfo.versionCode
- + "] is too new for installed version ["
- + packageInfo.versionCode + "]");
+ String message = "Version " + metaInfo.versionCode
+ + " > installed version " + packageInfo.versionCode;
+ Log.w(TAG, "Package " + packageName + ": " + message);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, message);
continue;
}
- if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
+ if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
Log.w(TAG, "Signature mismatch restoring " + packageName);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+ "Signature mismatch");
continue;
}
@@ -1045,41 +1488,66 @@ class BackupManagerService extends IBackupManager.Stub {
+ "] is compatible with installed version ["
+ packageInfo.versionCode + "]");
- // Now perform the actual restore
+ // Now perform the actual restore: first clear the app's data
+ // if appropriate
clearApplicationDataSynchronous(packageName);
+
+ // Then set up and bind the agent (with a restricted Application object
+ // unless the application says otherwise)
+ boolean useRealApp = (packageInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION) != 0;
+ if (DEBUG && useRealApp) {
+ Log.v(TAG, "agent requires real Application subclass for restore");
+ }
IBackupAgent agent = bindToAgentSynchronous(
packageInfo.applicationInfo,
- IApplicationThread.BACKUP_MODE_RESTORE);
+ (useRealApp ? IApplicationThread.BACKUP_MODE_INCREMENTAL
+ : IApplicationThread.BACKUP_MODE_RESTORE));
if (agent == null) {
Log.w(TAG, "Can't find backup agent for " + packageName);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+ "Restore agent missing");
continue;
}
+ // And then finally run the restore on this agent
try {
processOneRestore(packageInfo, metaInfo.versionCode, agent);
+ ++count;
} finally {
- // unbind even on timeout or failure, just in case
+ // unbind and tidy up even on timeout or failure, just in case
mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
+
+ // The agent was probably running with a stub Application object,
+ // which isn't a valid run mode for the main app logic. Shut
+ // down the app so that next time it's launched, it gets the
+ // usual full initialization.
+ if ((packageInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
+ if (DEBUG) Log.d(TAG, "Restore complete, killing host process of "
+ + packageInfo.applicationInfo.processName);
+ mActivityManager.killApplicationProcess(
+ packageInfo.applicationInfo.processName,
+ packageInfo.applicationInfo.uid);
+ }
}
}
// if we get this far, report success to the observer
error = 0;
- } catch (NameNotFoundException e) {
- // STOPSHIP TODO: Handle the failure somehow?
- Log.e(TAG, "Invalid paackage restoring data", e);
- } catch (RemoteException e) {
- // STOPSHIP TODO: Handle the failure somehow?
- Log.e(TAG, "Error restoring data", e);
+ int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+ EventLog.writeEvent(RESTORE_SUCCESS_EVENT, count, millis);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in restore thread", e);
} finally {
+ if (DEBUG) Log.d(TAG, "finishing restore mObserver=" + mObserver);
+
try {
mTransport.finishRestore();
} catch (RemoteException e) {
Log.e(TAG, "Error finishing restore", e);
}
- Log.d(TAG, "finishing restore mObserver=" + mObserver);
-
if (mObserver != null) {
try {
mObserver.restoreFinished(error);
@@ -1089,6 +1557,9 @@ class BackupManagerService extends IBackupManager.Stub {
}
// done; we can finally release the wakelock
+ synchronized (mQueueLock) {
+ mBackupOrRestoreInProgress = false;
+ }
mWakelock.release();
}
}
@@ -1098,51 +1569,78 @@ class BackupManagerService extends IBackupManager.Stub {
// !!! TODO: actually run the restore through mTransport
final String packageName = app.packageName;
- Log.d(TAG, "processOneRestore packageName=" + packageName);
+ if (DEBUG) Log.d(TAG, "processOneRestore packageName=" + packageName);
+
+ // Don't restore to unprivileged packages
+ if (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
+ packageName) != PackageManager.PERMISSION_GRANTED) {
+ Log.d(TAG, "Skipping restore of unprivileged package " + packageName);
+ }
// !!! TODO: get the dirs from the transport
File backupDataName = new File(mDataDir, packageName + ".restore");
- backupDataName.delete();
+ File newStateName = new File(mStateDir, packageName + ".new");
+ File savedStateName = new File(mStateDir, packageName);
+
+ ParcelFileDescriptor backupData = null;
+ ParcelFileDescriptor newState = null;
+
try {
- ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(backupDataName,
+ // Run the transport's restore pass
+ backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
- // Run the transport's restore pass
- // Run the target's backup pass
- try {
- if (!mTransport.getRestoreData(backupData)) {
- // STOPSHIP TODO: Handle this error somehow?
- Log.e(TAG, "Error getting restore data for " + packageName);
- return;
- }
- } finally {
- backupData.close();
+ if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
+ Log.e(TAG, "Error getting restore data for " + packageName);
+ EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
+ return;
}
// Okay, we have the data. Now have the agent do the restore.
- File newStateName = new File(mStateDir, packageName + ".new");
- ParcelFileDescriptor newState =
- ParcelFileDescriptor.open(newStateName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
-
+ backupData.close();
backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
- try {
- agent.doRestore(backupData, appVersionCode, newState);
- } finally {
- newState.close();
- backupData.close();
- }
+ newState = ParcelFileDescriptor.open(newStateName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
+
+ agent.doRestore(backupData, appVersionCode, newState);
// if everything went okay, remember the recorded state now
- File savedStateName = new File(mStateDir, packageName);
- newStateName.renameTo(savedStateName);
+ //
+ // !!! TODO: the restored data should be migrated on the server
+ // side into the current dataset. In that case the new state file
+ // we just created would reflect the data already extant in the
+ // backend, so there'd be nothing more to do. Until that happens,
+ // however, we need to make sure that we record the data to the
+ // current backend dataset. (Yes, this means shipping the data over
+ // the wire in both directions. That's bad, but consistency comes
+ // first, then efficiency.) Once we introduce server-side data
+ // migration to the newly-restored device's dataset, we will change
+ // the following from a discard of the newly-written state to the
+ // "correct" operation of renaming into the canonical state blob.
+ newStateName.delete(); // TODO: remove; see above comment
+ //newStateName.renameTo(savedStateName); // TODO: replace with this
+
+ int size = (int) backupDataName.length();
+ EventLog.writeEvent(RESTORE_PACKAGE_EVENT, packageName, size);
} catch (Exception e) {
Log.e(TAG, "Error restoring data for " + packageName, e);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, e.toString());
+
+ // If the agent fails restore, it might have put the app's data
+ // into an incoherent state. For consistency we wipe its data
+ // again in this case before propagating the exception
+ clearApplicationDataSynchronous(packageName);
+ } finally {
+ backupDataName.delete();
+ try { if (backupData != null) backupData.close(); } catch (IOException e) {}
+ try { if (newState != null) newState.close(); } catch (IOException e) {}
+ backupData = newState = null;
}
}
}
@@ -1165,17 +1663,88 @@ class BackupManagerService extends IBackupManager.Stub {
stateFile.delete();
// Tell the transport to remove all the persistent storage for the app
+ // TODO - need to handle failures
mTransport.clearBackupData(mPackage);
} catch (RemoteException e) {
// can't happen; the transport is local
} finally {
try {
+ // TODO - need to handle failures
mTransport.finishBackup();
} catch (RemoteException e) {
// can't happen; the transport is local
}
// Last but not least, release the cpu
+ synchronized (mQueueLock) {
+ mBackupOrRestoreInProgress = false;
+ }
+ mWakelock.release();
+ }
+ }
+ }
+
+ class PerformInitializeThread extends Thread {
+ HashSet<String> mQueue;
+
+ PerformInitializeThread(HashSet<String> transportNames) {
+ mQueue = transportNames;
+ }
+
+ @Override
+ public void run() {
+ try {
+ for (String transportName : mQueue) {
+ IBackupTransport transport = getTransport(transportName);
+ if (transport == null) {
+ Log.e(TAG, "Requested init for " + transportName + " but not found");
+ continue;
+ }
+
+ Log.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
+ EventLog.writeEvent(BACKUP_START_EVENT, transport.transportDirName());
+ long startRealtime = SystemClock.elapsedRealtime();
+ int status = transport.initializeDevice();
+
+ if (status == BackupConstants.TRANSPORT_OK) {
+ status = transport.finishBackup();
+ }
+
+ // Okay, the wipe really happened. Clean up our local bookkeeping.
+ if (status == BackupConstants.TRANSPORT_OK) {
+ Log.i(TAG, "Device init successful");
+ int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+ EventLog.writeEvent(BACKUP_INITIALIZE_EVENT);
+ resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
+ EventLog.writeEvent(BACKUP_SUCCESS_EVENT, 0, millis);
+ synchronized (mQueueLock) {
+ recordInitPendingLocked(false, transportName);
+ }
+ } else {
+ // If this didn't work, requeue this one and try again
+ // after a suitable interval
+ Log.e(TAG, "Transport error in initializeDevice()");
+ EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "(initialize)");
+ synchronized (mQueueLock) {
+ recordInitPendingLocked(true, transportName);
+ }
+ // do this via another alarm to make sure of the wakelock states
+ long delay = transport.requestBackupTime();
+ if (DEBUG) Log.w(TAG, "init failed on "
+ + transportName + " resched in " + delay);
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay, mRunInitIntent);
+ }
+ }
+ } catch (RemoteException e) {
+ // can't happen; the transports are local
+ } catch (Exception e) {
+ Log.e(TAG, "Unexpected error performing init", e);
+ } finally {
+ // Done; indicate that we're finished and release the wakelock
+ synchronized (mQueueLock) {
+ mInitInProgress = false;
+ }
mWakelock.release();
}
}
@@ -1184,10 +1753,11 @@ class BackupManagerService extends IBackupManager.Stub {
// ----- IBackupManager binder interface -----
- public void dataChanged(String packageName) throws RemoteException {
+ public void dataChanged(String packageName) {
// 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(BACKUP_DATA_CHANGED_EVENT, packageName);
// If the caller does not hold the BACKUP permission, it can only request a
// backup of its own data.
@@ -1235,19 +1805,23 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
} else {
- Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'");
+ Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
}
}
private void writeToJournalLocked(String str) {
- if (mJournalStream != null) {
- try {
- mJournalStream.writeUTF(str);
- } catch (IOException e) {
- Log.e(TAG, "Error writing to backup journal");
- mJournalStream = null;
- mJournal = null;
- }
+ RandomAccessFile out = null;
+ try {
+ if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
+ out = new RandomAccessFile(mJournal, "rws");
+ out.seek(out.length());
+ out.writeUTF(str);
+ } catch (IOException e) {
+ Log.e(TAG, "Can't write " + str + " to backup journal", e);
+ mJournal = null;
+ } finally {
+ try { if (out != null) out.close(); } catch (IOException e) {}
}
}
@@ -1288,10 +1862,12 @@ class BackupManagerService extends IBackupManager.Stub {
if (DEBUG) Log.v(TAG, "Found the app - running clear process");
// found it; fire off the clear request
synchronized (mQueueLock) {
+ long oldId = Binder.clearCallingIdentity();
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
new ClearParams(getTransport(mCurrentTransport), info));
mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
}
break;
}
@@ -1300,13 +1876,16 @@ class BackupManagerService extends IBackupManager.Stub {
// Run a backup pass immediately for any applications that have declared
// that they have pending updates.
- public void backupNow() throws RemoteException {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "backupNow");
+ public void backupNow() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
+ // Because the alarms we are using can jitter, and we want an *immediate*
+ // backup pass to happen, we restart the timer beginning with "next time,"
+ // then manually fire the backup trigger intent ourselves.
+ startBackupAlarmsLocked(BACKUP_INTERVAL);
try {
- if (DEBUG) Log.v(TAG, "sending immediate backup broadcast");
mRunBackupIntent.send();
} catch (PendingIntent.CanceledException e) {
// should never happen
@@ -1320,6 +1899,8 @@ class BackupManagerService extends IBackupManager.Stub {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
+ Log.i(TAG, "Backup enabled => " + enable);
+
boolean wasEnabled = mEnabled;
synchronized (this) {
Settings.Secure.putInt(mContext.getContentResolver(),
@@ -1333,7 +1914,27 @@ class BackupManagerService extends IBackupManager.Stub {
startBackupAlarmsLocked(BACKUP_INTERVAL);
} else if (!enable) {
// No longer enabled, so stop running backups
+ if (DEBUG) Log.i(TAG, "Opting out of backup");
+
mAlarmManager.cancel(mRunBackupIntent);
+
+ // This also constitutes an opt-out, so we wipe any data for
+ // this device from the backend. We start that process with
+ // an alarm in order to guarantee wakelock states.
+ if (wasEnabled && mProvisioned) {
+ // NOTE: we currently flush every registered transport, not just
+ // the currently-active one.
+ HashSet<String> allTransports;
+ synchronized (mTransports) {
+ allTransports = new HashSet<String>(mTransports.keySet());
+ }
+ // build the set of transports for which we are posting an init
+ for (String transport : allTransports) {
+ recordInitPendingLocked(true, transport);
+ }
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
+ mRunInitIntent);
+ }
}
}
}
@@ -1363,20 +1964,27 @@ class BackupManagerService extends IBackupManager.Stub {
}
private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
- long when = System.currentTimeMillis() + delayBeforeFirstBackup;
- mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, when,
- BACKUP_INTERVAL, mRunBackupIntent);
+ // We used to use setInexactRepeating(), but that may be linked to
+ // backups running at :00 more often than not, creating load spikes.
+ // Schedule at an exact time for now, and also add a bit of "fuzz".
+
+ Random random = new Random();
+ long when = System.currentTimeMillis() + delayBeforeFirstBackup +
+ random.nextInt(FUZZ_MILLIS);
+ mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
+ BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
+ mNextBackupPass = when;
}
// Report whether the backup mechanism is currently enabled
public boolean isBackupEnabled() {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
return mEnabled; // no need to synchronize just to read it
}
// Report the name of the currently active transport
public String getCurrentTransport() {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getCurrentTransport");
Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
return mCurrentTransport;
@@ -1405,7 +2013,7 @@ class BackupManagerService extends IBackupManager.Stub {
// name is not one of the available transports, no action is taken and the method
// returns null.
public String selectBackupTransport(String transport) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
synchronized (mTransports) {
String prevTransport = null;
@@ -1459,7 +2067,7 @@ class BackupManagerService extends IBackupManager.Stub {
// Hand off a restore session
public IRestoreSession beginRestoreSession(String transport) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
synchronized(this) {
if (mActiveRestoreSession != null) {
@@ -1484,55 +2092,80 @@ class BackupManagerService extends IBackupManager.Stub {
}
// --- Binder interface ---
- public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ public synchronized RestoreSet[] getAvailableRestoreSets() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getAvailableRestoreSets");
try {
- synchronized(this) {
- if (mRestoreSets == null) {
+ if (mRestoreTransport == null) {
+ Log.w(TAG, "Null transport getting restore sets");
+ return null;
+ }
+ if (mRestoreSets == null) { // valid transport; do the one-time fetch
mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
+ if (mRestoreSets == null) EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
}
return mRestoreSets;
- }
- } catch (RuntimeException e) {
- Log.d(TAG, "getAvailableRestoreSets exception");
- e.printStackTrace();
- throw e;
+ } catch (Exception e) {
+ Log.e(TAG, "Error in getAvailableRestoreSets", e);
+ return null;
}
}
- public int performRestore(long token, IRestoreObserver observer)
- throws android.os.RemoteException {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "performRestore");
+ public synchronized int performRestore(long token, IRestoreObserver observer) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "performRestore");
+
+ if (DEBUG) Log.d(TAG, "performRestore token=" + Long.toHexString(token)
+ + " observer=" + observer);
- Log.d(TAG, "performRestore token=" + token + " observer=" + observer);
+ if (mRestoreTransport == null || mRestoreSets == null) {
+ Log.e(TAG, "Ignoring performRestore() with no restore set");
+ return -1;
+ }
+
+ synchronized (mQueueLock) {
+ if (mBackupOrRestoreInProgress) {
+ Log.e(TAG, "Backup pass in progress, restore aborted");
+ return -1;
+ }
- if (mRestoreSets != null) {
for (int i = 0; i < mRestoreSets.length; i++) {
if (token == mRestoreSets[i].token) {
+ long oldId = Binder.clearCallingIdentity();
+ // Suppress backups until the restore operation is finished
+ mBackupOrRestoreInProgress = true;
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(mRestoreTransport, observer, token);
mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
return 0;
}
}
- } else {
- if (DEBUG) Log.v(TAG, "No current restore set, not doing restore");
}
+
+ Log.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
return -1;
}
- public void endRestoreSession() throws android.os.RemoteException {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ public synchronized void endRestoreSession() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"endRestoreSession");
- Log.d(TAG, "endRestoreSession");
+ if (DEBUG) Log.d(TAG, "endRestoreSession");
+
+ synchronized (this) {
+ try {
+ if (mRestoreTransport != null) mRestoreTransport.finishRestore();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in finishRestore", e);
+ } finally {
+ mRestoreTransport = null;
+ }
+ }
- mRestoreTransport.finishRestore();
- mRestoreTransport = null;
- synchronized(BackupManagerService.this) {
+ synchronized (BackupManagerService.this) {
if (BackupManagerService.this.mActiveRestoreSession == this) {
BackupManagerService.this.mActiveRestoreSession = null;
} else {
@@ -1546,32 +2179,55 @@ class BackupManagerService extends IBackupManager.Stub {
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mQueueLock) {
- long oldId = Binder.clearCallingIdentity();
-
pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
- + " / " + (!mProvisioned ? "not " : "") + "provisioned");
+ + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+ + (!mBackupOrRestoreInProgress ? "not " : "") + "in progress / "
+ + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init / "
+ + (!mInitInProgress ? "not " : "") + "initializing");
+ pw.println("Last backup pass: " + mLastBackupPass
+ + " (now = " + System.currentTimeMillis() + ')');
+ pw.println(" next scheduled: " + mNextBackupPass);
+
pw.println("Available transports:");
for (String t : listAllTransports()) {
- String pad = (t.equals(mCurrentTransport)) ? " * " : " ";
- pw.println(pad + t);
+ pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
+ try {
+ File dir = new File(mBaseStateDir, getTransport(t).transportDirName());
+ for (File f : dir.listFiles()) {
+ pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in transportDirName()", e);
+ pw.println(" Error: " + e);
+ }
}
+
+ pw.println("Pending init: " + mPendingInits.size());
+ for (String s : mPendingInits) {
+ pw.println(" " + s);
+ }
+
int N = mBackupParticipants.size();
- pw.println("Participants: " + N);
+ pw.println("Participants:");
for (int i=0; i<N; i++) {
int uid = mBackupParticipants.keyAt(i);
pw.print(" uid: ");
pw.println(uid);
HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
for (ApplicationInfo app: participants) {
- pw.println(" " + app.toString());
+ pw.println(" " + app.packageName);
}
}
- pw.println("Pending: " + mPendingBackups.size());
+
+ pw.println("Ever backed up: " + mEverStoredApps.size());
+ for (String pkg : mEverStoredApps) {
+ pw.println(" " + pkg);
+ }
+
+ pw.println("Pending backup: " + mPendingBackups.size());
for (BackupRequest req : mPendingBackups.values()) {
pw.println(" " + req);
}
-
- Binder.restoreCallingIdentity(oldId);
}
}
}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 90d8c9d..bb36936 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -26,7 +26,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
import android.os.Binder;
-import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -45,7 +44,6 @@ import java.io.IOException;
import java.io.PrintWriter;
-
/**
* <p>BatteryService monitors the charging status, and charge level of the device
* battery. When these values change this service broadcasts the new values
@@ -113,18 +111,27 @@ class BatteryService extends Binder {
private int mLastBatteryVoltage;
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;
-
+
+ private int mLowBatteryWarningLevel;
+ private int mLowBatteryCloseWarningLevel;
+
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
private long mDischargeStartTime;
private int mDischargeStartLevel;
+ private boolean mSentLowBatteryBroadcast = false;
public BatteryService(Context context) {
mContext = context;
mBatteryStats = BatteryStatsService.getService();
+ mLowBatteryWarningLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryWarningLevel);
+ mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+
mUEventObserver.startObserving("SUBSYSTEM=power_supply");
// set initial status
@@ -171,6 +178,22 @@ class BatteryService extends Binder {
return mBatteryLevel;
}
+ void systemReady() {
+ // check our power situation now that it is safe to display the shutdown dialog.
+ shutdownIfNoPower();
+ }
+
+ private final void shutdownIfNoPower() {
+ // shut down gracefully if our battery is critically low and we are not powered.
+ // wait until the system has booted before attempting to display the shutdown dialog.
+ if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {
+ Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
+ intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+ }
+
private native void native_update();
private synchronized final void update() {
@@ -178,7 +201,9 @@ class BatteryService extends Binder {
boolean logOutlier = false;
long dischargeDuration = 0;
-
+
+ shutdownIfNoPower();
+
mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
if (mAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
@@ -247,18 +272,49 @@ class BatteryService extends Binder {
logOutlier = true;
}
+ final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
+ final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
+
+ /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
+ * - is just un-plugged (previously was plugged) and battery level is
+ * less than or equal to WARNING, or
+ * - is not plugged and battery level falls to WARNING boundary
+ * (becomes <= mLowBatteryWarningLevel).
+ */
+ final boolean sendBatteryLow = !plugged
+ && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mBatteryLevel <= mLowBatteryWarningLevel
+ && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+
+ sendIntent();
+
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
// applications may want to have smart behavior based on this.
+ Intent statusIntent = new Intent();
+ statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (mPlugType != 0 && mLastPlugType == 0) {
- Intent intent = new Intent(Intent.ACTION_POWER_CONNECTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
+ mContext.sendBroadcast(statusIntent);
}
else if (mPlugType == 0 && mLastPlugType != 0) {
- Intent intent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
+ mContext.sendBroadcast(statusIntent);
+ }
+
+ if (sendBatteryLow) {
+ mSentLowBatteryBroadcast = true;
+ statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
+ mContext.sendBroadcast(statusIntent);
+ } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
+ mSentLowBatteryBroadcast = false;
+ statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
+ mContext.sendBroadcast(statusIntent);
+ }
+
+ // This needs to be done after sendIntent() so that we get the lastest battery stats.
+ if (logOutlier && dischargeDuration != 0) {
+ logOutlier(dischargeDuration);
}
mLastBatteryStatus = mBatteryStatus;
@@ -269,13 +325,6 @@ class BatteryService extends Binder {
mLastBatteryVoltage = mBatteryVoltage;
mLastBatteryTemperature = mBatteryTemperature;
mLastBatteryLevelCritical = mBatteryLevelCritical;
-
- sendIntent();
-
- // This needs to be done after sendIntent() so that we get the lastest battery stats.
- if (logOutlier && dischargeDuration != 0) {
- logOutlier(dischargeDuration);
- }
}
}
@@ -291,16 +340,16 @@ class BatteryService extends Binder {
int icon = getIcon(mBatteryLevel);
- intent.putExtra("status", mBatteryStatus);
- intent.putExtra("health", mBatteryHealth);
- intent.putExtra("present", mBatteryPresent);
- intent.putExtra("level", mBatteryLevel);
- intent.putExtra("scale", BATTERY_SCALE);
- intent.putExtra("icon-small", icon);
- intent.putExtra("plugged", mPlugType);
- intent.putExtra("voltage", mBatteryVoltage);
- intent.putExtra("temperature", mBatteryTemperature);
- intent.putExtra("technology", mBatteryTechnology);
+ intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus);
+ intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth);
+ intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryPresent);
+ intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryLevel);
+ intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
+ intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
+ intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage);
+ intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature);
+ intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
if (false) {
Log.d(TAG, "updateBattery level:" + mBatteryLevel +
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 493bd09..78215b0 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -30,51 +30,136 @@ import android.net.NetworkStateTracker;
import android.net.wifi.WifiStateTracker;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import com.android.internal.telephony.Phone;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* @hide
*/
public class ConnectivityService extends IConnectivityManager.Stub {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String TAG = "ConnectivityService";
// Event log tags (must be in sync with event-log-tags)
private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
+ // how long to wait before switching back to a radio's default network
+ private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
+ // system property that can override the above value
+ private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
+ "android.telephony.apn-restore";
+
/**
* Sometimes we want to refer to the individual network state
* trackers separately, and sometimes we just want to treat them
* abstractly.
*/
private NetworkStateTracker mNetTrackers[];
- private WifiStateTracker mWifiStateTracker;
- private MobileDataStateTracker mMobileDataStateTracker;
+
+ /**
+ * A per Net list of the PID's that requested access to the net
+ * used both as a refcount and for per-PID DNS selection
+ */
+ private List mNetRequestersPids[];
+
private WifiWatchdogService mWifiWatchdogService;
+ // priority order of the nettrackers
+ // (excluding dynamically set mNetworkPreference)
+ // TODO - move mNetworkTypePreference into this
+ private int[] mPriorityList;
+
private Context mContext;
private int mNetworkPreference;
- private NetworkStateTracker mActiveNetwork;
+ private int mActiveDefaultNetwork = -1;
private int mNumDnsEntries;
- private static int sDnsChangeCounter;
private boolean mTestMode;
private static ConnectivityService sServiceInstance;
+ private Handler mHandler;
+
+ // list of DeathRecipients used to make sure features are turned off when
+ // a process dies
+ private List mFeatureUsers;
+
+ private boolean mSystemReady;
+ private ArrayList<Intent> mDeferredBroadcasts;
+
+ private class NetworkAttributes {
+ /**
+ * Class for holding settings read from resources.
+ */
+ public String mName;
+ public int mType;
+ public int mRadio;
+ public int mPriority;
+ public NetworkAttributes(String init) {
+ String fragments[] = init.split(",");
+ mName = fragments[0].toLowerCase();
+ if (fragments[1].toLowerCase().equals("wifi")) {
+ mRadio = ConnectivityManager.TYPE_WIFI;
+ } else {
+ mRadio = ConnectivityManager.TYPE_MOBILE;
+ }
+ if (mName.equals("default")) {
+ mType = mRadio;
+ } else if (mName.equals("mms")) {
+ mType = ConnectivityManager.TYPE_MOBILE_MMS;
+ } else if (mName.equals("supl")) {
+ mType = ConnectivityManager.TYPE_MOBILE_SUPL;
+ } else if (mName.equals("dun")) {
+ mType = ConnectivityManager.TYPE_MOBILE_DUN;
+ } else if (mName.equals("hipri")) {
+ mType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+ }
+ mPriority = Integer.parseInt(fragments[2]);
+ }
+ public boolean isDefault() {
+ return (mType == mRadio);
+ }
+ }
+ NetworkAttributes[] mNetAttributes;
+
+ private class RadioAttributes {
+ public String mName;
+ public int mPriority;
+ public int mSimultaneity;
+ public int mType;
+ public RadioAttributes(String init) {
+ String fragments[] = init.split(",");
+ mName = fragments[0].toLowerCase();
+ mPriority = Integer.parseInt(fragments[1]);
+ mSimultaneity = Integer.parseInt(fragments[2]);
+ if (mName.equals("wifi")) {
+ mType = ConnectivityManager.TYPE_WIFI;
+ } else {
+ mType = ConnectivityManager.TYPE_MOBILE;
+ }
+ }
+ }
+ RadioAttributes[] mRadioAttributes;
+
private static class ConnectivityThread extends Thread {
private Context mContext;
-
+
private ConnectivityThread(Context context) {
super("ConnectivityThread");
mContext = context;
@@ -89,11 +174,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
Looper.loop();
}
-
+
public static ConnectivityService getServiceInstance(Context context) {
ConnectivityThread thread = new ConnectivityThread(context);
thread.start();
-
+
synchronized (thread) {
while (sServiceInstance == null) {
try {
@@ -101,27 +186,71 @@ public class ConnectivityService extends IConnectivityManager.Stub {
thread.wait();
} catch (InterruptedException ignore) {
Log.e(TAG,
- "Unexpected InterruptedException while waiting for ConnectivityService thread");
+ "Unexpected InterruptedException while waiting"+
+ " for ConnectivityService thread");
}
}
}
-
+
return sServiceInstance;
}
}
-
+
public static ConnectivityService getInstance(Context context) {
return ConnectivityThread.getServiceInstance(context);
}
-
+
private ConnectivityService(Context context) {
if (DBG) Log.v(TAG, "ConnectivityService starting up");
mContext = context;
- mNetTrackers = new NetworkStateTracker[2];
- Handler handler = new MyHandler();
-
+ mNetTrackers = new NetworkStateTracker[
+ ConnectivityManager.MAX_NETWORK_TYPE+1];
+ mHandler = new MyHandler();
+
mNetworkPreference = getPersistedNetworkPreference();
-
+
+ // Load device network attributes from resources
+ mNetAttributes = new NetworkAttributes[
+ ConnectivityManager.MAX_NETWORK_TYPE+1];
+ mRadioAttributes = new RadioAttributes[
+ ConnectivityManager.MAX_RADIO_TYPE+1];
+ String[] naStrings = context.getResources().getStringArray(
+ com.android.internal.R.array.networkAttributes);
+ // TODO - what if the setting has gaps/unknown types?
+ for (String a : naStrings) {
+ NetworkAttributes n = new NetworkAttributes(a);
+ mNetAttributes[n.mType] = n;
+ }
+ String[] raStrings = context.getResources().getStringArray(
+ com.android.internal.R.array.radioAttributes);
+ for (String a : raStrings) {
+ RadioAttributes r = new RadioAttributes(a);
+ mRadioAttributes[r.mType] = r;
+ }
+
+ // high priority first
+ mPriorityList = new int[naStrings.length];
+ {
+ int priority = 0; //lowest
+ int nextPos = naStrings.length-1;
+ while (nextPos>-1) {
+ for (int i = 0; i < mNetAttributes.length; i++) {
+ if(mNetAttributes[i].mPriority == priority) {
+ mPriorityList[nextPos--] = i;
+ }
+ }
+ priority++;
+ }
+ }
+
+ mNetRequestersPids =
+ new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
+ for (int i=0; i<=ConnectivityManager.MAX_NETWORK_TYPE; i++) {
+ mNetRequestersPids[i] = new ArrayList();
+ }
+
+ mFeatureUsers = new ArrayList();
+
/*
* Create the network state trackers for Wi-Fi and mobile
* data. Maybe this could be done with a factory class,
@@ -130,15 +259,36 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* to change very often.
*/
if (DBG) Log.v(TAG, "Starting Wifi Service.");
- mWifiStateTracker = new WifiStateTracker(context, handler);
- WifiService wifiService = new WifiService(context, mWifiStateTracker);
+ WifiStateTracker wst = new WifiStateTracker(context, mHandler);
+ WifiService wifiService = new WifiService(context, wst);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
- mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
+ mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE, Phone.APN_TYPE_DEFAULT,
+ "MOBILE");
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_MMS] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE_MMS, Phone.APN_TYPE_MMS,
+ "MOBILE_MMS");
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_SUPL] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE_SUPL, Phone.APN_TYPE_SUPL,
+ "MOBILE_SUPL");
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE_DUN, Phone.APN_TYPE_DUN,
+ "MOBILE_DUN");
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE_HIPRI, Phone.APN_TYPE_HIPRI,
+ "MOBILE_HIPRI");
- mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
- mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
-
- mActiveNetwork = null;
mNumDnsEntries = 0;
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
@@ -148,16 +298,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
t.startMonitoring();
// Constructing this starts it too
- mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
+ mWifiWatchdogService = new WifiWatchdogService(context, wst);
}
/**
- * Sets the preferred network.
+ * Sets the preferred network.
* @param preference the new preference
*/
public synchronized void setNetworkPreference(int preference) {
enforceChangePermission();
- if (ConnectivityManager.isNetworkTypeValid(preference)) {
+ if (ConnectivityManager.isNetworkTypeValid(preference) &&
+ mNetAttributes[preference].isDefault()) {
if (mNetworkPreference != preference) {
persistNetworkPreference(preference);
mNetworkPreference = preference;
@@ -173,9 +324,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void persistNetworkPreference(int networkPreference) {
final ContentResolver cr = mContext.getContentResolver();
- Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference);
+ Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
+ networkPreference);
}
-
+
private int getPersistedNetworkPreference() {
final ContentResolver cr = mContext.getContentResolver();
@@ -187,31 +339,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
}
-
+
/**
- * Make the state of network connectivity conform to the preference settings.
+ * Make the state of network connectivity conform to the preference settings
* In this method, we only tear down a non-preferred network. Establishing
* a connection to the preferred network is taken care of when we handle
* the disconnect event from the non-preferred network
* (see {@link #handleDisconnect(NetworkInfo)}).
*/
private void enforcePreference() {
- if (mActiveNetwork == null)
+ if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected())
return;
- for (NetworkStateTracker t : mNetTrackers) {
- if (t == mActiveNetwork) {
- int netType = t.getNetworkInfo().getType();
- int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ?
- ConnectivityManager.TYPE_MOBILE :
- ConnectivityManager.TYPE_WIFI);
-
- if (t.getNetworkInfo().getType() != mNetworkPreference) {
- NetworkStateTracker otherTracker = mNetTrackers[otherNetType];
- if (otherTracker.isAvailable()) {
- teardown(t);
- }
+ if (!mNetTrackers[mNetworkPreference].isAvailable())
+ return;
+
+ for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
+ if (t != mNetworkPreference &&
+ mNetTrackers[t].getNetworkInfo().isConnected()) {
+ if (DBG) {
+ Log.d(TAG, "tearing down " +
+ mNetTrackers[t].getNetworkInfo() +
+ " in enforcePreference");
}
+ teardown(mNetTrackers[t]);
}
}
}
@@ -229,13 +380,21 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* Return NetworkInfo for the active (i.e., connected) network interface.
* It is assumed that at most one network is active at a time. If more
* than one is active, it is indeterminate which will be returned.
- * @return the info for the active network, or {@code null} if none is active
+ * @return the info for the active network, or {@code null} if none is
+ * active
*/
public NetworkInfo getActiveNetworkInfo() {
enforceAccessPermission();
- for (NetworkStateTracker t : mNetTrackers) {
+ for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
+ if (!mNetAttributes[type].isDefault()) {
+ continue;
+ }
+ NetworkStateTracker t = mNetTrackers[type];
NetworkInfo info = t.getNetworkInfo();
if (info.isConnected()) {
+ if (DBG && type != mActiveDefaultNetwork) Log.e(TAG,
+ "connected default network is not " +
+ "mActiveDefaultNetwork!");
return info;
}
}
@@ -280,36 +439,253 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return tracker != null && tracker.setRadio(turnOn);
}
- public int startUsingNetworkFeature(int networkType, String feature) {
+ /**
+ * Used to notice when the calling process dies so we can self-expire
+ *
+ * Also used to know if the process has cleaned up after itself when
+ * our auto-expire timer goes off. The timer has a link to an object.
+ *
+ */
+ private class FeatureUser implements IBinder.DeathRecipient {
+ int mNetworkType;
+ String mFeature;
+ IBinder mBinder;
+ int mPid;
+ int mUid;
+
+ FeatureUser(int type, String feature, IBinder binder) {
+ super();
+ mNetworkType = type;
+ mFeature = feature;
+ mBinder = binder;
+ mPid = getCallingPid();
+ mUid = getCallingUid();
+
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ binderDied();
+ }
+ }
+
+ void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
+
+ public void binderDied() {
+ Log.d(TAG, "ConnectivityService FeatureUser binderDied(" +
+ mNetworkType + ", " + mFeature + ", " + mBinder);
+ stopUsingNetworkFeature(this, false);
+ }
+
+ public void expire() {
+ Log.d(TAG, "ConnectivityService FeatureUser expire(" +
+ mNetworkType + ", " + mFeature + ", " + mBinder);
+ stopUsingNetworkFeature(this, false);
+ }
+ }
+
+ // javadoc from interface
+ public int startUsingNetworkFeature(int networkType, String feature,
+ IBinder binder) {
+ if (DBG) {
+ Log.d(TAG, "startUsingNetworkFeature for net " + networkType +
+ ": " + feature);
+ }
enforceChangePermission();
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
- return -1;
+ return Phone.APN_REQUEST_FAILED;
}
- NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+
+ FeatureUser f = new FeatureUser(networkType, feature, binder);
+
+ // TODO - move this into the MobileDataStateTracker
+ int usedNetworkType = networkType;
+ if(networkType == ConnectivityManager.TYPE_MOBILE) {
+ if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+ }
}
- return -1;
+ NetworkStateTracker network = mNetTrackers[usedNetworkType];
+ if (network != null) {
+ if (usedNetworkType != networkType) {
+ Integer currentPid = new Integer(getCallingPid());
+
+ NetworkStateTracker radio = mNetTrackers[networkType];
+ NetworkInfo ni = network.getNetworkInfo();
+
+ if (ni.isAvailable() == false) {
+ if (DBG) Log.d(TAG, "special network not available");
+ return Phone.APN_TYPE_NOT_AVAILABLE;
+ }
+
+ synchronized(this) {
+ mFeatureUsers.add(f);
+ if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
+ // this gets used for per-pid dns when connected
+ mNetRequestersPids[usedNetworkType].add(currentPid);
+ }
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+ f), getRestoreDefaultNetworkDelay());
+
+
+ if ((ni.isConnectedOrConnecting() == true) &&
+ !network.isTeardownRequested()) {
+ if (ni.isConnected() == true) {
+ // add the pid-specific dns
+ handleDnsConfigurationChange();
+ if (DBG) Log.d(TAG, "special network already active");
+ return Phone.APN_ALREADY_ACTIVE;
+ }
+ if (DBG) Log.d(TAG, "special network already connecting");
+ return Phone.APN_REQUEST_STARTED;
+ }
+
+ // check if the radio in play can make another contact
+ // assume if cannot for now
+
+ if (DBG) Log.d(TAG, "reconnecting to special network");
+ network.reconnect();
+ return Phone.APN_REQUEST_STARTED;
+ } else {
+ synchronized(this) {
+ mFeatureUsers.add(f);
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+ f), getRestoreDefaultNetworkDelay());
+
+ return network.startUsingNetworkFeature(feature,
+ getCallingPid(), getCallingUid());
+ }
+ }
+ return Phone.APN_TYPE_NOT_AVAILABLE;
}
+ // javadoc from interface
public int stopUsingNetworkFeature(int networkType, String feature) {
+ int pid = getCallingPid();
+ int uid = getCallingUid();
+
+ FeatureUser u = null;
+ boolean found = false;
+
+ synchronized(this) {
+ for (int i = 0; i < mFeatureUsers.size() ; i++) {
+ u = (FeatureUser)mFeatureUsers.get(i);
+ if (uid == u.mUid && pid == u.mPid &&
+ networkType == u.mNetworkType &&
+ TextUtils.equals(feature, u.mFeature)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (found && u != null) {
+ // stop regardless of how many other time this proc had called start
+ return stopUsingNetworkFeature(u, true);
+ } else {
+ // none found!
+ return 1;
+ }
+ }
+
+ private int stopUsingNetworkFeature(FeatureUser u, boolean ignoreDups) {
+ int networkType = u.mNetworkType;
+ String feature = u.mFeature;
+ int pid = u.mPid;
+ int uid = u.mUid;
+
+ NetworkStateTracker tracker = null;
+ boolean callTeardown = false; // used to carry our decision outside of sync block
+
+ if (DBG) {
+ Log.d(TAG, "stopUsingNetworkFeature for net " + networkType +
+ ": " + feature);
+ }
enforceChangePermission();
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
return -1;
}
- NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+
+ // need to link the mFeatureUsers list with the mNetRequestersPids state in this
+ // sync block
+ synchronized(this) {
+ // check if this process still has an outstanding start request
+ if (!mFeatureUsers.contains(u)) {
+ return 1;
+ }
+ u.unlinkDeathRecipient();
+ mFeatureUsers.remove(mFeatureUsers.indexOf(u));
+ // If we care about duplicate requests, check for that here.
+ //
+ // This is done to support the extension of a request - the app
+ // can request we start the network feature again and renew the
+ // auto-shutoff delay. Normal "stop" calls from the app though
+ // do not pay attention to duplicate requests - in effect the
+ // API does not refcount and a single stop will counter multiple starts.
+ if (ignoreDups == false) {
+ for (int i = 0; i < mFeatureUsers.size() ; i++) {
+ FeatureUser x = (FeatureUser)mFeatureUsers.get(i);
+ if (x.mUid == u.mUid && x.mPid == u.mPid &&
+ x.mNetworkType == u.mNetworkType &&
+ TextUtils.equals(x.mFeature, u.mFeature)) {
+ return 1;
+ }
+ }
+ }
+
+ // TODO - move to MobileDataStateTracker
+ int usedNetworkType = networkType;
+ if (networkType == ConnectivityManager.TYPE_MOBILE) {
+ if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+ }
+ }
+ tracker = mNetTrackers[usedNetworkType];
+ if(usedNetworkType != networkType) {
+ Integer currentPid = new Integer(pid);
+ reassessPidDns(pid, true);
+ mNetRequestersPids[usedNetworkType].remove(currentPid);
+ if (mNetRequestersPids[usedNetworkType].size() != 0) {
+ if (DBG) Log.d(TAG, "not tearing down special network - " +
+ "others still using it");
+ return 1;
+ }
+ callTeardown = true;
+ }
+ }
+
+ if (callTeardown) {
+ tracker.teardown();
+ return 1;
+ } else {
+ // do it the old fashioned way
+ return tracker.stopUsingNetworkFeature(feature, pid, uid);
}
- return -1;
}
/**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
- * @param networkType the type of the network over which traffic to the specified
- * host is to be routed
- * @param hostAddress the IP address of the host to which the route is desired
+ * @param networkType the type of the network over which traffic to the
+ * specified host is to be routed
+ * @param hostAddress the IP address of the host to which the route is
+ * desired
* @return {@code true} on success, {@code false} on failure
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
@@ -318,19 +694,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return false;
}
NetworkStateTracker tracker = mNetTrackers[networkType];
- /*
- * If there's only one connected network, and it's the one requested,
- * then we don't have to do anything - the requested route already
- * exists. If it's not the requested network, then it's not possible
- * to establish the requested route. Finally, if there is more than
- * one connected network, then we must insert an entry in the routing
- * table.
- */
- if (getNumConnectedNetworks() > 1) {
- return tracker.requestRouteToHost(hostAddress);
- } else {
- return tracker.getNetworkInfo().getType() == networkType;
+
+ if (!tracker.getNetworkInfo().isConnected() || tracker.isTeardownRequested()) {
+ if (DBG) {
+ Log.d(TAG, "requestRouteToHost on down network (" + networkType + " - dropped");
+ }
+ return false;
}
+ return tracker.requestRouteToHost(hostAddress);
}
/**
@@ -340,7 +711,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.BACKGROUND_DATA, 1) == 1;
}
-
+
/**
* @see ConnectivityManager#setBackgroundDataSetting(boolean)
*/
@@ -348,22 +719,24 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
"ConnectivityService");
-
+
if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKGROUND_DATA, allowBackgroundDataUsage ? 1 : 0);
-
+ Settings.Secure.BACKGROUND_DATA,
+ allowBackgroundDataUsage ? 1 : 0);
+
Intent broadcast = new Intent(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
mContext.sendBroadcast(broadcast);
- }
-
+ }
+
private int getNumConnectedNetworks() {
int numConnectedNets = 0;
for (NetworkStateTracker nt : mNetTrackers) {
- if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
+ if (nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
++numConnectedNets;
}
}
@@ -371,64 +744,46 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
private void enforceAccessPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
- "ConnectivityService");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE,
+ "ConnectivityService");
}
private void enforceChangePermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
- "ConnectivityService");
-
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE,
+ "ConnectivityService");
}
/**
- * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
- * we ignore it. If it is for the active network, we send out a broadcast.
- * But first, we check whether it might be possible to connect to a different
- * network.
+ * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
+ * network, we ignore it. If it is for the active network, we send out a
+ * broadcast. But first, we check whether it might be possible to connect
+ * to a different network.
* @param info the {@code NetworkInfo} for the network
*/
private void handleDisconnect(NetworkInfo info) {
- if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
+ int prevNetType = info.getType();
- mNetTrackers[info.getType()].setTeardownRequested(false);
+ mNetTrackers[prevNetType].setTeardownRequested(false);
/*
* If the disconnected network is not the active one, then don't report
* this as a loss of connectivity. What probably happened is that we're
* getting the disconnect for a network that we explicitly disabled
* in accordance with network preference policies.
*/
- if (mActiveNetwork == null || info.getType() != mActiveNetwork.getNetworkInfo().getType())
- return;
-
- NetworkStateTracker newNet;
- if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
- newNet = mWifiStateTracker;
- } else /* info().getType() == TYPE_WIFI */ {
- newNet = mMobileDataStateTracker;
- }
-
- /**
- * See if the other network is available to fail over to.
- * If is not available, we enable it anyway, so that it
- * will be able to connect when it does become available,
- * but we report a total loss of connectivity rather than
- * report that we are attempting to fail over.
- */
- NetworkInfo switchTo = null;
- if (newNet.isAvailable()) {
- mActiveNetwork = newNet;
- switchTo = newNet.getNetworkInfo();
- switchTo.setFailover(true);
- if (!switchTo.isConnectedOrConnecting()) {
- newNet.reconnect();
+ if (!mNetAttributes[prevNetType].isDefault()) {
+ List pids = mNetRequestersPids[prevNetType];
+ for (int i = 0; i<pids.size(); i++) {
+ Integer pid = (Integer)pids.get(i);
+ // will remove them because the net's no longer connected
+ // need to do this now as only now do we know the pids and
+ // can properly null things that are no longer referenced.
+ reassessPidDns(pid.intValue(), false);
}
- } else {
- newNet.reconnect();
}
- boolean otherNetworkConnected = false;
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (info.isFailover()) {
@@ -439,31 +794,92 @@ public class ConnectivityService extends IConnectivityManager.Stub {
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
}
if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
+ info.getExtraInfo());
}
- if (switchTo != null) {
- otherNetworkConnected = switchTo.isConnected();
- if (DBG) {
- if (otherNetworkConnected) {
- Log.v(TAG, "Switching to already connected " + switchTo.getTypeName());
+
+ /*
+ * If this is a default network, check if other defaults are available
+ * or active
+ */
+ NetworkStateTracker newNet = null;
+ if (mNetAttributes[prevNetType].isDefault()) {
+ if (mActiveDefaultNetwork == prevNetType) {
+ mActiveDefaultNetwork = -1;
+ }
+
+ int newType = -1;
+ int newPriority = -1;
+ for (int checkType=0; checkType <=
+ ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
+ if (checkType == prevNetType) {
+ continue;
+ }
+ if (mNetAttributes[checkType].isDefault()) {
+ /* TODO - if we have multiple nets we could use
+ * we may want to put more thought into which we choose
+ */
+ if (checkType == mNetworkPreference) {
+ newType = checkType;
+ break;
+ }
+ if (mRadioAttributes[mNetAttributes[checkType].mRadio].
+ mPriority > newPriority) {
+ newType = checkType;
+ newPriority = mRadioAttributes[mNetAttributes[newType].
+ mRadio].mPriority;
+ }
+ }
+ }
+
+ if (newType != -1) {
+ newNet = mNetTrackers[newType];
+ /**
+ * See if the other network is available to fail over to.
+ * If is not available, we enable it anyway, so that it
+ * will be able to connect when it does become available,
+ * but we report a total loss of connectivity rather than
+ * report that we are attempting to fail over.
+ */
+ if (newNet.isAvailable()) {
+ NetworkInfo switchTo = newNet.getNetworkInfo();
+ switchTo.setFailover(true);
+ if (!switchTo.isConnectedOrConnecting() ||
+ newNet.isTeardownRequested()) {
+ newNet.reconnect();
+ }
+ if (DBG) {
+ if (switchTo.isConnected()) {
+ Log.v(TAG, "Switching to already connected " +
+ switchTo.getTypeName());
+ } else {
+ Log.v(TAG, "Attempting to switch to " +
+ switchTo.getTypeName());
+ }
+ }
+ intent.putExtra(ConnectivityManager.
+ EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
- Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName());
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
+ true);
+ newNet.reconnect();
}
+ } else {
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
+ true);
}
- intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
- } else {
- intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
- if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
- (switchTo == null ? "" : " other=" + switchTo.getTypeName()));
- mContext.sendStickyBroadcast(intent);
+ // do this before we broadcast the change
+ handleConnectivityChange();
+
+ sendStickyBroadcast(intent);
/*
- * If the failover network is already connected, then immediately send out
- * a followup broadcast indicating successful failover
+ * If the failover network is already connected, then immediately send
+ * out a followup broadcast indicating successful failover
*/
- if (switchTo != null && otherNetworkConnected)
- sendConnectedBroadcast(switchTo);
+ if (newNet != null && newNet.getNetworkInfo().isConnected())
+ sendConnectedBroadcast(newNet.getNetworkInfo());
}
private void sendConnectedBroadcast(NetworkInfo info) {
@@ -477,9 +893,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
}
if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
+ info.getExtraInfo());
}
- mContext.sendStickyBroadcast(intent);
+ sendStickyBroadcast(intent);
}
/**
@@ -488,106 +905,124 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private void handleConnectionFailure(NetworkInfo info) {
mNetTrackers[info.getType()].setTeardownRequested(false);
- if (getActiveNetworkInfo() == null) {
- String reason = info.getReason();
- String extraInfo = info.getExtraInfo();
- if (DBG) {
- String reasonText;
- if (reason == null) {
- reasonText = ".";
- } else {
- reasonText = " (" + reason + ").";
- }
- Log.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
+ String reason = info.getReason();
+ String extraInfo = info.getExtraInfo();
+
+ if (DBG) {
+ String reasonText;
+ if (reason == null) {
+ reasonText = ".";
+ } else {
+ reasonText = " (" + reason + ").";
}
-
- Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ Log.v(TAG, "Attempt to connect to " + info.getTypeName() +
+ " failed" + reasonText);
+ }
+
+ Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
- if (reason != null) {
- intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
- }
- if (extraInfo != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
+ }
+ if (reason != null) {
+ intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
+ }
+ if (extraInfo != null) {
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
+ }
+ if (info.isFailover()) {
+ intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+ info.setFailover(false);
+ }
+ sendStickyBroadcast(intent);
+ }
+
+ private void sendStickyBroadcast(Intent intent) {
+ synchronized(this) {
+ if (mSystemReady) {
+ mContext.sendStickyBroadcast(intent);
+ } else {
+ if (mDeferredBroadcasts == null) {
+ mDeferredBroadcasts = new ArrayList<Intent>();
+ }
+ mDeferredBroadcasts.add(intent);
}
- if (info.isFailover()) {
- intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
- info.setFailover(false);
+ }
+ }
+
+ void systemReady() {
+ synchronized(this) {
+ mSystemReady = true;
+ if (mDeferredBroadcasts != null) {
+ int count = mDeferredBroadcasts.size();
+ for (int i = 0; i < count; i++) {
+ mContext.sendStickyBroadcast(mDeferredBroadcasts.get(i));
+ }
+ mDeferredBroadcasts = null;
}
- mContext.sendStickyBroadcast(intent);
}
}
private void handleConnect(NetworkInfo info) {
- if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName());
+ int type = info.getType();
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
- NetworkStateTracker thisNet = mNetTrackers[info.getType()];
- NetworkStateTracker deadnet = null;
- NetworkStateTracker otherNet;
- if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
- otherNet = mWifiStateTracker;
- } else /* info().getType() == TYPE_WIFI */ {
- otherNet = mMobileDataStateTracker;
- }
- /*
- * Check policy to see whether we are connected to a non-preferred
- * network that now needs to be torn down.
- */
- NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
- NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
- if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
- if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
- deadnet = mMobileDataStateTracker;
- else
- deadnet = mWifiStateTracker;
- }
-
- boolean toredown = false;
- thisNet.setTeardownRequested(false);
- if (!mTestMode && deadnet != null) {
- if (DBG) Log.v(TAG, "Policy requires " +
- deadnet.getNetworkInfo().getTypeName() + " teardown");
- toredown = teardown(deadnet);
- if (DBG && !toredown) {
- Log.d(TAG, "Network declined teardown request");
+ NetworkStateTracker thisNet = mNetTrackers[type];
+
+ // if this is a default net and other default is running
+ // kill the one not preferred
+ if (mNetAttributes[type].isDefault()) {
+ if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
+ if ((type != mNetworkPreference &&
+ mNetAttributes[mActiveDefaultNetwork].mPriority >
+ mNetAttributes[type].mPriority) ||
+ mNetworkPreference == mActiveDefaultNetwork) {
+ // don't accept this one
+ if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION " +
+ "to torn down network " + info.getTypeName());
+ teardown(thisNet);
+ return;
+ } else {
+ // tear down the other
+ NetworkStateTracker otherNet =
+ mNetTrackers[mActiveDefaultNetwork];
+ if (DBG) Log.v(TAG, "Policy requires " +
+ otherNet.getNetworkInfo().getTypeName() +
+ " teardown");
+ if (!teardown(otherNet)) {
+ Log.e(TAG, "Network declined teardown request");
+ return;
+ }
+ if (isFailover) {
+ otherNet.releaseWakeLock();
+ }
+ }
}
+ mActiveDefaultNetwork = type;
}
-
- /*
- * Note that if toredown is true, deadnet cannot be null, so there is
- * no danger of a null pointer exception here..
- */
- if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
- mActiveNetwork = thisNet;
- if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName());
- thisNet.updateNetworkSettings();
- sendConnectedBroadcast(info);
- if (isFailover) {
- otherNet.releaseWakeLock();
- }
- } else {
- if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " +
- info.getTypeName());
- }
+ thisNet.setTeardownRequested(false);
+ thisNet.updateNetworkSettings();
+ handleConnectivityChange();
+ sendConnectedBroadcast(info);
}
private void handleScanResultsAvailable(NetworkInfo info) {
int networkType = info.getType();
if (networkType != ConnectivityManager.TYPE_WIFI) {
- if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
- + " Don't know how to handle.");
+ if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " +
+ info.getTypeName() + " network. Don't know how to handle.");
}
-
+
mNetTrackers[networkType].interpretScanResultsAvailable();
}
- private void handleNotificationChange(boolean visible, int id, Notification notification) {
+ private void handleNotificationChange(boolean visible, int id,
+ Notification notification) {
NotificationManager notificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
-
+
if (visible) {
notificationManager.notify(id, notification);
} else {
@@ -605,79 +1040,173 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private void handleConnectivityChange() {
/*
+ * 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.
*/
- int numConnectedNets = handleConfigurationChange();
- if (numConnectedNets > 1) {
- mMobileDataStateTracker.addPrivateRoutes();
- mMobileDataStateTracker.removeDefaultRoute();
- } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) {
- mMobileDataStateTracker.removePrivateRoutes();
- mMobileDataStateTracker.restoreDefaultRoute();
+ handleDnsConfigurationChange();
+
+ for (int netType : mPriorityList) {
+ if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
+ if (mNetAttributes[netType].isDefault()) {
+ mNetTrackers[netType].addDefaultRoute();
+ } else {
+ mNetTrackers[netType].addPrivateDnsRoutes();
+ }
+ } else {
+ if (mNetAttributes[netType].isDefault()) {
+ mNetTrackers[netType].removeDefaultRoute();
+ } else {
+ mNetTrackers[netType].removePrivateDnsRoutes();
+ }
+ }
+ }
+ }
+
+ /**
+ * Adjust the per-process dns entries (net.dns<x>.<pid>) based
+ * on the highest priority active net which this process requested.
+ * If there aren't any, clear it out
+ */
+ private void reassessPidDns(int myPid, boolean doBump)
+ {
+ if (DBG) Log.d(TAG, "reassessPidDns for pid " + myPid);
+ for(int i : mPriorityList) {
+ if (mNetAttributes[i].isDefault()) {
+ continue;
+ }
+ NetworkStateTracker nt = mNetTrackers[i];
+ if (nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
+ List pids = mNetRequestersPids[i];
+ for (int j=0; j<pids.size(); j++) {
+ Integer pid = (Integer)pids.get(j);
+ if (pid.intValue() == myPid) {
+ String[] dnsList = nt.getNameServers();
+ writePidDns(dnsList, myPid);
+ if (doBump) {
+ bumpDns();
+ }
+ return;
+ }
+ }
+ }
+ }
+ // nothing found - delete
+ for (int i = 1; ; i++) {
+ String prop = "net.dns" + i + "." + myPid;
+ if (SystemProperties.get(prop).length() == 0) {
+ if (doBump) {
+ bumpDns();
+ }
+ return;
+ }
+ SystemProperties.set(prop, "");
}
}
- private int handleConfigurationChange() {
+ private void writePidDns(String[] dnsList, int pid) {
+ int j = 1;
+ for (String dns : dnsList) {
+ if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+ SystemProperties.set("net.dns" + j++ + "." + pid, dns);
+ }
+ }
+ }
+
+ private void bumpDns() {
/*
- * Set DNS properties. Always put Wi-Fi entries at the front of
- * the list if it is active.
+ * Bump the property that tells the name resolver library to reread
+ * the DNS server list from the properties.
*/
- int index = 1;
- String lastDns = "";
- int numConnectedNets = 0;
- int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
- int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
+ String propVal = SystemProperties.get("net.dnschange");
+ int n = 0;
+ if (propVal.length() != 0) {
+ try {
+ n = Integer.parseInt(propVal);
+ } catch (NumberFormatException e) {}
+ }
+ SystemProperties.set("net.dnschange", "" + (n+1));
+ }
- for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) {
+ private void handleDnsConfigurationChange() {
+ // 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.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
- ++numConnectedNets;
+ if (nt != null && nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
String[] dnsList = nt.getNameServers();
- for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
- // skip duplicate entries
- if (!dnsList[i].equals(lastDns)) {
- SystemProperties.set("net.dns" + index++, dnsList[i]);
- lastDns = dnsList[i];
+ if (mNetAttributes[netType].isDefault()) {
+ int j = 1;
+ for (String dns : dnsList) {
+ if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+ if (DBG) {
+ Log.d(TAG, "adding dns " + dns + " for " +
+ nt.getNetworkInfo().getTypeName());
+ }
+ SystemProperties.set("net.dns" + j++, dns);
+ }
+ }
+ for (int k=j ; k<mNumDnsEntries; k++) {
+ if (DBG) Log.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());
}
}
}
}
- // Null out any DNS properties that are no longer used
- for (int i = index; i <= mNumDnsEntries; i++) {
- SystemProperties.set("net.dns" + i, "");
+
+ bumpDns();
+ }
+
+ private int getRestoreDefaultNetworkDelay() {
+ String restoreDefaultNetworkDelayStr = SystemProperties.get(
+ NETWORK_RESTORE_DELAY_PROP_NAME);
+ if(restoreDefaultNetworkDelayStr != null &&
+ restoreDefaultNetworkDelayStr.length() != 0) {
+ try {
+ return Integer.valueOf(restoreDefaultNetworkDelayStr);
+ } catch (NumberFormatException e) {
+ }
}
- mNumDnsEntries = index - 1;
- // Notify the name resolver library of the change
- SystemProperties.set("net.dnschange", String.valueOf(sDnsChangeCounter++));
- return numConnectedNets;
+ return RESTORE_DEFAULT_NETWORK_DELAY;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump ConnectivityService from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
+ pw.println("Permission Denial: can't dump ConnectivityService " +
+ "from from pid=" + Binder.getCallingPid() + ", uid=" +
+ Binder.getCallingUid());
return;
}
- if (mActiveNetwork == null) {
- pw.println("No active network");
- } else {
- pw.println("Active network: " + mActiveNetwork.getNetworkInfo().getTypeName());
- }
pw.println();
for (NetworkStateTracker nst : mNetTrackers) {
+ if (nst.getNetworkInfo().isConnected()) {
+ pw.println("Active network: " + nst.getNetworkInfo().
+ getTypeName());
+ }
pw.println(nst.getNetworkInfo());
pw.println(nst);
pw.println();
}
}
+ // must be stateless - things change under us.
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
@@ -685,46 +1214,54 @@ public class ConnectivityService extends IConnectivityManager.Stub {
switch (msg.what) {
case NetworkStateTracker.EVENT_STATE_CHANGED:
info = (NetworkInfo) msg.obj;
- if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
+ if (DBG) Log.d(TAG, "ConnectivityChange for " +
+ info.getTypeName() + ": " +
info.getState() + "/" + info.getDetailedState());
// Connectivity state changed:
// [31-13] Reserved for future use
- // [12-9] Network subtype (for mobile network, as defined by TelephonyManager)
- // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
+ // [12-9] Network subtype (for mobile network, as defined
+ // by TelephonyManager)
+ // [8-3] Detailed state ordinal (as defined by
+ // NetworkInfo.DetailedState)
// [2-0] Network type (as defined by ConnectivityManager)
int eventLogParam = (info.getType() & 0x7) |
((info.getDetailedState().ordinal() & 0x3f) << 3) |
(info.getSubtype() << 9);
- EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
-
- if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
+ EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED,
+ eventLogParam);
+
+ if (info.getDetailedState() ==
+ NetworkInfo.DetailedState.FAILED) {
handleConnectionFailure(info);
- } else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
+ } else if (info.getState() ==
+ NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} else if (info.getState() == NetworkInfo.State.SUSPENDED) {
// TODO: need to think this over.
- // the logic here is, handle SUSPENDED the same as DISCONNECTED. The
- // only difference being we are broadcasting an intent with NetworkInfo
- // that's suspended. This allows the applications an opportunity to
- // handle DISCONNECTED and SUSPENDED differently, or not.
+ // the logic here is, handle SUSPENDED the same as
+ // DISCONNECTED. The only difference being we are
+ // broadcasting an intent with NetworkInfo that's
+ // suspended. This allows the applications an
+ // opportunity to handle DISCONNECTED and SUSPENDED
+ // differently, or not.
handleDisconnect(info);
} else if (info.getState() == NetworkInfo.State.CONNECTED) {
handleConnect(info);
}
- handleConnectivityChange();
break;
case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
info = (NetworkInfo) msg.obj;
handleScanResultsAvailable(info);
break;
-
+
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
- handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
+ handleNotificationChange(msg.arg1 == 1, msg.arg2,
+ (Notification) msg.obj);
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
- handleConfigurationChange();
+ handleDnsConfigurationChange();
break;
case NetworkStateTracker.EVENT_ROAMING_CHANGED:
@@ -734,6 +1271,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
// fill me in
break;
+ case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
+ FeatureUser u = (FeatureUser)msg.obj;
+ u.expire();
+ break;
}
}
}
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 52e09ca..57af029 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -43,8 +43,8 @@ import android.provider.Settings;
/**
* This class implements a service to monitor the amount of disk storage space
* on the device. If the free storage on device is less than a tunable threshold value
- * (default is 10%. this value is a gservices parameter) a low memory notification is
- * displayed to alert the user. If the user clicks on the low memory notification the
+ * (default is 10%. this value is a gservices parameter) a low memory notification is
+ * displayed to alert the user. If the user clicks on the low memory notification the
* Application Manager application gets launched to let the user free storage space.
* Event log events:
* A low memory event with the free storage on device in bytes is logged to the event log
@@ -68,32 +68,35 @@ class DeviceStorageMonitorService extends Binder {
private static final int EVENT_LOG_FREE_STORAGE_LEFT = 2746;
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 long mFreeMem;
+ private long mFreeMem; // on /data
private long mLastReportedFreeMem;
private long mLastReportedFreeMemTime;
private boolean mLowMemFlag=false;
private Context mContext;
private ContentResolver mContentResolver;
- long mBlkSize;
- long mTotalMemory;
- StatFs mFileStats;
- private static final String DATA_PATH="/data";
- long mThreadStartTime = -1;
- boolean mClearSucceeded = false;
- boolean mClearingCache;
+ private long mTotalMemory; // on /data
+ private StatFs mDataFileStats;
+ private StatFs mSystemFileStats;
+ private StatFs mCacheFileStats;
+ private static final String DATA_PATH = "/data";
+ private static final String SYSTEM_PATH = "/system";
+ private static final String CACHE_PATH = "/cache";
+ private long mThreadStartTime = -1;
+ private boolean mClearSucceeded = false;
+ private boolean mClearingCache;
private Intent mStorageLowIntent;
private Intent mStorageOkIntent;
private CachePackageDataObserver mClearCacheObserver;
private static final int _TRUE = 1;
private static final int _FALSE = 0;
-
+
/**
* This string is used for ServiceManager access to this class.
*/
static final String SERVICE = "devicestoragemonitor";
-
+
/**
- * Handler that checks the amount of disk space on the device and sends a
+ * Handler that checks the amount of disk space on the device and sends a
* notification if the device runs low on disk space
*/
Handler mHandler = new Handler() {
@@ -107,7 +110,7 @@ class DeviceStorageMonitorService extends Binder {
checkMemory(msg.arg1 == _TRUE);
}
};
-
+
class CachePackageDataObserver extends IPackageDataObserver.Stub {
public void onRemoveCompleted(String packageName, boolean succeeded) {
mClearSucceeded = succeeded;
@@ -115,12 +118,17 @@ class DeviceStorageMonitorService extends Binder {
if(localLOGV) Log.i(TAG, " Clear succeeded:"+mClearSucceeded
+", mClearingCache:"+mClearingCache+" Forcing memory check");
postCheckMemoryMsg(false, 0);
- }
+ }
}
-
+
private final void restatDataDir() {
- mFileStats.restat(DATA_PATH);
- mFreeMem = mFileStats.getAvailableBlocks()*mBlkSize;
+ try {
+ mDataFileStats.restat(DATA_PATH);
+ mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
+ mDataFileStats.getBlockSize();
+ } catch (IllegalArgumentException e) {
+ // use the old value of mFreeMem
+ }
// Allow freemem to be overridden by debug.freemem for testing
String debugFreeMem = SystemProperties.get("debug.freemem");
if (!"".equals(debugFreeMem)) {
@@ -132,10 +140,27 @@ class DeviceStorageMonitorService extends Binder {
DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
//log the amount of free memory in event log
long currTime = SystemClock.elapsedRealtime();
- if((mLastReportedFreeMemTime == 0) ||
- (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
+ if((mLastReportedFreeMemTime == 0) ||
+ (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
mLastReportedFreeMemTime = currTime;
- EventLog.writeEvent(EVENT_LOG_FREE_STORAGE_LEFT, mFreeMem);
+ long mFreeSystem = -1, mFreeCache = -1;
+ try {
+ mSystemFileStats.restat(SYSTEM_PATH);
+ mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
+ mSystemFileStats.getBlockSize();
+ } catch (IllegalArgumentException e) {
+ // ignore; report -1
+ }
+ try {
+ mCacheFileStats.restat(CACHE_PATH);
+ mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
+ mCacheFileStats.getBlockSize();
+ } catch (IllegalArgumentException e) {
+ // ignore; report -1
+ }
+ mCacheFileStats.restat(CACHE_PATH);
+ EventLog.writeEvent(EVENT_LOG_FREE_STORAGE_LEFT,
+ mFreeMem, mFreeSystem, mFreeCache);
}
// Read the reporting threshold from Gservices
long threshold = Gservices.getLong(mContentResolver,
@@ -148,7 +173,7 @@ class DeviceStorageMonitorService extends Binder {
EventLog.writeEvent(EVENT_LOG_STORAGE_BELOW_THRESHOLD, mFreeMem);
}
}
-
+
private final void clearCache() {
if (mClearCacheObserver == null) {
// Lazy instantiation
@@ -165,10 +190,10 @@ class DeviceStorageMonitorService extends Binder {
mClearSucceeded = false;
}
}
-
+
private final void checkMemory(boolean checkCache) {
- //if the thread that was started to clear cache is still running do nothing till its
- //finished clearing cache. Ideally this flag could be modified by clearCache
+ //if the thread that was started to clear cache is still running do nothing till its
+ //finished clearing cache. Ideally this flag could be modified by clearCache
// and should be accessed via a lock but even if it does this test will fail now and
//hopefully the next time this flag will be set to the correct value.
if(mClearingCache) {
@@ -177,11 +202,11 @@ class DeviceStorageMonitorService extends Binder {
long diffTime = System.currentTimeMillis() - mThreadStartTime;
if(diffTime > (10*60*1000)) {
Log.w(TAG, "Thread that clears cache file seems to run for ever");
- }
+ }
} else {
restatDataDir();
if (localLOGV) Log.v(TAG, "freeMemory="+mFreeMem);
-
+
//post intent to NotificationManager to display icon if necessary
long memThreshold = getMemThreshold();
if (mFreeMem < memThreshold) {
@@ -214,7 +239,7 @@ class DeviceStorageMonitorService extends Binder {
//keep posting messages to itself periodically
postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
}
-
+
private void postCheckMemoryMsg(boolean clearCache, long delay) {
// Remove queued messages
mHandler.removeMessages(DEVICE_MEMORY_WHAT);
@@ -222,16 +247,16 @@ class DeviceStorageMonitorService extends Binder {
clearCache ?_TRUE : _FALSE, 0),
delay);
}
-
+
/*
- * just query settings to retrieve the memory threshold.
+ * just query settings to retrieve the memory threshold.
* Preferred this over using a ContentObserver since Settings.Gservices caches the value
* any way
*/
private long getMemThreshold() {
int value = Settings.Gservices.getInt(
- mContentResolver,
- Settings.Gservices.SYS_STORAGE_THRESHOLD_PERCENTAGE,
+ mContentResolver,
+ Settings.Gservices.SYS_STORAGE_THRESHOLD_PERCENTAGE,
DEFAULT_THRESHOLD_PERCENTAGE);
if(localLOGV) Log.v(TAG, "Threshold Percentage="+value);
//evaluate threshold value
@@ -247,16 +272,17 @@ class DeviceStorageMonitorService extends Binder {
mContext = context;
mContentResolver = mContext.getContentResolver();
//create StatFs object
- mFileStats = new StatFs(DATA_PATH);
- //initialize block size
- mBlkSize = mFileStats.getBlockSize();
+ mDataFileStats = new StatFs(DATA_PATH);
+ mSystemFileStats = new StatFs(SYSTEM_PATH);
+ mCacheFileStats = new StatFs(CACHE_PATH);
//initialize total storage on device
- mTotalMemory = ((long)mFileStats.getBlockCount()*mBlkSize)/100L;
+ mTotalMemory = ((long)mDataFileStats.getBlockCount() *
+ mDataFileStats.getBlockSize())/100L;
mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
checkMemory(true);
}
-
+
/**
* This method sends a notification to NotificationManager to display
@@ -271,7 +297,7 @@ class DeviceStorageMonitorService extends Binder {
Intent lowMemIntent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
lowMemIntent.putExtra("memory", mFreeMem);
lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- NotificationManager mNotificationMgr =
+ NotificationManager mNotificationMgr =
(NotificationManager)mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
CharSequence title = mContext.getText(
@@ -302,7 +328,7 @@ class DeviceStorageMonitorService extends Binder {
mContext.removeStickyBroadcast(mStorageLowIntent);
mContext.sendBroadcast(mStorageOkIntent);
}
-
+
public void updateMemory() {
int callingUid = getCallingUid();
if(callingUid != Process.SYSTEM_UID) {
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
new file mode 100644
index 0000000..f089de1
--- /dev/null
+++ b/services/java/com/android/server/DockObserver.java
@@ -0,0 +1,184 @@
+/*
+ * 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.app.Activity;
+import android.app.KeyguardManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UEventObserver;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+
+/**
+ * <p>DockObserver monitors for a docking station.
+ */
+class DockObserver extends UEventObserver {
+ private static final String TAG = DockObserver.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
+ private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
+
+ private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ private boolean mSystemReady;
+
+ private final Context mContext;
+
+ private PowerManagerService mPowerManager;
+
+ private KeyguardManager.KeyguardLock mKeyguardLock;
+ private boolean mKeyguardDisabled;
+ private LockPatternUtils mLockPatternUtils;
+
+ // The broadcast receiver which receives the result of the ordered broadcast sent when
+ // the dock state changes. The original ordered broadcast is sent with an initial result
+ // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
+ // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
+ private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() != Activity.RESULT_OK) {
+ return;
+ }
+
+ // Launch a dock activity
+ String category;
+ switch (mDockState) {
+ case Intent.EXTRA_DOCK_STATE_CAR:
+ category = Intent.CATEGORY_CAR_DOCK;
+ break;
+ case Intent.EXTRA_DOCK_STATE_DESK:
+ category = Intent.CATEGORY_DESK_DOCK;
+ break;
+ default:
+ category = null;
+ break;
+ }
+ if (category != null) {
+ intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(category);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, e.getCause());
+ }
+ }
+ }
+ };
+
+ public DockObserver(Context context, PowerManagerService pm) {
+ mContext = context;
+ mPowerManager = pm;
+ mLockPatternUtils = new LockPatternUtils(context.getContentResolver());
+ init(); // set initial status
+ startObserving(DOCK_UEVENT_MATCH);
+ }
+
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Dock UEVENT: " + event.toString());
+ }
+
+ synchronized (this) {
+ try {
+ int newState = Integer.parseInt(event.get("SWITCH_STATE"));
+ if (newState != mDockState) {
+ mDockState = newState;
+ if (mSystemReady) {
+ update();
+ }
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Could not parse switch state from event " + event);
+ }
+ }
+ }
+
+ private final void init() {
+ char[] buffer = new char[1024];
+
+ try {
+ FileReader file = new FileReader(DOCK_STATE_PATH);
+ int len = file.read(buffer, 0, 1024);
+ mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "This kernel does not have dock station support");
+ } catch (Exception e) {
+ Log.e(TAG, "" , e);
+ }
+ }
+
+ void systemReady() {
+ synchronized (this) {
+ KeyguardManager keyguardManager =
+ (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mKeyguardLock = keyguardManager.newKeyguardLock(TAG);
+
+ // don't bother broadcasting undocked here
+ if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ update();
+ }
+ mSystemReady = true;
+ }
+ }
+
+ private final void update() {
+ mHandler.sendEmptyMessage(0);
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (this) {
+ Log.i(TAG, "Dock state changed: " + mDockState);
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+ Log.i(TAG, "Device not provisioned, skipping dock broadcast");
+ return;
+ }
+ // Pack up the values and broadcast them to everyone
+ mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), false, true);
+ Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
+ intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
+
+ // Send the ordered broadcast; the result receiver will receive after all
+ // broadcasts have been sent. If any broadcast receiver changes the result
+ // code from the initial value of RESULT_OK, then the result receiver will
+ // not launch the corresponding dock application. This gives apps a chance
+ // to override the behavior and stay in their app even when the device is
+ // placed into a dock.
+ mContext.sendStickyOrderedBroadcast(
+ intent, mResultReceiver, null, Activity.RESULT_OK, null, null);
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java
index 5bc9b5f..88074c2 100755
--- a/services/java/com/android/server/HardwareService.java
+++ b/services/java/com/android/server/HardwareService.java
@@ -37,6 +37,9 @@ import android.os.Binder;
import android.os.SystemClock;
import android.util.Log;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
public class HardwareService extends IHardwareService.Stub {
private static final String TAG = "HardwareService";
@@ -49,10 +52,74 @@ public class HardwareService extends IHardwareService.Stub {
static final int LIGHT_FLASH_NONE = 0;
static final int LIGHT_FLASH_TIMED = 1;
+ static final int LIGHT_FLASH_HARDWARE = 2;
+
+ /**
+ * Light brightness is managed by a user setting.
+ */
+ static final int BRIGHTNESS_MODE_USER = 0;
+
+ /**
+ * Light brightness is managed by a light sensor.
+ */
+ static final int BRIGHTNESS_MODE_SENSOR = 1;
+
+ private final LinkedList<Vibration> mVibrations;
+ private Vibration mCurrentVibration;
private boolean mAttentionLightOn;
private boolean mPulsing;
+ private class Vibration implements IBinder.DeathRecipient {
+ private final IBinder mToken;
+ private final long mTimeout;
+ private final long mStartTime;
+ private final long[] mPattern;
+ private final int mRepeat;
+
+ Vibration(IBinder token, long millis) {
+ this(token, millis, null, 0);
+ }
+
+ Vibration(IBinder token, long[] pattern, int repeat) {
+ this(token, 0, pattern, repeat);
+ }
+
+ private Vibration(IBinder token, long millis, long[] pattern,
+ int repeat) {
+ mToken = token;
+ mTimeout = millis;
+ mStartTime = SystemClock.uptimeMillis();
+ mPattern = pattern;
+ mRepeat = repeat;
+ }
+
+ public void binderDied() {
+ synchronized (mVibrations) {
+ mVibrations.remove(this);
+ if (this == mCurrentVibration) {
+ doCancelVibrateLocked();
+ startNextVibrationLocked();
+ }
+ }
+ }
+
+ public boolean hasLongerTimeout(long millis) {
+ if (mTimeout == 0) {
+ // This is a pattern, return false to play the simple
+ // vibration.
+ return false;
+ }
+ if ((mStartTime + mTimeout)
+ < (SystemClock.uptimeMillis() + millis)) {
+ // If this vibration will end before the time passed in, let
+ // the new vibration play.
+ return false;
+ }
+ return true;
+ }
+ }
+
HardwareService(Context context) {
// Reset the hardware to a default state, in case this is a runtime
// restart instead of a fresh boot.
@@ -66,8 +133,10 @@ public class HardwareService extends IHardwareService.Stub {
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(true);
+ mVibrations = new LinkedList<Vibration>();
+
mBatteryStats = BatteryStatsService.getService();
-
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mIntentReceiver, filter);
@@ -78,13 +147,27 @@ public class HardwareService extends IHardwareService.Stub {
super.finalize();
}
- public void vibrate(long milliseconds) {
+ public void vibrate(long milliseconds, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
- doCancelVibrate();
- vibratorOn(milliseconds);
+ // 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.
+ if (milliseconds <= 0 || (mCurrentVibration != null
+ && mCurrentVibration.hasLongerTimeout(milliseconds))) {
+ // Ignore this vibration since the current vibration will play for
+ // longer than milliseconds.
+ return;
+ }
+ Vibration vib = new Vibration(token, milliseconds);
+ synchronized (mVibrations) {
+ removeVibrationLocked(token);
+ doCancelVibrateLocked();
+ mCurrentVibration = vib;
+ startVibrationLocked(vib);
+ }
}
private boolean isAll0(long[] pattern) {
@@ -121,34 +204,25 @@ public class HardwareService extends IHardwareService.Stub {
return;
}
- synchronized (this) {
- Death death = new Death(token);
- try {
- token.linkToDeath(death, 0);
- } catch (RemoteException e) {
- return;
- }
-
- Thread oldThread = mThread;
-
- if (oldThread != null) {
- // stop the old one
- synchronized (mThread) {
- mThread.mDone = true;
- mThread.notify();
- }
- }
+ Vibration vib = new Vibration(token, pattern, repeat);
+ try {
+ token.linkToDeath(vib, 0);
+ } catch (RemoteException e) {
+ return;
+ }
- if (mDeath != null) {
- mToken.unlinkToDeath(mDeath, 0);
+ synchronized (mVibrations) {
+ removeVibrationLocked(token);
+ doCancelVibrateLocked();
+ if (repeat >= 0) {
+ mVibrations.addFirst(vib);
+ startNextVibrationLocked();
+ } else {
+ // A negative repeat means that this pattern is not meant
+ // to repeat. Treat it like a simple vibration.
+ mCurrentVibration = vib;
+ startVibrationLocked(vib);
}
-
- mDeath = death;
- mToken = token;
-
- // start the new thread
- mThread = new VibrateThread(pattern, repeat);
- mThread.start();
}
}
finally {
@@ -156,7 +230,7 @@ public class HardwareService extends IHardwareService.Stub {
}
}
- public void cancelVibrate() {
+ public void cancelVibrate(IBinder token) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.VIBRATE,
"cancelVibrate");
@@ -164,21 +238,27 @@ public class HardwareService extends IHardwareService.Stub {
// so wakelock calls will succeed
long identity = Binder.clearCallingIdentity();
try {
- doCancelVibrate();
+ synchronized (mVibrations) {
+ final Vibration vib = removeVibrationLocked(token);
+ if (vib == mCurrentVibration) {
+ doCancelVibrateLocked();
+ startNextVibrationLocked();
+ }
+ }
}
finally {
Binder.restoreCallingIdentity(identity);
}
}
-
+
public boolean getFlashlightEnabled() {
return Hardware.getFlashlightEnabled();
}
-
+
public void setFlashlightEnabled(boolean on) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT)
!= PackageManager.PERMISSION_GRANTED &&
- mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
+ mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires FLASHLIGHT or HARDWARE_TEST permission");
}
@@ -186,60 +266,40 @@ public class HardwareService extends IHardwareService.Stub {
}
public void enableCameraFlash(int milliseconds) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CAMERA)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED &&
- mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
+ mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires CAMERA or HARDWARE_TEST permission");
}
Hardware.enableCameraFlash(milliseconds);
}
- public void setBacklights(int brightness) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires HARDWARE_TEST permission");
- }
- // Don't let applications turn the screen all the way off
- brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
- setLightBrightness_UNCHECKED(LIGHT_ID_BACKLIGHT, brightness);
- setLightBrightness_UNCHECKED(LIGHT_ID_KEYBOARD, brightness);
- setLightBrightness_UNCHECKED(LIGHT_ID_BUTTONS, brightness);
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(brightness);
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
void setLightOff_UNCHECKED(int light) {
- setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0);
+ setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0, 0);
}
- void setLightBrightness_UNCHECKED(int light, int brightness) {
+ void setLightBrightness_UNCHECKED(int light, int brightness, int brightnessMode) {
int b = brightness & 0x000000ff;
b = 0xff000000 | (b << 16) | (b << 8) | b;
- setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0);
+ setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
}
void setLightColor_UNCHECKED(int light, int color) {
- setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0);
+ setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0, 0);
}
void setLightFlashing_UNCHECKED(int light, int color, int mode, int onMS, int offMS) {
- setLight_native(mNativePointer, light, color, mode, onMS, offMS);
+ setLight_native(mNativePointer, light, color, mode, onMS, offMS, 0);
}
- public void setAttentionLight(boolean on) {
+ public void setAttentionLight(boolean on, int color) {
// Not worthy of a permission. We shouldn't have a flashlight permission.
synchronized (this) {
mAttentionLightOn = on;
mPulsing = false;
- setLight_native(mNativePointer, LIGHT_ID_ATTENTION, on ? 0xffffffff : 0,
- LIGHT_FLASH_NONE, 0, 0);
+ setLight_native(mNativePointer, LIGHT_ID_ATTENTION, color,
+ LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0, 0);
}
}
@@ -253,8 +313,8 @@ public class HardwareService extends IHardwareService.Stub {
}
if (!mAttentionLightOn && !mPulsing) {
mPulsing = true;
- setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0xff101010,
- LIGHT_FLASH_NONE, 0, 0);
+ setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0x00ffffff,
+ LIGHT_FLASH_HARDWARE, 7, 0, 0);
mH.sendMessageDelayed(Message.obtain(mH, 1), 3000);
}
}
@@ -271,33 +331,80 @@ public class HardwareService extends IHardwareService.Stub {
mPulsing = false;
setLight_native(mNativePointer, LIGHT_ID_ATTENTION,
mAttentionLightOn ? 0xffffffff : 0,
- LIGHT_FLASH_NONE, 0, 0);
+ LIGHT_FLASH_NONE, 0, 0, 0);
}
}
}
};
- private void doCancelVibrate() {
- synchronized (this) {
- if (mThread != null) {
- synchronized (mThread) {
- mThread.mDone = true;
- mThread.notify();
- }
- mThread = null;
+ private final Runnable mVibrationRunnable = new Runnable() {
+ public void run() {
+ synchronized (mVibrations) {
+ doCancelVibrateLocked();
+ startNextVibrationLocked();
+ }
+ }
+ };
+
+ // Lock held on mVibrations
+ private void doCancelVibrateLocked() {
+ if (mThread != null) {
+ synchronized (mThread) {
+ mThread.mDone = true;
+ mThread.notify();
}
- vibratorOff();
+ mThread = null;
}
+ vibratorOff();
+ mH.removeCallbacks(mVibrationRunnable);
+ }
+
+ // Lock held on mVibrations
+ private void startNextVibrationLocked() {
+ if (mVibrations.size() <= 0) {
+ return;
+ }
+ mCurrentVibration = mVibrations.getFirst();
+ startVibrationLocked(mCurrentVibration);
+ }
+
+ // Lock held on mVibrations
+ private void startVibrationLocked(final Vibration vib) {
+ if (vib.mTimeout != 0) {
+ vibratorOn(vib.mTimeout);
+ mH.postDelayed(mVibrationRunnable, vib.mTimeout);
+ } else {
+ // mThread better be null here. doCancelVibrate should always be
+ // called before startNextVibrationLocked or startVibrationLocked.
+ mThread = new VibrateThread(vib);
+ mThread.start();
+ }
+ }
+
+ // Lock held on mVibrations
+ private Vibration removeVibrationLocked(IBinder token) {
+ ListIterator<Vibration> iter = mVibrations.listIterator(0);
+ while (iter.hasNext()) {
+ Vibration vib = iter.next();
+ if (vib.mToken == token) {
+ iter.remove();
+ return vib;
+ }
+ }
+ // We might be looking for a simple vibration which is only stored in
+ // mCurrentVibration.
+ if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
+ return mCurrentVibration;
+ }
+ return null;
}
private class VibrateThread extends Thread {
- long[] mPattern;
- int mRepeat;
+ final Vibration mVibration;
boolean mDone;
-
- VibrateThread(long[] pattern, int repeat) {
- mPattern = pattern;
- mRepeat = repeat;
+
+ VibrateThread(Vibration vib) {
+ mVibration = vib;
mWakeLock.acquire();
}
@@ -323,12 +430,13 @@ public class HardwareService extends IHardwareService.Stub {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
synchronized (this) {
int index = 0;
- long[] pattern = mPattern;
+ long[] pattern = mVibration.mPattern;
int len = pattern.length;
+ int repeat = mVibration.mRepeat;
long duration = 0;
while (!mDone) {
- // add off-time duration to any accumulated on-time duration
+ // add off-time duration to any accumulated on-time duration
if (index < len) {
duration += pattern[index++];
}
@@ -347,68 +455,53 @@ public class HardwareService extends IHardwareService.Stub {
HardwareService.this.vibratorOn(duration);
}
} else {
- if (mRepeat < 0) {
+ if (repeat < 0) {
break;
} else {
- index = mRepeat;
+ index = repeat;
duration = 0;
}
}
}
- if (mDone) {
- // make sure vibrator is off if we were cancelled.
- // otherwise, it will turn off automatically
- // when the last timeout expires.
- HardwareService.this.vibratorOff();
- }
mWakeLock.release();
}
- synchronized (HardwareService.this) {
+ synchronized (mVibrations) {
if (mThread == this) {
mThread = null;
}
- }
- }
- };
-
- private class Death implements IBinder.DeathRecipient {
- IBinder mMe;
-
- Death(IBinder me) {
- mMe = me;
- }
-
- public void binderDied() {
- synchronized (HardwareService.this) {
- if (mMe == mToken) {
- doCancelVibrate();
+ if (!mDone) {
+ // If this vibration finished naturally, start the next
+ // vibration.
+ mVibrations.remove(mVibration);
+ startNextVibrationLocked();
}
}
}
- }
+ };
BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- doCancelVibrate();
+ synchronized (mVibrations) {
+ doCancelVibrateLocked();
+ mVibrations.clear();
+ }
}
}
};
-
+
private static native int init_native();
private static native void finalize_native(int ptr);
private static native void setLight_native(int ptr, int light, int color, int mode,
- int onMS, int offMS);
+ int onMS, int offMS, int brightnessMode);
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStats;
-
+
volatile VibrateThread mThread;
- volatile Death mDeath;
- volatile IBinder mToken;
private int mNativePointer;
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 9b0a2d4..bee3108 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -41,10 +41,16 @@ class HeadsetObserver extends UEventObserver {
private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
+ private static final int BIT_HEADSET = (1 << 0);
+ private static final int BIT_HEADSET_NO_MIC = (1 << 1);
+ private static final int BIT_TTY = (1 << 2);
+ private static final int BIT_FM_HEADSET = (1 << 3);
+ private static final int BIT_FM_SPEAKER = (1 << 4);
+
private int mHeadsetState;
+ private int mPrevHeadsetState;
private String mHeadsetName;
- private boolean mAudioRouteNeedsUpdate;
- private AudioManager mAudioManager;
+ private boolean mPendingIntent;
private final Context mContext;
private final WakeLock mWakeLock; // held while there is a pending route change
@@ -76,6 +82,7 @@ class HeadsetObserver extends UEventObserver {
String newName = mHeadsetName;
int newState = mHeadsetState;
+ mPrevHeadsetState = mHeadsetState;
try {
FileReader file = new FileReader(HEADSET_STATE_PATH);
int len = file.read(buffer, 0, 1024);
@@ -91,20 +98,25 @@ class HeadsetObserver extends UEventObserver {
Log.e(TAG, "" , e);
}
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
update(newName, newState);
}
private synchronized final void update(String newName, int newState) {
if (newName != mHeadsetName || newState != mHeadsetState) {
- boolean isUnplug = (newState == 0 && mHeadsetState == 1);
+ boolean isUnplug = false;
+ if ( (mHeadsetState & BIT_HEADSET) > 0 || (mHeadsetState & BIT_HEADSET_NO_MIC) > 0) {
+ if ((newState & BIT_HEADSET) == 0 && (newState & BIT_HEADSET_NO_MIC) == 0)
+ isUnplug = true;
+ }
mHeadsetName = newName;
+ mPrevHeadsetState = mHeadsetState;
mHeadsetState = newState;
- mAudioRouteNeedsUpdate = true;
-
- sendIntent(isUnplug);
+ mPendingIntent = true;
if (isUnplug) {
+ Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ mContext.sendBroadcast(intent);
+
// It can take hundreds of ms flush the audio pipeline after
// apps pause audio playback, but audio route changes are
// immediate, so delay the route change by 1000ms.
@@ -113,12 +125,13 @@ class HeadsetObserver extends UEventObserver {
mWakeLock.acquire();
mHandler.sendEmptyMessageDelayed(0, 1000);
} else {
- updateAudioRoute();
+ sendIntent();
+ mPendingIntent = false;
}
}
}
- private synchronized final void sendIntent(boolean isUnplug) {
+ private synchronized final void sendIntent() {
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -128,24 +141,15 @@ class HeadsetObserver extends UEventObserver {
// TODO: Should we require a permission?
ActivityManagerNative.broadcastStickyIntent(intent, null);
-
- if (isUnplug) {
- intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
- mContext.sendBroadcast(intent);
- }
- }
-
- private synchronized final void updateAudioRoute() {
- if (mAudioRouteNeedsUpdate) {
- mAudioManager.setWiredHeadsetOn(mHeadsetState == 1);
- mAudioRouteNeedsUpdate = false;
- }
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- updateAudioRoute();
+ if (mPendingIntent) {
+ sendIntent();
+ mPendingIntent = false;
+ }
mWakeLock.release();
}
};
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
index 7b8a2a4..6eb6242 100644
--- a/services/java/com/android/server/InputDevice.java
+++ b/services/java/com/android/server/InputDevice.java
@@ -23,9 +23,15 @@ import android.view.Surface;
import android.view.WindowManagerPolicy;
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;
+
final int id;
final int classes;
final String name;
@@ -34,9 +40,13 @@ public class InputDevice {
final AbsoluteInfo absPressure;
final AbsoluteInfo absSize;
- long mDownTime = 0;
+ 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);
@@ -48,146 +58,707 @@ public class InputDevice {
float yMoveScale;
MotionEvent currentMove = null;
boolean changed = false;
- boolean down = false;
- boolean lastDown = false;
- long downTime = 0;
- int x = 0;
- int y = 0;
- int pressure = 1;
- int size = 0;
+ 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 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];
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;
+ }
}
- MotionEvent generateMotion(InputDevice device, long curTime,
- boolean isAbs, Display display, int orientation,
- int metaState) {
- if (!changed) {
- return null;
+ /**
+ * 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;
}
- float scaledX = x;
- float scaledY = y;
- float temp;
- float scaledPressure = 1.0f;
- float scaledSize = 0;
- int edgeFlags = 0;
- if (isAbs) {
- int w = display.getWidth()-1;
- int h = display.getHeight()-1;
- if (orientation == Surface.ROTATION_90
- || orientation == Surface.ROTATION_270) {
- int tmp = w;
- w = h;
- h = tmp;
- }
- if (device.absX != null) {
- scaledX = ((scaledX-device.absX.minValue)
- / device.absX.range) * w;
- }
- if (device.absY != null) {
- scaledY = ((scaledY-device.absY.minValue)
- / device.absY.range) * h;
- }
- if (device.absPressure != null) {
- scaledPressure =
- ((pressure-device.absPressure.minValue)
- / (float)device.absPressure.range);
- }
- if (device.absSize != null) {
- scaledSize =
- ((size-device.absSize.minValue)
- / (float)device.absSize.range);
+ // 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) Log.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) Log.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;
+ Log.i("InputDevice", "Dropping bad point #" + i
+ + ": newY=" + y + " closestDy=" + closestDy
+ + " maxDy=" + maxDy);
+ mNextData[ioff + MotionEvent.SAMPLE_Y] = closestY;
+ break;
+ }
}
- switch (orientation) {
- case Surface.ROTATION_90:
- temp = scaledX;
- scaledX = scaledY;
- scaledY = w-temp;
+ mDroppedBadPoint[i] = dropped;
+ }
+ }
+
+ /**
+ * 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) Log.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) Log.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) Log.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) Log.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) Log.v("InputDevice", "Averaging " + totalPressure
+ + " weight: (" + x + "," + y + ")");
+ mAveragedData[ioff + MotionEvent.SAMPLE_X] = x;
+ mAveragedData[ioff + MotionEvent.SAMPLE_Y] = y;
+ }
+ 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) Log.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 (!allowOverlap && last2Next[j] < 0) {
+ 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 (j == 0 || distance < bestDistance) {
+ bestDistance = distance;
+ bestIndex = j;
+ }
+ }
+
+ if (DEBUG_POINTERS) Log.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) Log.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) Log.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) Log.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) Log.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) Log.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) Log.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) Log.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);
break;
- case Surface.ROTATION_180:
- scaledX = w-scaledX;
- scaledY = h-scaledY;
+ }
+ i++;
+ nextId++;
+ }
+
+ if (DEBUG_POINTERS) Log.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) Log.v("InputDevice",
+ "Assigning new id to new pointer index " + j);
+ next2Last[j] = i;
break;
- case Surface.ROTATION_270:
- temp = scaledX;
- scaledX = h-scaledY;
- scaledY = temp;
+ }
+ }
+ }
+
+ // 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) Log.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) Log.v("InputDevice", "Removing old pointer");
+ for (int i=0; i<lastNumPointers; i++) {
+ if (last2Next[i] == -1) {
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "Removing old pointer at index " + i);
+ retIndex = i;
break;
+ }
}
-
- if (scaledX == 0) {
- edgeFlags += MotionEvent.EDGE_LEFT;
- } else if (scaledX == display.getWidth() - 1.0f) {
- edgeFlags += MotionEvent.EDGE_RIGHT;
+ }
+
+ 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) {
+ Log.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) Log.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_ID_SHIFT);
+ }
+ } else {
+ if (numPointers == 1) {
+ action = MotionEvent.ACTION_UP;
+ } else {
+ action = MotionEvent.ACTION_POINTER_UP
+ | (upOrDownPointer << MotionEvent.ACTION_POINTER_ID_SHIFT);
+ }
}
-
- if (scaledY == 0) {
- edgeFlags += MotionEvent.EDGE_TOP;
- } else if (scaledY == display.getHeight() - 1.0f) {
- edgeFlags += MotionEvent.EDGE_BOTTOM;
+ 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);
}
- } else {
- scaledX *= xMoveScale;
- scaledY *= yMoveScale;
switch (orientation) {
- case Surface.ROTATION_90:
- temp = scaledX;
- scaledX = scaledY;
- scaledY = -temp;
+ 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:
- scaledX = -scaledX;
- scaledY = -scaledY;
+ }
+ 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:
- temp = scaledX;
- scaledX = -scaledY;
- scaledY = temp;
+ }
+ 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;
}
}
- changed = false;
- if (down != lastDown) {
- int action;
- lastDown = down;
- if (down) {
+ if (currentMove != null) {
+ if (false) Log.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) {
+ Log.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;
- downTime = curTime;
- } else {
+ mDownTime = curTime;
+ } else if (mNextNumPointers == 0) {
action = MotionEvent.ACTION_UP;
+ } else {
+ action = MotionEvent.ACTION_MOVE;
}
+ mLastNumPointers = mNextNumPointers;
currentMove = null;
- if (!isAbs) {
- x = y = 0;
- }
- return MotionEvent.obtain(downTime, curTime, action,
- scaledX, scaledY, scaledPressure, scaledSize, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
} else {
- if (currentMove != null) {
- if (false) Log.i("InputDevice", "Adding batch x=" + scaledX
- + " y=" + scaledY + " to " + currentMove);
- currentMove.addBatch(curTime, scaledX, scaledY,
- scaledPressure, scaledSize, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Log.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
+ 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;
}
- MotionEvent me = MotionEvent.obtain(downTime, curTime,
- MotionEvent.ACTION_MOVE, scaledX, scaledY,
- scaledPressure, scaledSize, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
+ }
+
+ if (currentMove != null) {
+ if (false) Log.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) {
+ Log.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;
}
+ return me;
}
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 4198154..e2e0ba9 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -32,6 +32,7 @@ import org.xmlpull.v1.XmlPullParserException;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -40,6 +41,7 @@ import android.content.IntentFilter;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -180,6 +182,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
= new HashMap<IBinder, ClientState>();
/**
+ * Set once the system is ready to run third party code.
+ */
+ boolean mSystemReady;
+
+ /**
* Id of the currently selected input method.
*/
String mCurMethodId;
@@ -360,16 +367,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// Uh oh, current input method is no longer around!
// Pick another one...
Log.i(TAG, "Current input method removed: " + curInputMethodId);
- List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
- if (enabled != null && enabled.size() > 0) {
- changed = true;
- curIm = enabled.get(0);
- curInputMethodId = curIm.getId();
- Log.i(TAG, "Switching to: " + curInputMethodId);
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD,
- curInputMethodId);
- } else if (curIm != null) {
+ if (!chooseNewDefaultIME()) {
changed = true;
curIm = null;
curInputMethodId = "";
@@ -383,16 +381,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} else if (curIm == null) {
// We currently don't have a default input method... is
// one now available?
- List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
- if (enabled != null && enabled.size() > 0) {
- changed = true;
- curIm = enabled.get(0);
- curInputMethodId = curIm.getId();
- Log.i(TAG, "New default input method: " + curInputMethodId);
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD,
- curInputMethodId);
- }
+ changed = chooseNewDefaultIME();
}
if (changed) {
@@ -508,6 +497,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
public void systemReady() {
+ synchronized (mMethodMap) {
+ if (!mSystemReady) {
+ mSystemReady = true;
+ try {
+ startInputInnerLocked();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Unexpected exception", e);
+ }
+ }
+ }
}
public List<InputMethodInfo> getInputMethodList() {
@@ -727,6 +726,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ return startInputInnerLocked();
+ }
+
+ InputBindResult startInputInnerLocked() {
+ if (mCurMethodId == null) {
+ return mNoBinding;
+ }
+
+ if (!mSystemReady) {
+ // If the system is not yet ready, we shouldn't be running third
+ // party code.
+ return new InputBindResult(null, mCurMethodId, mCurSeq);
+ }
+
InputMethodInfo info = mMethodMap.get(mCurMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
@@ -736,6 +749,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
+ mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.input_method_binding_label);
+ mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {
mLastBindTime = SystemClock.uptimeMillis();
mHaveConnection = true;
@@ -777,17 +794,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);
+ if (mCurToken == null) {
+ Log.w(TAG, "Service connected without a token!");
+ unbindCurrentMethodLocked(false);
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken);
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
+ MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
if (mCurClient != null) {
- if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken);
+ if (DEBUG) Log.v(TAG, "Creating first session while with client "
+ + mCurClient);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
- if (mCurClient != null) {
- if (DEBUG) Log.v(TAG, "Creating first session while with client "
- + mCurClient);
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_CREATE_SESSION, mCurMethod,
- new MethodCallback(mCurMethod)));
- }
+ MSG_CREATE_SESSION, mCurMethod,
+ new MethodCallback(mCurMethod)));
}
}
}
@@ -977,6 +997,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mShowExplicitlyRequested = true;
mShowForced = true;
}
+
+ if (!mSystemReady) {
+ return false;
+ }
+
boolean res = false;
if (mCurMethod != null) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
@@ -1327,6 +1352,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
+ private boolean isSystemIme(InputMethodInfo inputMethod) {
+ return (inputMethod.getServiceInfo().applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ private boolean chooseNewDefaultIME() {
+ List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
+ if (enabled != null && enabled.size() > 0) {
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD,
+ enabled.get(0).getId());
+ return true;
+ }
+
+ return false;
+ }
+
void buildInputMethodListLocked(ArrayList<InputMethodInfo> list,
HashMap<String, InputMethodInfo> map) {
list.clear();
@@ -1357,6 +1399,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
list.add(p);
map.put(p.getId(), p);
+ // System IMEs are enabled by default
+ if (isSystemIme(p)) {
+ setInputMethodEnabled(p.getId(), true);
+ }
+
if (DEBUG) {
Log.d(TAG, "Found a third-party input method " + p);
}
@@ -1367,6 +1414,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Log.w(TAG, "Unable to load input method " + compName, e);
}
}
+
+ String defaultIme = Settings.Secure.getString(mContext
+ .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ if (!map.containsKey(defaultIme)) {
+ if (chooseNewDefaultIME()) {
+ updateFromSettingsLocked();
+ }
+ }
}
// ----------------------------------------------------------------------
@@ -1612,7 +1667,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
+ " mShowForced=" + mShowForced
+ " mInputShown=" + mInputShown);
- p.println(" mScreenOn=" + mScreenOn);
+ p.println(" mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn);
}
if (client != null) {
diff --git a/services/java/com/android/server/JournaledFile.java b/services/java/com/android/server/JournaledFile.java
new file mode 100644
index 0000000..3d1f52d
--- /dev/null
+++ b/services/java/com/android/server/JournaledFile.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.io.File;
+import java.io.IOException;
+
+public class JournaledFile {
+ File mReal;
+ File mTemp;
+ boolean mWriting;
+
+ public JournaledFile(File real, File temp) {
+ mReal = real;
+ mTemp = temp;
+ }
+
+ /** Returns the file for you to read.
+ * @more
+ * Prefers the real file. If it doesn't exist, uses the temp one, and then copies
+ * it to the real one. If there is both a real file and a temp one, assumes that the
+ * temp one isn't fully written and deletes it.
+ */
+ public File chooseForRead() {
+ File result;
+ if (mReal.exists()) {
+ result = mReal;
+ if (mTemp.exists()) {
+ mTemp.delete();
+ }
+ } else if (mTemp.exists()) {
+ result = mTemp;
+ mTemp.renameTo(mReal);
+ } else {
+ return mReal;
+ }
+ return result;
+ }
+
+ /**
+ * Returns a file for you to write.
+ * @more
+ * If a write is already happening, throws. In other words, you must provide your
+ * own locking.
+ * <p>
+ * Call {@link #commit} to commit the changes, or {@link #rollback} to forget the changes.
+ */
+ public File chooseForWrite() {
+ if (mWriting) {
+ throw new IllegalStateException("uncommitted write already in progress");
+ }
+ if (!mReal.exists()) {
+ // If the real one doesn't exist, it's either because this is the first time
+ // or because something went wrong while copying them. In this case, we can't
+ // trust anything that's in temp. In order to have the chooseForRead code not
+ // use the temporary one until it's fully written, create an empty file
+ // for real, which will we'll shortly delete.
+ try {
+ mReal.createNewFile();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ if (mTemp.exists()) {
+ mTemp.delete();
+ }
+ mWriting = true;
+ return mTemp;
+ }
+
+ /**
+ * Commit changes.
+ */
+ public void commit() {
+ if (!mWriting) {
+ throw new IllegalStateException("no file to commit");
+ }
+ mWriting = false;
+ mTemp.renameTo(mReal);
+ }
+
+ /**
+ * Roll back changes.
+ */
+ public void rollback() {
+ if (!mWriting) {
+ throw new IllegalStateException("no file to roll back");
+ }
+ mWriting = false;
+ mTemp.delete();
+ }
+}
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 411cd6b..d68ccfa 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -18,10 +18,13 @@ package com.android.server;
import android.content.Context;
import android.content.res.Configuration;
-import android.os.SystemClock;
+import android.os.Environment;
+import android.os.LatencyTimer;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
+import android.util.Xml;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -29,10 +32,38 @@ 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.util.ArrayList;
+
public abstract class KeyInputQueue {
static final String TAG = "KeyInputQueue";
- SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
+ 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 final boolean BAD_TOUCH_HACK = true;
+
+ 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;
@@ -43,10 +74,14 @@ public abstract class KeyInputQueue {
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[] {
@@ -73,14 +108,21 @@ public abstract class KeyInputQueue {
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 when;
+ long whenNano;
int flags; // From the raw event
int classType; // One of the class constants in InputEvent
Object event;
@@ -88,7 +130,7 @@ public abstract class KeyInputQueue {
void copyFrom(QueuedEvent that) {
this.inputDevice = that.inputDevice;
- this.when = that.when;
+ this.whenNano = that.whenNano;
this.flags = that.flags;
this.classType = that.classType;
this.event = that.event;
@@ -106,7 +148,144 @@ public abstract class KeyInputQueue {
QueuedEvent next;
}
- KeyInputQueue(Context context) {
+ /**
+ * 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) Log.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) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
+ final int N = it.length-6;
+ for (int i=0; i<=N; i+=6) {
+ if (!"0x01".equals(it[i])) {
+ Log.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) Log.v(TAG, "Virtual key "
+ + sb.scancode + ": center=" + sb.centerx + ","
+ + sb.centery + " size=" + sb.width + "x"
+ + sb.height);
+ mVirtualKeys.add(sb);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Bad number at region " + i + " in: "
+ + str, e);
+ }
+ }
+ }
+ br.close();
+ } catch (FileNotFoundException e) {
+ Log.i(TAG, "No virtual keys found");
+ } catch (IOException e) {
+ Log.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) Log.v(TAG, "addExcludedDevice " + name);
+ addExcludedDevice(name);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // It's ok if the file does not exist.
+ } catch (Exception e) {
+ Log.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);
+ }
+
+ mHapticFeedbackCallback = hapticFeedbackCallback;
+
+ readExcludedDevices();
+
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -123,6 +302,12 @@ public abstract class KeyInputQueue {
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) {
@@ -149,22 +334,76 @@ public abstract class KeyInputQueue {
config.navigation
= Configuration.NAVIGATION_TRACKBALL;
//Log.i("foo", "***** HAVE TRACKBALL!");
+ } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
+ config.navigation
+ = Configuration.NAVIGATION_DPAD;
+ //Log.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 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 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 getScancodeState(int sw);
- public static native int getScancodeState(int deviceId, int sw);
- public static native int getKeycodeState(int sw);
- public static native int getKeycodeState(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,
@@ -181,12 +420,13 @@ public abstract class KeyInputQueue {
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
+ if (DEBUG) Log.v(TAG, "InputDeviceReader.run()");
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
- try {
- RawInputEvent ev = new RawInputEvent();
- while (true) {
+ RawInputEvent ev = new RawInputEvent();
+ while (true) {
+ try {
InputDevice di;
// block, doesn't release the monitor
@@ -207,23 +447,50 @@ public abstract class KeyInputQueue {
if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
synchronized (mFirst) {
di = newInputDevice(ev.deviceId);
- mDevices.put(ev.deviceId, di);
- configChanged = true;
+ 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);
+ Log.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) {
- Log.i(TAG, "Device removed: id=0x"
- + Integer.toHexString(ev.deviceId));
+ if (false) {
+ Log.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 {
- Log.w(TAG, "Bad device id: " + ev.deviceId);
+ Log.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);
@@ -235,13 +502,9 @@ public abstract class KeyInputQueue {
}
}
- if (di == null) {
- continue;
- }
-
if (configChanged) {
synchronized (mFirst) {
- addLocked(di, SystemClock.uptimeMillis(), 0,
+ addLocked(di, System.nanoTime(), 0,
RawInputEvent.CLASS_CONFIGURATION_CHANGED,
null);
}
@@ -256,6 +519,7 @@ public abstract class KeyInputQueue {
// timebase as SystemClock.uptimeMillis().
//curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
final long curTime = SystemClock.uptimeMillis();
+ final long curTimeNano = System.nanoTime();
//Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
final int classes = di.classes;
@@ -271,95 +535,376 @@ public abstract class KeyInputQueue {
boolean down;
if (ev.value != 0) {
down = true;
- di.mDownTime = curTime;
+ di.mKeyDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
- addLocked(di, curTime, ev.flags,
+ addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
- newKeyEvent(di, di.mDownTime, curTime, down,
+ 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) != 0) {
+ (classes&(RawInputEvent.CLASS_TOUCHSCREEN
+ |RawInputEvent.CLASS_TOUCHSCREEN_MT))
+ == RawInputEvent.CLASS_TOUCHSCREEN) {
di.mAbs.changed = true;
- di.mAbs.down = ev.value != 0;
- }
- if (ev.scancode == RawInputEvent.BTN_MOUSE &&
+ 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.down = ev.value != 0;
+ 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) Log.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) Log.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.mAbs.x = ev.value;
+ di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_Y) {
di.mAbs.changed = true;
- di.mAbs.y = ev.value;
+ di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
di.mAbs.changed = true;
- di.mAbs.pressure = ev.value;
+ 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.mAbs.size = ev.value;
+ 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.x += ev.value;
+ di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
} else if (ev.scancode == RawInputEvent.REL_Y) {
di.mRel.changed = true;
- di.mRel.y += ev.value;
+ di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
}
}
- if (send || ev.type == RawInputEvent.EV_SYN) {
+ // 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) Log.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) Log.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;
- me = di.mAbs.generateMotion(di, curTime, true,
- mDisplay, mOrientation, mGlobalMetaState);
- if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
- + " y=" + di.mAbs.y + " ev=" + me);
- if (me != null) {
- if (WindowManagerPolicy.WATCH_POINTER) {
- Log.i(TAG, "Enqueueing: " + me);
+
+ InputDevice.MotionState ms = di.mAbs;
+ if (ms.changed) {
+ 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++;
+ }
}
- addLocked(di, curTime, ev.flags,
- RawInputEvent.CLASS_TOUCHSCREEN, me);
+
+ if (BAD_TOUCH_HACK) {
+ ms.dropBadPoint(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) Log.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) {
+ Log.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();
}
- me = di.mRel.generateMotion(di, curTime, false,
- mDisplay, mOrientation, mGlobalMetaState);
- if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
- + " y=" + di.mRel.y + " ev=" + me);
- if (me != null) {
- addLocked(di, curTime, ev.flags,
- RawInputEvent.CLASS_TRACKBALL, me);
+
+ ms = di.mRel;
+ if (ms.changed) {
+ ms.changed = false;
+
+ me = ms.generateRelMotion(di, curTime,
+ curTimeNano,
+ mOrientation, mGlobalMetaState);
+ if (false) Log.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);
+ }
+
+ ms.finish();
}
}
}
}
+
+ } catch (RuntimeException exc) {
+ Log.e(TAG, "InputReaderThread uncaught exception", exc);
}
}
- catch (RuntimeException exc) {
- Log.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) Log.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) Log.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) Log.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) Log.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;
+ Log.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) Log.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) Log.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.
*/
@@ -497,6 +1042,29 @@ public abstract class KeyInputQueue {
}
}
+ /**
+ * 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) {
//Log.i(TAG, "Recycle event: " + ev);
@@ -506,8 +1074,8 @@ public abstract class KeyInputQueue {
if (ev.event == ev.inputDevice.mRel.currentMove) {
if (false) Log.i(TAG, "Detach rel " + ev.event);
ev.inputDevice.mRel.currentMove = null;
- ev.inputDevice.mRel.x = 0;
- ev.inputDevice.mRel.y = 0;
+ ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
+ ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
}
recycleLocked(ev);
}
@@ -530,7 +1098,7 @@ public abstract class KeyInputQueue {
}
}
- private QueuedEvent obtainLocked(InputDevice device, long when,
+ private QueuedEvent obtainLocked(InputDevice device, long whenNano,
int flags, int classType, Object event) {
QueuedEvent ev;
if (mCacheCount == 0) {
@@ -542,7 +1110,7 @@ public abstract class KeyInputQueue {
mCacheCount--;
}
ev.inputDevice = device;
- ev.when = when;
+ ev.whenNano = whenNano;
ev.flags = flags;
ev.classType = classType;
ev.event = event;
@@ -561,13 +1129,13 @@ public abstract class KeyInputQueue {
}
}
- private void addLocked(InputDevice device, long when, int flags,
+ private void addLocked(InputDevice device, long whenNano, int flags,
int classType, Object event) {
boolean poke = mFirst.next == mLast;
- QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
+ QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
QueuedEvent p = mLast.prev;
- while (p != mFirst && ev.when < p.when) {
+ while (p != mFirst && ev.whenNano < p.whenNano) {
p = p.prev;
}
@@ -578,31 +1146,48 @@ public abstract class KeyInputQueue {
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);
- Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
- + ", name=" + name
- + ", classes=" + Integer.toHexString(classes));
- InputDevice.AbsoluteInfo absX;
- InputDevice.AbsoluteInfo absY;
- InputDevice.AbsoluteInfo absPressure;
- InputDevice.AbsoluteInfo absSize;
- 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");
- } else {
- absX = null;
- absY = null;
- absPressure = null;
- absSize = null;
+ InputDevice.AbsoluteInfo absX = null;
+ InputDevice.AbsoluteInfo absY = null;
+ InputDevice.AbsoluteInfo absPressure = null;
+ InputDevice.AbsoluteInfo absSize = null;
+ if (classes != 0) {
+ Log.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);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 3f268c9..bbb43d7 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,12 +16,7 @@
package com.android.server;
-import java.io.BufferedReader;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -31,7 +26,6 @@ import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
-import java.util.regex.Pattern;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -49,10 +43,12 @@ import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.ILocationProvider;
+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.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -63,7 +59,6 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -71,6 +66,7 @@ import android.util.PrintWriterPrinter;
import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.LocationProviderProxy;
import com.android.internal.location.MockProvider;
+import com.android.internal.location.GpsNetInitiatedHandler;
/**
* The service class that manages LocationProviders and issues location
@@ -82,17 +78,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private static final String TAG = "LocationManagerService";
private static final boolean LOCAL_LOGV = false;
- // Minimum time interval between last known location writes, in milliseconds.
- private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
-
- // Max time to hold wake lock for, in milliseconds.
- private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
-
// The last time a location was written, by provider name.
private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
- private static final Pattern PATTERN_COMMA = Pattern.compile(",");
-
private static final String ACCESS_FINE_LOCATION =
android.Manifest.permission.ACCESS_FINE_LOCATION;
private static final String ACCESS_COARSE_LOCATION =
@@ -118,6 +106,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private final Context mContext;
private IGeocodeProvider mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
+ private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
// Cache the real providers for use in addTestProvider() and removeTestProvider()
@@ -394,7 +383,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
public void locationCallbackFinished(ILocationListener listener) {
- Receiver receiver = getReceiver(listener);
+ //Do not use getReceiver here as that will add the ILocationListener to
+ //the receiver list if it is not found. If it is not found then the
+ //LocationListener was removed when it had a pending broadcast and should
+ //not be added back.
+ IBinder binder = listener.asBinder();
+ Receiver receiver = mReceivers.get(binder);
if (receiver != null) {
synchronized (receiver) {
// so wakelock calls will succeed
@@ -413,97 +407,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private Location readLastKnownLocationLocked(String provider) {
- Location location = null;
- String s = null;
- try {
- File f = new File(LocationManager.SYSTEM_DIR + "/location."
- + provider);
- if (!f.exists()) {
- return null;
- }
- BufferedReader reader = new BufferedReader(new FileReader(f), 256);
- s = reader.readLine();
- } catch (IOException e) {
- Log.w(TAG, "Unable to read last known location", e);
- }
-
- if (s == null) {
- return null;
- }
- try {
- String[] tokens = PATTERN_COMMA.split(s);
- int idx = 0;
- long time = Long.parseLong(tokens[idx++]);
- double latitude = Double.parseDouble(tokens[idx++]);
- double longitude = Double.parseDouble(tokens[idx++]);
- double altitude = Double.parseDouble(tokens[idx++]);
- float bearing = Float.parseFloat(tokens[idx++]);
- float speed = Float.parseFloat(tokens[idx++]);
-
- location = new Location(provider);
- location.setTime(time);
- location.setLatitude(latitude);
- location.setLongitude(longitude);
- location.setAltitude(altitude);
- location.setBearing(bearing);
- location.setSpeed(speed);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "NumberFormatException reading last known location", nfe);
- return null;
- }
-
- return location;
- }
-
- private void writeLastKnownLocationLocked(String provider,
- Location location) {
- long now = SystemClock.elapsedRealtime();
- Long last = mLastWriteTime.get(provider);
- if ((last != null)
- && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
- return;
- }
- mLastWriteTime.put(provider, now);
-
- StringBuilder sb = new StringBuilder(100);
- sb.append(location.getTime());
- sb.append(',');
- sb.append(location.getLatitude());
- sb.append(',');
- sb.append(location.getLongitude());
- sb.append(',');
- sb.append(location.getAltitude());
- sb.append(',');
- sb.append(location.getBearing());
- sb.append(',');
- sb.append(location.getSpeed());
-
- FileWriter writer = null;
- try {
- File d = new File(LocationManager.SYSTEM_DIR);
- if (!d.exists()) {
- if (!d.mkdirs()) {
- Log.w(TAG, "Unable to create directory to write location");
- return;
- }
- }
- File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
- writer = new FileWriter(f);
- writer.write(sb.toString());
- } catch (IOException e) {
- Log.w(TAG, "Unable to write location", e);
- } finally {
- if (writer != null) {
- try {
- writer.close();
- } catch (IOException e) {
- Log.w(TAG, "Exception closing file", e);
- }
- }
- }
- }
-
private void addProvider(LocationProviderProxy provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
@@ -541,6 +444,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Create a gps location provider
GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
mGpsStatusProvider = provider.getGpsStatusProvider();
+ mNetInitiatedListener = provider.getNetInitiatedListener();
LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
addProvider(proxy);
mGpsLocationProvider = proxy;
@@ -626,7 +530,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
// notify provider of current network state
- proxy.updateNetworkState(mNetworkState);
+ proxy.updateNetworkState(mNetworkState, null);
}
}
@@ -850,7 +754,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
*/
void disposeLocked() {
ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
- records.remove(this);
+ if (records != null) {
+ records.remove(this);
+ }
}
@Override
@@ -869,15 +775,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
}
-
- /**
- * Calls dispose().
- */
- @Override protected void finalize() {
- synchronized (mLock) {
- disposeLocked();
- }
- }
}
private Receiver getReceiver(ILocationListener listener) {
@@ -1030,6 +927,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
try {
if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
+ synchronized(receiver) {
+ if(receiver.mPendingBroadcasts > 0) {
+ decrementPendingBroadcasts();
+ receiver.mPendingBroadcasts = 0;
+ }
+ }
}
// Record which providers were associated with this listener
@@ -1108,6 +1011,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ if (provider == null) {
+ // throw NullPointerException to remain compatible with previous implementation
+ throw new NullPointerException();
+ }
+
// first check for permission to the provider
checkPermissionsSafe(provider);
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
@@ -1118,7 +1026,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (mLock) {
LocationProviderProxy proxy = mProvidersByName.get(provider);
- if (provider == null) {
+ if (proxy == null) {
return false;
}
@@ -1126,6 +1034,22 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ public boolean sendNiResponse(int notifId, int userResponse)
+ {
+ if (Binder.getCallingUid() != Process.myUid()) {
+ throw new SecurityException(
+ "calling sendNiResponse from outside of the system is not allowed");
+ }
+ try {
+ return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
+ }
+ catch (RemoteException e)
+ {
+ Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
+ return false;
+ }
+ }
+
class ProximityAlert {
final int mUid;
final double mLatitude;
@@ -1157,13 +1081,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return mIntent;
}
- boolean isInProximity(double latitude, double longitude) {
+ boolean isInProximity(double latitude, double longitude, float accuracy) {
Location loc = new Location("");
loc.setLatitude(latitude);
loc.setLongitude(longitude);
double radius = loc.distanceTo(mLocation);
- return radius <= mRadius;
+ return radius <= Math.max(mRadius,accuracy);
}
@Override
@@ -1203,6 +1127,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
long now = System.currentTimeMillis();
double latitude = loc.getLatitude();
double longitude = loc.getLongitude();
+ float accuracy = loc.getAccuracy();
ArrayList<PendingIntent> intentsToRemove = null;
for (ProximityAlert alert : mProximityAlerts.values()) {
@@ -1212,7 +1137,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if ((expiration == -1) || (now <= expiration)) {
boolean entered = mProximitiesEntered.contains(alert);
boolean inProximity =
- alert.isInProximity(latitude, longitude);
+ alert.isInProximity(latitude, longitude, accuracy);
if (!entered && inProximity) {
if (LOCAL_LOGV) {
Log.v(TAG, "Entered alert");
@@ -1487,16 +1412,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return null;
}
- Location location = mLastKnownLocation.get(provider);
- if (location == null) {
- // Get the persistent last known location for the provider
- location = readLastKnownLocationLocked(provider);
- if (location != null) {
- mLastKnownLocation.put(provider, location);
- }
- }
-
- return location;
+ return mLastKnownLocation.get(provider);
}
private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
@@ -1541,7 +1457,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
} else {
lastLocation.set(location);
}
- writeLastKnownLocationLocked(provider, location);
// Fetch latest status update time
long newStatusUpdateTime = p.getStatusUpdateTime();
@@ -1686,13 +1601,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
} else {
mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
}
+ NetworkInfo info =
+ (NetworkInfo)intent.getExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
// Notify location providers of current network state
synchronized (mLock) {
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderProxy provider = mProviders.get(i);
if (provider.requiresNetwork()) {
- provider.updateNetworkState(mNetworkState);
+ provider.updateNetworkState(mNetworkState, info);
}
}
}
@@ -1792,6 +1709,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
checkMockPermissionsSafe();
+ long identity = Binder.clearCallingIdentity();
synchronized (mLock) {
MockProvider provider = new MockProvider(name, this,
requiresNetwork, requiresSatellite,
@@ -1814,6 +1732,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mLastKnownLocation.put(name, null);
updateProvidersLocked();
}
+ Binder.restoreCallingIdentity(identity);
}
public void removeTestProvider(String provider) {
@@ -1823,6 +1742,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
+ long identity = Binder.clearCallingIdentity();
removeProvider(mProvidersByName.get(provider));
mMockProviders.remove(mockProvider);
// reinstall real provider if we were mocking GPS or network provider
@@ -1835,6 +1755,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
mLastKnownLocation.put(provider, null);
updateProvidersLocked();
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -1870,6 +1791,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
+ long identity = Binder.clearCallingIdentity();
if (enabled) {
mockProvider.enable();
mEnabledProviders.add(provider);
@@ -1880,6 +1802,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mDisabledProviders.add(provider);
}
updateProvidersLocked();
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -1890,9 +1813,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
+ long identity = Binder.clearCallingIdentity();
mEnabledProviders.remove(provider);
mDisabledProviders.remove(provider);
updateProvidersLocked();
+ Binder.restoreCallingIdentity(identity);
}
}
diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java
index 5a42e76..3c366da 100644
--- a/services/java/com/android/server/MasterClearReceiver.java
+++ b/services/java/com/android/server/MasterClearReceiver.java
@@ -30,8 +30,8 @@ public class MasterClearReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals("android.intent.action.GTALK_DATA_MESSAGE_RECEIVED")) {
- if (!intent.getBooleanExtra("from_trusted_server", false)) {
+ if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
+ if (!intent.getBooleanExtra("android.intent.extra.from_trusted_server", false)) {
Log.w(TAG, "Ignoring master clear request -- not from trusted server.");
return;
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index f81c519..204389e 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -231,6 +231,7 @@ class MountService extends IMountService.Stub {
if (getMassStorageConnected() && !suppressIfConnected) {
Intent intent = new Intent();
intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
setUsbStorageNotification(
com.android.internal.R.string.usb_storage_notification_title,
@@ -295,7 +296,7 @@ class MountService extends IMountService.Stub {
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_sys_no_sim,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb,
true, false, null);
handlePossibleExplicitUnmountBroadcast(path);
@@ -312,7 +313,7 @@ class MountService extends IMountService.Stub {
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_sim_toolkit,
+ com.android.internal.R.drawable.stat_notify_sdcard,
true, true, null);
mShowSafeUnmountNotificationWhenUnmounted = false;
} else {
@@ -332,7 +333,7 @@ class MountService extends IMountService.Stub {
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_sim_toolkit,
+ com.android.internal.R.drawable.stat_notify_sdcard_prepare,
true, false, null);
updateUsbMassStorageNotification(true, false);
@@ -352,7 +353,7 @@ class MountService extends IMountService.Stub {
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_sys_no_sim,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb,
true, false, pi);
updateUsbMassStorageNotification(false, false);
intent = new Intent(Intent.ACTION_MEDIA_NOFS,
@@ -420,7 +421,7 @@ class MountService extends IMountService.Stub {
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_sys_no_sim,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb,
true, false, pi);
updateUsbMassStorageNotification(false, false);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index aac7124..ff23a13 100644..100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -30,11 +30,11 @@ import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
@@ -48,6 +48,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Power;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Vibrator;
@@ -96,12 +97,13 @@ class NotificationManagerService extends INotificationManager.Stub
private Vibrator mVibrator = new Vibrator();
// adb
- private int mBatteryPlugged;
+ private boolean mUsbConnected;
private boolean mAdbEnabled = false;
private boolean mAdbNotificationShown = false;
private Notification mAdbNotification;
- private ArrayList<NotificationRecord> mNotificationList;
+ private final ArrayList<NotificationRecord> mNotificationList =
+ new ArrayList<NotificationRecord>();
private ArrayList<ToastRecord> mToastQueue;
@@ -150,20 +152,22 @@ class NotificationManagerService extends INotificationManager.Stub
private static final class NotificationRecord
{
- String pkg;
- int id;
+ final String pkg;
+ final String tag;
+ final int id;
ITransientNotification callback;
int duration;
- Notification notification;
+ final Notification notification;
IBinder statusBarKey;
- NotificationRecord(String pkg, int id, Notification notification)
+ NotificationRecord(String pkg, String tag, int id, Notification notification)
{
this.pkg = pkg;
+ this.tag = tag;
this.id = id;
this.notification = notification;
}
-
+
void dump(PrintWriter pw, String prefix, Context baseContext) {
pw.println(prefix + this);
pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
@@ -187,7 +191,8 @@ class NotificationManagerService extends INotificationManager.Stub
return "NotificationRecord{"
+ Integer.toHexString(System.identityHashCode(this))
+ " pkg=" + pkg
- + " id=" + Integer.toHexString(id) + "}";
+ + " id=" + Integer.toHexString(id)
+ + " tag=" + tag + "}";
}
}
@@ -256,8 +261,9 @@ class NotificationManagerService extends INotificationManager.Stub
cancelAll();
}
- public void onNotificationClick(String pkg, int id) {
- cancelNotification(pkg, id, Notification.FLAG_AUTO_CANCEL);
+ public void onNotificationClick(String pkg, String tag, int id) {
+ cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
+ Notification.FLAG_FOREGROUND_SERVICE);
}
public void onPanelRevealed() {
@@ -310,8 +316,11 @@ class NotificationManagerService extends INotificationManager.Stub
mBatteryFull = batteryFull;
updateLights();
}
-
- mBatteryPlugged = intent.getIntExtra("plugged", 0);
+ } 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(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
@@ -323,7 +332,7 @@ class NotificationManagerService extends INotificationManager.Stub
if (pkgName == null) {
return;
}
- cancelAllNotifications(pkgName);
+ cancelAllNotificationsInt(pkgName, 0, 0);
}
}
};
@@ -363,7 +372,6 @@ class NotificationManagerService extends INotificationManager.Stub
mSound = new AsyncPlayer(TAG);
mSound.setUsesWakeLock(context);
mToastQueue = new ArrayList<ToastRecord>();
- mNotificationList = new ArrayList<NotificationRecord>();
mHandler = new WorkerHandler();
mStatusBarService = statusBar;
statusBar.setNotificationCallbacks(mNotificationCallbacks);
@@ -380,6 +388,8 @@ 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(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
mContext.registerReceiver(mIntentReceiver, filter);
@@ -575,6 +585,14 @@ class NotificationManagerService extends INotificationManager.Stub
// ============================================================================
public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
{
+ enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
+ }
+
+ public void enqueueNotificationWithTag(String pkg, String tag, int id,
+ Notification notification, int[] idOut)
+ {
+ checkIncomingCall(pkg);
+
// 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")
@@ -598,16 +616,29 @@ class NotificationManagerService extends INotificationManager.Stub
}
synchronized (mNotificationList) {
- NotificationRecord r = new NotificationRecord(pkg, id, notification);
+ NotificationRecord r = new NotificationRecord(pkg, tag, id, notification);
NotificationRecord old = null;
- int index = indexOfNotificationLocked(pkg, id);
+ int index = indexOfNotificationLocked(pkg, tag, id);
if (index < 0) {
mNotificationList.add(r);
} else {
old = mNotificationList.remove(index);
mNotificationList.add(index, r);
+ // Make sure we don't lose the foreground service state.
+ if (old != null) {
+ notification.flags |=
+ old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
+ }
}
+
+ // Ensure if this is a foreground service that the proper additional
+ // flags are set.
+ if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ notification.flags |= Notification.FLAG_ONGOING_EVENT
+ | Notification.FLAG_NO_CLEAR;
+ }
+
if (notification.icon != 0) {
IconData icon = IconData.makeIcon(null, pkg, notification.icon,
notification.iconLevel,
@@ -622,17 +653,18 @@ class NotificationManagerService extends INotificationManager.Stub
}
NotificationData n = new NotificationData();
- n.id = id;
- n.pkg = pkg;
- 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;
+ 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;
if (old != null && old.statusBarKey != null) {
r.statusBarKey = old.statusBarKey;
long identity = Binder.clearCallingIdentity();
@@ -802,21 +834,24 @@ class NotificationManagerService extends INotificationManager.Stub
}
/**
- * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}.
+ * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
+ * and none of the {@code mustNotHaveFlags}.
*/
- private void cancelNotification(String pkg, int id, int mustHaveFlags) {
+ private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
+ int mustNotHaveFlags) {
EventLog.writeEvent(EVENT_LOG_CANCEL, pkg, id, mustHaveFlags);
synchronized (mNotificationList) {
- NotificationRecord r = null;
-
- int index = indexOfNotificationLocked(pkg, id);
+ int index = indexOfNotificationLocked(pkg, tag, id);
if (index >= 0) {
- r = mNotificationList.get(index);
+ NotificationRecord r = mNotificationList.get(index);
if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
return;
}
+ if ((r.notification.flags & mustNotHaveFlags) != 0) {
+ return;
+ }
mNotificationList.remove(index);
@@ -830,7 +865,8 @@ class NotificationManagerService extends INotificationManager.Stub
* Cancels all notifications from a given package that have all of the
* {@code mustHaveFlags}.
*/
- private void cancelAllNotificationsInt(String pkg, int mustHaveFlags) {
+ void cancelAllNotificationsInt(String pkg, int mustHaveFlags,
+ int mustNotHaveFlags) {
EventLog.writeEvent(EVENT_LOG_CANCEL_ALL, pkg, mustHaveFlags);
synchronized (mNotificationList) {
@@ -841,6 +877,9 @@ class NotificationManagerService extends INotificationManager.Stub
if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
continue;
}
+ if ((r.notification.flags & mustNotHaveFlags) != 0) {
+ continue;
+ }
if (!r.pkg.equals(pkg)) {
continue;
}
@@ -855,17 +894,44 @@ class NotificationManagerService extends INotificationManager.Stub
}
- public void cancelNotification(String pkg, int id)
- {
- cancelNotification(pkg, id, 0);
+ public void cancelNotification(String pkg, int id) {
+ cancelNotificationWithTag(pkg, null /* tag */, id);
}
- public void cancelAllNotifications(String pkg)
- {
- cancelAllNotificationsInt(pkg, 0);
+ public void cancelNotificationWithTag(String pkg, String tag, int id) {
+ checkIncomingCall(pkg);
+ // Don't allow client applications to cancel foreground service notis.
+ cancelNotification(pkg, tag, id, 0,
+ Binder.getCallingUid() == Process.SYSTEM_UID
+ ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
+ }
+
+ public void cancelAllNotifications(String pkg) {
+ checkIncomingCall(pkg);
+
+ // Calling from user space, don't allow the canceling of actively
+ // running foreground services.
+ cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE);
}
- public void cancelAll() {
+ void checkIncomingCall(String pkg) {
+ int uid = Binder.getCallingUid();
+ if (uid == Process.SYSTEM_UID || uid == 0) {
+ return;
+ }
+ try {
+ ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
+ pkg, 0);
+ if (ai.uid != uid) {
+ throw new SecurityException("Calling uid " + uid + " gave package"
+ + pkg + " which is owned by uid " + ai.uid);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new SecurityException("Unknown package " + pkg);
+ }
+ }
+
+ void cancelAll() {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
for (int i=N-1; i>=0; i--) {
@@ -902,8 +968,15 @@ class NotificationManagerService extends INotificationManager.Stub
{
// Battery low always shows, other states only show if charging.
if (mBatteryLow) {
- mHardware.setLightFlashing_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, BATTERY_LOW_ARGB,
- HardwareService.LIGHT_FLASH_TIMED, BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
+ if (mBatteryCharging) {
+ mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY,
+ BATTERY_LOW_ARGB);
+ } else {
+ // Flash when battery is low and not charging
+ mHardware.setLightFlashing_UNCHECKED(HardwareService.LIGHT_ID_BATTERY,
+ BATTERY_LOW_ARGB, HardwareService.LIGHT_FLASH_TIMED,
+ BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
+ }
} else if (mBatteryCharging) {
if (mBatteryFull) {
mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY,
@@ -937,12 +1010,21 @@ class NotificationManagerService extends INotificationManager.Stub
}
// lock on mNotificationList
- private int indexOfNotificationLocked(String pkg, int id)
+ private int indexOfNotificationLocked(String pkg, String tag, int id)
{
ArrayList<NotificationRecord> list = mNotificationList;
final int len = list.size();
for (int i=0; i<len; i++) {
NotificationRecord r = list.get(i);
+ if (tag == null) {
+ if (r.tag != null) {
+ continue;
+ }
+ } else {
+ if (!tag.equals(r.tag)) {
+ continue;
+ }
+ }
if (r.id == id && r.pkg.equals(pkg)) {
return i;
}
@@ -954,7 +1036,7 @@ class NotificationManagerService extends INotificationManager.Stub
// security feature that we don't want people customizing the platform
// to accidentally lose.
private void updateAdbNotification() {
- if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) {
+ if (mAdbEnabled && mUsbConnected) {
if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
return;
}
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 786f423..dbd1826 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -51,7 +51,7 @@ import java.util.List;
*/
public class PackageManagerBackupAgent extends BackupAgent {
private static final String TAG = "PMBA";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
// key under which we store global metadata (individual app metadata
// is stored using the package name as a key)
@@ -195,7 +195,6 @@ public class PackageManagerBackupAgent extends BackupAgent {
byte[] sigs = flattenSignatureArray(info.signatures);
- // !!! TODO: take out this debugging
if (DEBUG) {
Log.v(TAG, "+ metadata for " + packName
+ " version=" + info.versionCode
@@ -214,7 +213,6 @@ public class PackageManagerBackupAgent extends BackupAgent {
// mentioned in the saved state file, but appear to no longer be present
// on the device. Write a deletion entity for them.
for (String app : mExisting) {
- // !!! TODO: take out this msg
if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
try {
data.writeEntityHeader(app, -1);
@@ -266,7 +264,6 @@ public class PackageManagerBackupAgent extends BackupAgent {
mStoredSdkVersion = storedSdkVersion;
mStoredIncrementalVersion = in.readUTF();
mHasMetadata = true;
- // !!! TODO: remove this debugging output
if (DEBUG) {
Log.i(TAG, "Restore set version " + storedSystemVersion
+ " is compatible with OS version " + Build.VERSION.SDK_INT
@@ -277,7 +274,6 @@ public class PackageManagerBackupAgent extends BackupAgent {
// it's a file metadata record
int versionCode = in.readInt();
Signature[] sigs = unflattenSignatureArray(in);
-// !!! TODO: take out this debugging
if (DEBUG) {
Log.i(TAG, " restored metadata for " + key
+ " dataSize=" + dataSize
@@ -326,7 +322,14 @@ public class PackageManagerBackupAgent extends BackupAgent {
try {
int num = in.readInt();
- Log.v(TAG, " ... unflatten read " + num);
+ if (DEBUG) Log.v(TAG, " ... unflatten read " + num);
+
+ // Sensical?
+ if (num > 20) {
+ Log.e(TAG, "Suspiciously large sig count in restore data; aborting");
+ throw new IllegalStateException("Bad restore state");
+ }
+
sigs = new Signature[num];
for (int i = 0; i < num; i++) {
int len = in.readInt();
@@ -340,7 +343,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
Log.w(TAG, "Empty signature block found");
}
} catch (IOException e) {
- Log.d(TAG, "Unable to unflatten sigs");
+ Log.e(TAG, "Unable to unflatten sigs");
return null;
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 89f854e..a83459e 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -35,6 +35,7 @@ import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
@@ -61,6 +62,8 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.Environment;
@@ -88,6 +91,7 @@ import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
@@ -108,6 +112,7 @@ class PackageManagerService extends IPackageManager.Stub {
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 FIRST_APPLICATION_UID =
Process.FIRST_APPLICATION_UID;
private static final int MAX_APPLICATION_UIDS = 1000;
@@ -138,7 +143,7 @@ class PackageManagerService extends IPackageManager.Stub {
final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
Process.THREAD_PRIORITY_BACKGROUND);
- final Handler mHandler;
+ final PackageHandler mHandler;
final int mSdkVersion = Build.VERSION.SDK_INT;
final String mSdkCodename = "REL".equals(Build.VERSION.CODENAME)
@@ -173,6 +178,7 @@ class PackageManagerService extends IPackageManager.Stub {
final File mFrameworkDir;
final File mSystemAppDir;
final File mAppInstallDir;
+ final File mDalvikCacheDir;
// Directory containing the private parts (e.g. code and non-resource assets) of forward-locked
// apps.
@@ -221,6 +227,14 @@ class PackageManagerService extends IPackageManager.Stub {
// etc/permissions.xml file.
final HashMap<String, String> mSharedLibraries = new HashMap<String, String>();
+ // Temporary for building the final shared libraries for an .apk.
+ String[] mTmpSharedLibraries = null;
+
+ // These are the features this devices supports that were read from the
+ // etc/permissions.xml file.
+ final HashMap<String, FeatureInfo> mAvailableFeatures =
+ new HashMap<String, FeatureInfo>();
+
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
@@ -262,6 +276,49 @@ class PackageManagerService extends IPackageManager.Stub {
ComponentName mResolveComponentName;
PackageParser.Package mPlatformPackage;
+ // Set of pending broadcasts for aggregating enable/disable of components.
+ final HashMap<String, String> mPendingBroadcasts = new HashMap<String, String>();
+ static final int SEND_PENDING_BROADCAST = 1;
+ // Delay time in millisecs
+ static final int BROADCAST_DELAY = 10 * 1000;
+
+ class PackageHandler extends Handler {
+ PackageHandler(Looper looper) {
+ super(looper);
+ }
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SEND_PENDING_BROADCAST : {
+ int size = 0;
+ String broadcastList[];
+ HashMap<String, String> tmpMap;
+ int uids[];
+ synchronized (mPackages) {
+ size = mPendingBroadcasts.size();
+ if (size <= 0) {
+ // Nothing to be done. Just return
+ return;
+ }
+ broadcastList = new String[size];
+ mPendingBroadcasts.keySet().toArray(broadcastList);
+ tmpMap = new HashMap<String, String>(mPendingBroadcasts);
+ uids = new int[size];
+ for (int i = 0; i < size; i++) {
+ PackageSetting ps = mSettings.mPackages.get(mPendingBroadcasts.get(broadcastList[i]));
+ uids[i] = (ps != null) ? ps.userId : -1;
+ }
+ mPendingBroadcasts.clear();
+ }
+ // Send broadcasts
+ for (int i = 0; i < size; i++) {
+ String className = broadcastList[i];
+ sendPackageChangedBroadcast(className, true, tmpMap.get(className), uids[i]);
+ }
+ break;
+ }
+ }
+ }
+ }
public static final IPackageManager main(Context context, boolean factoryTest) {
PackageManagerService m = new PackageManagerService(context, factoryTest);
ServiceManager.addService("package", m);
@@ -309,6 +366,10 @@ class PackageManagerService extends IPackageManager.Stub {
MULTIPLE_APPLICATION_UIDS
? RADIO_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
+ mSettings.addSharedUserLP("android.uid.log",
+ MULTIPLE_APPLICATION_UIDS
+ ? LOG_UID : FIRST_APPLICATION_UID,
+ ApplicationInfo.FLAG_SYSTEM);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -345,7 +406,7 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) {
synchronized (mPackages) {
mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ mHandler = new PackageHandler(mHandlerThread.getLooper());
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
@@ -378,8 +439,11 @@ class PackageManagerService extends IPackageManager.Stub {
final HashSet<String> libFiles = new HashSet<String>();
mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
+ mDalvikCacheDir = new File(dataDir, "dalvik-cache");
if (mInstaller != null) {
+ boolean didDexOpt = false;
+
/**
* Out of paranoia, ensure that everything in the boot class
* path has been dexed.
@@ -392,6 +456,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
libFiles.add(paths[i]);
mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
+ didDexOpt = true;
}
} catch (FileNotFoundException e) {
Log.w(TAG, "Boot class path not found: " + paths[i]);
@@ -414,6 +479,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
libFiles.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
+ didDexOpt = true;
}
} catch (FileNotFoundException e) {
Log.w(TAG, "Library not found: " + lib);
@@ -433,7 +499,7 @@ class PackageManagerService extends IPackageManager.Stub {
* run from a non-root shell.
*/
String[] frameworkFiles = mFrameworkDir.list();
- if (frameworkFiles != null && mInstaller != null) {
+ if (frameworkFiles != null) {
for (int i=0; i<frameworkFiles.length; i++) {
File libPath = new File(mFrameworkDir, frameworkFiles[i]);
String path = libPath.getPath();
@@ -448,6 +514,7 @@ class PackageManagerService extends IPackageManager.Stub {
try {
if (dalvik.system.DexFile.isDexOptNeeded(path)) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true);
+ didDexOpt = true;
}
} catch (FileNotFoundException e) {
Log.w(TAG, "Jar not found: " + path);
@@ -456,6 +523,25 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ if (didDexOpt) {
+ // If we had to do a dexopt of one of the previous
+ // things, then something on the system has changed.
+ // Consider this significant, and wipe away all other
+ // existing dexopt files to ensure we don't leave any
+ // dangling around.
+ String[] files = mDalvikCacheDir.list();
+ if (files != null) {
+ for (int i=0; i<files.length; i++) {
+ String fn = files[i];
+ if (fn.startsWith("data@app@")
+ || fn.startsWith("data@app-private@")) {
+ Log.i(TAG, "Pruning dalvik file: " + fn);
+ (new File(mDalvikCacheDir, fn)).delete();
+ }
+ }
+ }
+ }
}
mFrameworkInstallObserver = new AppDirObserver(
@@ -581,6 +667,27 @@ 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) {
@@ -670,8 +777,22 @@ class PackageManagerService extends IPackageManager.Stub {
Log.w(TAG, "<library> without file at "
+ parser.getPositionDescription());
} else {
- Log.i(TAG, "Got library " + lname + " in " + lfile);
- this.mSharedLibraries.put(lname, lfile);
+ //Log.i(TAG, "Got library " + lname + " in " + lfile);
+ mSharedLibraries.put(lname, lfile);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
+ } else if ("feature".equals(name)) {
+ String fname = parser.getAttributeValue(null, "name");
+ if (fname == null) {
+ Log.w(TAG, "<feature> without name at "
+ + parser.getPositionDescription());
+ } else {
+ //Log.i(TAG, "Got feature " + fname);
+ FeatureInfo fi = new FeatureInfo();
+ fi.name = fname;
+ mAvailableFeatures.put(fname, fi);
}
XmlUtils.skipCurrentTag(parser);
continue;
@@ -751,6 +872,10 @@ 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);
+ }
final PackageSetting ps = (PackageSetting)p.mExtras;
if (ps == null) {
return null;
@@ -1001,16 +1126,40 @@ class PackageManagerService extends IPackageManager.Stub {
Set<String> libSet;
synchronized (mPackages) {
libSet = mSharedLibraries.keySet();
+ int size = libSet.size();
+ if (size > 0) {
+ String[] libs = new String[size];
+ libSet.toArray(libs);
+ return libs;
+ }
}
- int size = libSet.size();
- if (size > 0) {
- String[] libs = new String[size];
- libSet.toArray(libs);
- return libs;
+ return null;
+ }
+
+ public FeatureInfo[] getSystemAvailableFeatures() {
+ Collection<FeatureInfo> featSet;
+ synchronized (mPackages) {
+ featSet = mAvailableFeatures.values();
+ int size = featSet.size();
+ if (size > 0) {
+ FeatureInfo[] features = new FeatureInfo[size+1];
+ featSet.toArray(features);
+ FeatureInfo fi = new FeatureInfo();
+ fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
+ FeatureInfo.GL_ES_VERSION_UNDEFINED);
+ features[size] = fi;
+ return features;
+ }
}
return null;
}
+ public boolean hasSystemFeature(String name) {
+ synchronized (mPackages) {
+ return mAvailableFeatures.containsKey(name);
+ }
+ }
+
public int checkPermission(String permName, String pkgName) {
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(pkgName);
@@ -1138,25 +1287,57 @@ class PackageManagerService extends IPackageManager.Stub {
|| p2 == null || p2.mExtras == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- return checkSignaturesLP(p1, p2);
+ return checkSignaturesLP(p1.mSignatures, p2.mSignatures);
+ }
+ }
+
+ public int checkUidSignatures(int uid1, int uid2) {
+ synchronized (mPackages) {
+ Signature[] s1;
+ Signature[] s2;
+ Object obj = mSettings.getUserIdLP(uid1);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ s1 = ((SharedUserSetting)obj).signatures.mSignatures;
+ } else if (obj instanceof PackageSetting) {
+ s1 = ((PackageSetting)obj).signatures.mSignatures;
+ } else {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ } else {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ obj = mSettings.getUserIdLP(uid2);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ s2 = ((SharedUserSetting)obj).signatures.mSignatures;
+ } else if (obj instanceof PackageSetting) {
+ s2 = ((PackageSetting)obj).signatures.mSignatures;
+ } else {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ } else {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ return checkSignaturesLP(s1, s2);
}
}
- int checkSignaturesLP(PackageParser.Package p1, PackageParser.Package p2) {
- if (p1.mSignatures == null) {
- return p2.mSignatures == null
+ int checkSignaturesLP(Signature[] s1, Signature[] s2) {
+ if (s1 == null) {
+ return s2 == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
: PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
}
- if (p2.mSignatures == null) {
+ if (s2 == null) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
}
- final int N1 = p1.mSignatures.length;
- final int N2 = p2.mSignatures.length;
+ final int N1 = s1.length;
+ final int N2 = s2.length;
for (int i=0; i<N1; i++) {
boolean match = false;
for (int j=0; j<N2; j++) {
- if (p1.mSignatures[i].equals(p2.mSignatures[j])) {
+ if (s1[i].equals(s2[j])) {
match = true;
break;
}
@@ -1677,6 +1858,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ /**
+ * @deprecated
+ */
public void querySyncProviders(List outNames, List outInfo) {
synchronized (mPackages) {
Iterator<Map.Entry<String, PackageParser.Provider>> i
@@ -1817,7 +2001,6 @@ class PackageManagerService extends IPackageManager.Stub {
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser(scanFile.getPath());
pp.setSeparateProcesses(mSeparateProcesses);
- pp.setSdkVersion(mSdkVersion, mSdkCodename);
final PackageParser.Package pkg = pp.parsePackage(scanFile,
destCodeFile.getAbsolutePath(), mMetrics, parseFlags);
if (pkg == null) {
@@ -1841,27 +2024,25 @@ class PackageManagerService extends IPackageManager.Stub {
}
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
// Check for updated system applications here
- if (updatedPkg != null) {
- if ((ps != null) && (!ps.codePath.getPath().equals(scanFile.getPath()))) {
- if (pkg.mVersionCode <= ps.versionCode) {
- // The system package has been updated and the code path does not match
- // Ignore entry. Just return
- Log.w(TAG, "Package:" + pkg.packageName +
- " has been updated. Ignoring the one from path:"+scanFile);
- mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
- return null;
- } else {
- // Delete the older apk pointed to by ps
- // At this point, its safely assumed that package installation for
- // apps in system partition will go through. If not there won't be a working
- // version of the app
- synchronized (mPackages) {
- // Just remove the loaded entries from package lists.
- mPackages.remove(ps.name);
- }
- deletePackageResourcesLI(ps.name, ps.codePathString, ps.resourcePathString);
- mSettings.enableSystemPackageLP(ps.name);
+ if ((ps != null) && (!ps.codePath.equals(scanFile))) {
+ if (pkg.mVersionCode < ps.versionCode) {
+ // The system package has been updated and the code path does not match
+ // Ignore entry. Just return
+ Log.w(TAG, "Package:" + pkg.packageName +
+ " has been updated. Ignoring the one from path:"+scanFile);
+ mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
+ return null;
+ } else {
+ // Delete the older apk pointed to by ps
+ // At this point, its safely assumed that package installation for
+ // apps in system partition will go through. If not there won't be a working
+ // version of the app
+ synchronized (mPackages) {
+ // Just remove the loaded entries from package lists.
+ mPackages.remove(ps.name);
}
+ deletePackageResourcesLI(ps.name, ps.codePathString, ps.resourcePathString);
+ mSettings.enableSystemPackageLP(ps.name);
}
}
}
@@ -1871,7 +2052,7 @@ class PackageManagerService extends IPackageManager.Stub {
scanMode |= SCAN_FORWARD_LOCKED;
}
File resFile = destResourceFile;
- if ((scanMode & SCAN_FORWARD_LOCKED) != 0) {
+ if (ps != null && ((scanMode & SCAN_FORWARD_LOCKED) != 0)) {
resFile = getFwdLockedResource(ps.name);
}
// Note that we invoke the following method only if we are about to unpack an application
@@ -2030,17 +2211,62 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
// Check all shared libraries and map to their actual file path.
- if (pkg.usesLibraryFiles != null) {
- for (int i=0; i<pkg.usesLibraryFiles.length; i++) {
- String file = mSharedLibraries.get(pkg.usesLibraryFiles[i]);
+ if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
+ if (mTmpSharedLibraries == null ||
+ mTmpSharedLibraries.length < mSharedLibraries.size()) {
+ mTmpSharedLibraries = new String[mSharedLibraries.size()];
+ }
+ int num = 0;
+ int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
+ for (int i=0; i<N; i++) {
+ String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
if (file == null) {
Log.e(TAG, "Package " + pkg.packageName
+ " requires unavailable shared library "
- + pkg.usesLibraryFiles[i] + "; ignoring!");
+ + pkg.usesLibraries.get(i) + "; failing!");
mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
return null;
}
- pkg.usesLibraryFiles[i] = file;
+ mTmpSharedLibraries[num] = file;
+ num++;
+ }
+ N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
+ for (int i=0; i<N; i++) {
+ String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
+ if (file == null) {
+ Log.w(TAG, "Package " + pkg.packageName
+ + " desires unavailable shared library "
+ + pkg.usesOptionalLibraries.get(i) + "; ignoring!");
+ } else {
+ mTmpSharedLibraries[num] = file;
+ num++;
+ }
+ }
+ if (num > 0) {
+ pkg.usesLibraryFiles = new String[num];
+ System.arraycopy(mTmpSharedLibraries, 0,
+ pkg.usesLibraryFiles, 0, num);
+ }
+
+ if (pkg.reqFeatures != null) {
+ N = pkg.reqFeatures.size();
+ for (int i=0; i<N; i++) {
+ FeatureInfo fi = pkg.reqFeatures.get(i);
+ if ((fi.flags&FeatureInfo.FLAG_REQUIRED) == 0) {
+ // Don't care.
+ continue;
+ }
+
+ if (fi.name != null) {
+ if (mAvailableFeatures.get(fi.name) == null) {
+ Log.e(TAG, "Package " + pkg.packageName
+ + " requires unavailable feature "
+ + fi.name + "; failing!");
+ mLastScanError = PackageManager.INSTALL_FAILED_MISSING_FEATURE;
+ return null;
+ }
+ }
+ }
}
}
@@ -2904,9 +3130,9 @@ class PackageManagerService extends IPackageManager.Stub {
allowed = true;
} else if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
|| p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- allowed = (checkSignaturesLP(p.owner, pkg)
+ allowed = (checkSignaturesLP(p.owner.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
- || (checkSignaturesLP(mPlatformPackage, pkg)
+ || (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
@@ -3439,9 +3665,18 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- PackageInstalledInfo res;
- synchronized (mInstallLock) {
- res = installPackageLI(packageURI, flags, true, installerPackageName);
+ // Result object to be returned
+ PackageInstalledInfo res = new PackageInstalledInfo();
+ res.returnCode = PackageManager.INSTALL_SUCCEEDED;
+ res.uid = -1;
+ res.pkg = null;
+ res.removedInfo = new PackageRemovedInfo();
+ // Make a temporary copy of file from given packageURI
+ File tmpPackageFile = copyTempInstallFile(packageURI, res);
+ if (tmpPackageFile != null) {
+ synchronized (mInstallLock) {
+ installPackageLI(packageURI, flags, true, installerPackageName, tmpPackageFile, res);
+ }
}
if (observer != null) {
try {
@@ -3553,7 +3788,8 @@ class PackageManagerService extends IPackageManager.Stub {
// First find the old package info and check signatures
synchronized(mPackages) {
oldPackage = mPackages.get(pkgName);
- if(checkSignaturesLP(pkg, oldPackage) != PackageManager.SIGNATURE_MATCH) {
+ if(checkSignaturesLP(pkg.mSignatures, oldPackage.mSignatures)
+ != PackageManager.SIGNATURE_MATCH) {
res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
return;
}
@@ -3626,14 +3862,13 @@ class PackageManagerService extends IPackageManager.Stub {
final ApplicationInfo deletedPackageAppInfo = deletedPackage.applicationInfo;
final ApplicationInfo installedPackageAppInfo =
newPackage.applicationInfo;
- if (!deletedPackageAppInfo.sourceDir
- .equals(installedPackageAppInfo.sourceDir)) {
- new File(deletedPackageAppInfo.sourceDir).delete();
- }
- if (!deletedPackageAppInfo.publicSourceDir
- .equals(installedPackageAppInfo.publicSourceDir)) {
- new File(deletedPackageAppInfo.publicSourceDir).delete();
- }
+ deletePackageResourcesLI(pkgName,
+ !deletedPackageAppInfo.sourceDir
+ .equals(installedPackageAppInfo.sourceDir)
+ ? deletedPackageAppInfo.sourceDir : null,
+ !deletedPackageAppInfo.publicSourceDir
+ .equals(installedPackageAppInfo.publicSourceDir)
+ ? deletedPackageAppInfo.publicSourceDir : null);
//update signature on the new package setting
//this should always succeed, since we checked the
//signature earlier.
@@ -3655,11 +3890,30 @@ class PackageManagerService extends IPackageManager.Stub {
// Since we failed to install the new package we need to restore the old
// package that we deleted.
if(deletedPkg) {
+ File restoreFile = new File(deletedPackage.mPath);
+ if (restoreFile == null) {
+ Log.e(TAG, "Failed allocating storage when restoring pkg : " + pkgName);
+ return;
+ }
+ File restoreTmpFile = createTempPackageFile();
+ if (restoreTmpFile == null) {
+ Log.e(TAG, "Failed creating temp file when restoring pkg : " + pkgName);
+ return;
+ }
+ if (!FileUtils.copyFile(restoreFile, restoreTmpFile)) {
+ Log.e(TAG, "Failed copying temp file when restoring pkg : " + pkgName);
+ return;
+ }
+ PackageInstalledInfo restoreRes = new PackageInstalledInfo();
+ restoreRes.removedInfo = new PackageRemovedInfo();
installPackageLI(
- Uri.fromFile(new File(deletedPackage.mPath)),
+ Uri.fromFile(restoreFile),
isForwardLocked(deletedPackage)
? PackageManager.INSTALL_FORWARD_LOCK
- : 0, false, oldInstallerPackageName);
+ : 0, false, oldInstallerPackageName, restoreTmpFile, restoreRes);
+ if (restoreRes.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ Log.e(TAG, "Failed restoring pkg : " + pkgName + " after failed upgrade");
+ }
}
}
}
@@ -3822,50 +4076,36 @@ class PackageManagerService extends IPackageManager.Stub {
return new File(mAppInstallDir, publicZipFileName);
}
- private PackageInstalledInfo installPackageLI(Uri pPackageURI,
- int pFlags, boolean newInstall, String installerPackageName) {
- File tmpPackageFile = null;
- String pkgName = null;
- boolean forwardLocked = false;
- boolean replacingExistingPackage = false;
- // Result object to be returned
- PackageInstalledInfo res = new PackageInstalledInfo();
- res.returnCode = PackageManager.INSTALL_SUCCEEDED;
- res.uid = -1;
- res.pkg = null;
- res.removedInfo = new PackageRemovedInfo();
+ private File copyTempInstallFile(Uri pPackageURI,
+ PackageInstalledInfo res) {
+ File tmpPackageFile = createTempPackageFile();
+ int retCode = PackageManager.INSTALL_SUCCEEDED;
+ if (tmpPackageFile == null) {
+ res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ return null;
+ }
- main_flow: try {
- tmpPackageFile = createTempPackageFile();
- if (tmpPackageFile == null) {
- res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- break main_flow;
+ if (pPackageURI.getScheme().equals("file")) {
+ final File srcPackageFile = new File(pPackageURI.getPath());
+ // We copy the source package file to a temp file and then rename it to the
+ // destination file in order to eliminate a window where the package directory
+ // scanner notices the new package file but it's not completely copied yet.
+ if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) {
+ Log.e(TAG, "Couldn't copy package file to temp file.");
+ retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
- tmpPackageFile.deleteOnExit(); // paranoia
- if (pPackageURI.getScheme().equals("file")) {
- final File srcPackageFile = new File(pPackageURI.getPath());
- // We copy the source package file to a temp file and then rename it to the
- // destination file in order to eliminate a window where the package directory
- // scanner notices the new package file but it's not completely copied yet.
- if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) {
- Log.e(TAG, "Couldn't copy package file to temp file.");
- res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- break main_flow;
- }
- } else if (pPackageURI.getScheme().equals("content")) {
- ParcelFileDescriptor fd;
- try {
- fd = mContext.getContentResolver().openFileDescriptor(pPackageURI, "r");
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Couldn't open file descriptor from download service.");
- res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- break main_flow;
- }
- if (fd == null) {
- Log.e(TAG, "Couldn't open file descriptor from download service (null).");
- res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- break main_flow;
- }
+ } else if (pPackageURI.getScheme().equals("content")) {
+ ParcelFileDescriptor fd = null;
+ try {
+ fd = mContext.getContentResolver().openFileDescriptor(pPackageURI, "r");
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e);
+ retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ }
+ if (fd == null) {
+ Log.e(TAG, "Couldn't open file descriptor from download service (null).");
+ retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ } else {
if (Config.LOGV) {
Log.v(TAG, "Opened file descriptor from download service.");
}
@@ -3876,14 +4116,34 @@ class PackageManagerService extends IPackageManager.Stub {
// scanner notices the new package file but it's not completely copied yet.
if (!FileUtils.copyToFile(dlStream, tmpPackageFile)) {
Log.e(TAG, "Couldn't copy package stream to temp file.");
- res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- break main_flow;
+ retCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
- } else {
- Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
- res.returnCode = PackageManager.INSTALL_FAILED_INVALID_URI;
- break main_flow;
}
+ } else {
+ Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
+ retCode = PackageManager.INSTALL_FAILED_INVALID_URI;
+ }
+
+ res.returnCode = retCode;
+ if (retCode != PackageManager.INSTALL_SUCCEEDED) {
+ if (tmpPackageFile != null && tmpPackageFile.exists()) {
+ tmpPackageFile.delete();
+ }
+ return null;
+ }
+ return tmpPackageFile;
+ }
+
+ private void installPackageLI(Uri pPackageURI,
+ int pFlags, boolean newInstall, String installerPackageName,
+ File tmpPackageFile, PackageInstalledInfo res) {
+ String pkgName = null;
+ boolean forwardLocked = false;
+ boolean replacingExistingPackage = false;
+ // Result object to be returned
+ res.returnCode = PackageManager.INSTALL_SUCCEEDED;
+
+ main_flow: try {
pkgName = PackageParser.parsePackageName(
tmpPackageFile.getAbsolutePath(), 0);
if (pkgName == null) {
@@ -3911,7 +4171,6 @@ class PackageManagerService extends IPackageManager.Stub {
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser(tmpPackageFile.getPath());
pp.setSeparateProcesses(mSeparateProcesses);
- pp.setSdkVersion(mSdkVersion, mSdkCodename);
final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
destPackageFile.getAbsolutePath(), mMetrics, parseFlags);
if (pkg == null) {
@@ -3955,7 +4214,6 @@ class PackageManagerService extends IPackageManager.Stub {
tmpPackageFile.delete();
}
}
- return res;
}
private int setPermissionsLI(String pkgName,
@@ -4292,22 +4550,30 @@ class PackageManagerService extends IPackageManager.Stub {
private void deletePackageResourcesLI(String packageName,
String sourceDir, String publicSourceDir) {
- File sourceFile = new File(sourceDir);
- if (!sourceFile.exists()) {
- Log.w(TAG, "Package source " + sourceDir + " does not exist.");
- }
- // Delete application's code and resources
- sourceFile.delete();
- final File publicSourceFile = new File(publicSourceDir);
- if (publicSourceFile.exists()) {
- publicSourceFile.delete();
+ if (sourceDir != null) {
+ File sourceFile = new File(sourceDir);
+ if (!sourceFile.exists()) {
+ Log.w(TAG, "Package source " + sourceDir + " does not exist.");
+ }
+ // Delete application's code and resources
+ sourceFile.delete();
+ if (mInstaller != null) {
+ int retCode = mInstaller.rmdex(sourceFile.toString());
+ if (retCode < 0) {
+ Log.w(TAG, "Couldn't remove dex file for package: "
+ + packageName + " at location "
+ + sourceFile.toString() + ", retcode=" + retCode);
+ // we don't consider this to be a failure of the core package deletion
+ }
+ }
}
- if (mInstaller != null) {
- int retCode = mInstaller.rmdex(sourceFile.toString());
- if (retCode < 0) {
- Log.w(TAG, "Couldn't remove dex file for package: "
- + packageName + " at location " + sourceFile.toString() + ", retcode=" + retCode);
- // we don't consider this to be a failure of the core package deletion
+ if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) {
+ final File publicSourceFile = new File(publicSourceDir);
+ if (!publicSourceFile.exists()) {
+ Log.w(TAG, "Package public source " + publicSourceFile + " does not exist.");
+ }
+ if (publicSourceFile.exists()) {
+ publicSourceFile.delete();
}
}
}
@@ -4743,7 +5009,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
private void setEnabledSetting(
- final String packageNameStr, String classNameStr, int newState, final int flags) {
+ final String packageName, String className, int newState, final int flags) {
if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
|| newState == COMPONENT_ENABLED_STATE_ENABLED
|| newState == COMPONENT_ENABLED_STATE_DISABLED)) {
@@ -4755,17 +5021,20 @@ class PackageManagerService extends IPackageManager.Stub {
final int permission = mContext.checkCallingPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ boolean sendNow = false;
+ boolean isApp = (className == null);
+ String key = isApp ? packageName : className;
int packageUid = -1;
synchronized (mPackages) {
- pkgSetting = mSettings.mPackages.get(packageNameStr);
+ pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
- if (classNameStr == null) {
+ if (className == null) {
throw new IllegalArgumentException(
- "Unknown package: " + packageNameStr);
+ "Unknown package: " + packageName);
}
throw new IllegalArgumentException(
- "Unknown component: " + packageNameStr
- + "/" + classNameStr);
+ "Unknown component: " + packageName
+ + "/" + className);
}
if (!allowedByPermission && (uid != pkgSetting.userId)) {
throw new SecurityException(
@@ -4773,41 +5042,67 @@ class PackageManagerService extends IPackageManager.Stub {
+ Binder.getCallingPid()
+ ", uid=" + uid + ", package uid=" + pkgSetting.userId);
}
- packageUid = pkgSetting.userId;
- if (classNameStr == null) {
+ if (className == null) {
// We're dealing with an application/package level state change
pkgSetting.enabled = newState;
} else {
// We're dealing with a component level state change
switch (newState) {
case COMPONENT_ENABLED_STATE_ENABLED:
- pkgSetting.enableComponentLP(classNameStr);
+ pkgSetting.enableComponentLP(className);
break;
case COMPONENT_ENABLED_STATE_DISABLED:
- pkgSetting.disableComponentLP(classNameStr);
+ pkgSetting.disableComponentLP(className);
break;
case COMPONENT_ENABLED_STATE_DEFAULT:
- pkgSetting.restoreComponentLP(classNameStr);
+ pkgSetting.restoreComponentLP(className);
break;
default:
Log.e(TAG, "Invalid new component state: " + newState);
+ return;
}
}
mSettings.writeLP();
+ packageUid = pkgSetting.userId;
+ if ((flags&PackageManager.DONT_KILL_APP) == 0) {
+ sendNow = true;
+ // Purge entry from pending broadcast list if another one exists already
+ // since we are sending one right away.
+ if (mPendingBroadcasts.get(key) != null) {
+ mPendingBroadcasts.remove(key);
+ // Can ignore empty list since its handled in the handler anyway
+ }
+ } else {
+ if (mPendingBroadcasts.get(key) == null) {
+ mPendingBroadcasts.put(key, packageName);
+ }
+ if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
+ // Schedule a message
+ mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
+ }
+ }
}
-
+
long callingId = Binder.clearCallingIdentity();
try {
- Bundle extras = new Bundle(2);
- extras.putBoolean(Intent.EXTRA_DONT_KILL_APP,
- (flags&PackageManager.DONT_KILL_APP) != 0);
- extras.putInt(Intent.EXTRA_UID, packageUid);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageNameStr, extras);
+ if (sendNow) {
+ sendPackageChangedBroadcast(packageName,
+ (flags&PackageManager.DONT_KILL_APP) != 0, key, packageUid);
+ }
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
+ private void sendPackageChangedBroadcast(String packageName,
+ boolean killFlag, String componentName, int packageUid) {
+ Bundle extras = new Bundle(2);
+ extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentName);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
+ extras.putInt(Intent.EXTRA_UID, packageUid);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras);
+ }
+
public String getInstallerPackageName(String packageName) {
synchronized (mPackages) {
PackageSetting pkg = mSettings.mPackages.get(packageName);
@@ -5029,6 +5324,15 @@ class PackageManagerService extends IPackageManager.Stub {
pw.println("Settings parse messages:");
pw.println(mSettings.mReadMessages.toString());
}
+
+ synchronized (mProviders) {
+ pw.println(" ");
+ pw.println("Registered ContentProviders:");
+ for (PackageParser.Provider p : mProviders.values()) {
+ pw.println(" ["); pw.println(p.info.authority); pw.println("]: ");
+ pw.println(p.toString());
+ }
+ }
}
static final class BasePermission {
@@ -5475,7 +5779,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
static class GrantedPermissions {
- final int pkgFlags;
+ int pkgFlags;
HashSet<String> grantedPermissions = new HashSet<String>();
int[] gids;
@@ -5893,10 +6197,10 @@ class PackageManagerService extends IPackageManager.Stub {
// Let the app continue with previous uid if code path changes.
reportSettingsProblem(Log.WARN,
"Package " + name + " codePath changed from " + p.codePath
- + " to " + codePath + "; Retaining data and using new code from " +
- codePath);
+ + " to " + codePath + "; Retaining data and using new");
}
- } else if (p.sharedUser != sharedUser) {
+ }
+ if (p.sharedUser != sharedUser) {
reportSettingsProblem(Log.WARN,
"Package " + name + " shared user changed from "
+ (p.sharedUser != null ? p.sharedUser.name : "<nothing>")
@@ -5904,6 +6208,13 @@ class PackageManagerService extends IPackageManager.Stub {
+ (sharedUser != null ? sharedUser.name : "<nothing>")
+ "; replacing with new");
p = null;
+ } else {
+ if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // If what we are scanning is a system package, then
+ // make it so, regardless of whether it was previously
+ // installed only in the data partition.
+ p.pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ }
}
}
if (p == null) {
@@ -5964,14 +6275,14 @@ class PackageManagerService extends IPackageManager.Stub {
// Update code path if needed
if (!codePath.toString().equalsIgnoreCase(p.codePathString)) {
Log.w(TAG, "Code path for pkg : " + p.pkg.packageName +
- " changing form " + p.codePathString + " to " + codePath);
+ " changing from " + p.codePathString + " to " + codePath);
p.codePath = codePath;
p.codePathString = codePath.toString();
}
//Update resource path if needed
if (!resourcePath.toString().equalsIgnoreCase(p.resourcePathString)) {
Log.w(TAG, "Resource path for pkg : " + p.pkg.packageName +
- " changing form " + p.resourcePathString + " to " + resourcePath);
+ " changing from " + p.resourcePathString + " to " + resourcePath);
p.resourcePath = resourcePath;
p.resourcePathString = resourcePath.toString();
}
@@ -6033,7 +6344,9 @@ class PackageManagerService extends IPackageManager.Stub {
continue;
}
for (PackageSetting pkg:sus.packages) {
- if (pkg.pkg.requestedPermissions.contains(eachPerm)) {
+ if (pkg.pkg != null &&
+ !pkg.pkg.packageName.equalsIgnoreCase(deletedPs.pkg.packageName) &&
+ pkg.pkg.requestedPermissions.contains(eachPerm)) {
used = true;
break;
}
@@ -6048,7 +6361,9 @@ class PackageManagerService extends IPackageManager.Stub {
int newGids[] = globalGids;
for (String eachPerm : sus.grantedPermissions) {
BasePermission bp = mPermissions.get(eachPerm);
- newGids = appendInts(newGids, bp.gids);
+ if (bp != null) {
+ newGids = appendInts(newGids, bp.gids);
+ }
}
sus.gids = newGids;
}
@@ -6129,10 +6444,18 @@ class PackageManagerService extends IPackageManager.Stub {
// Keep the old settings around until we know the new ones have
// been successfully written.
if (mSettingsFilename.exists()) {
- if (mBackupSettingsFilename.exists()) {
- mBackupSettingsFilename.delete();
+ // Presence of backup settings file indicates that we failed
+ // to persist settings earlier. So preserve the older
+ // backup for future reference since the current settings
+ // might have been corrupted.
+ if (!mBackupSettingsFilename.exists()) {
+ if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {
+ Log.w(TAG, "Unable to backup package manager settings, current changes will be lost at reboot");
+ return;
+ }
+ } else {
+ Log.w(TAG, "Preserving older settings backup");
}
- mSettingsFilename.renameTo(mBackupSettingsFilename);
}
mPastSignatures.clear();
@@ -6217,15 +6540,19 @@ class PackageManagerService extends IPackageManager.Stub {
|FileUtils.S_IRGRP|FileUtils.S_IWGRP
|FileUtils.S_IROTH,
-1, -1);
+ return;
} catch(XmlPullParserException e) {
Log.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e);
-
} catch(java.io.IOException e) {
Log.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e);
-
}
-
+ // Clean up partially written file
+ if (mSettingsFilename.exists()) {
+ if (!mSettingsFilename.delete()) {
+ Log.i(TAG, "Failed to clean up mangled file: " + mSettingsFilename);
+ }
+ }
//Debug.stopMethodTracing();
}
@@ -6394,6 +6721,13 @@ class PackageManagerService extends IPackageManager.Stub {
str = new FileInputStream(mBackupSettingsFilename);
mReadMessages.append("Reading from backup settings file\n");
Log.i(TAG, "Reading from backup settings file!");
+ if (mSettingsFilename.exists()) {
+ // If both the backup and settings file exist, we
+ // ignore the settings since it might have been
+ // corrupted.
+ Log.w(TAG, "Cleaning up settings file " + mSettingsFilename);
+ mSettingsFilename.delete();
+ }
} catch (java.io.IOException e) {
// We'll try for the normal settings file.
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 79d78ad..e1425d4 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -28,7 +28,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.database.Cursor;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
@@ -48,6 +53,8 @@ import android.util.Log;
import android.view.WindowManagerPolicy;
import static android.provider.Settings.System.DIM_SCREEN;
import static android.provider.Settings.System.SCREEN_BRIGHTNESS;
+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;
@@ -58,7 +65,8 @@ import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
-class PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor {
+class PowerManagerService extends IPowerManager.Stub
+ implements LocalPowerManager, Watchdog.Monitor {
private static final String TAG = "PowerManagerService";
static final String PARTIAL_NAME = "PowerManagerService";
@@ -72,7 +80,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK
| PowerManager.SCREEN_DIM_WAKE_LOCK
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK
- | PowerManager.FULL_WAKE_LOCK;
+ | PowerManager.FULL_WAKE_LOCK
+ | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
// time since last state: time since last event:
// The short keylight delay comes from Gservices; this is the default.
@@ -81,6 +90,15 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec
private static final int LONG_DIM_TIME = 7000; // t+N-5 sec
+ // How long to wait to debounce light sensor changes.
+ private static final int LIGHT_SENSOR_DELAY = 2000;
+
+ // For debouncing the proximity sensor.
+ private static final int PROXIMITY_SENSOR_DELAY = 1000;
+
+ // trigger proximity if distance is less than 5 cm
+ private static final float PROXIMITY_THRESHOLD = 5.0f;
+
// Cached Gservices settings; see updateGservicesValues()
private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;
@@ -116,6 +134,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
static final boolean ANIMATE_KEYBOARD_LIGHTS = false;
static final int ANIM_STEPS = 60/4;
+ // Slower animation for autobrightness changes
+ static final int AUTOBRIGHTNESS_ANIM_STEPS = 60;
// These magic numbers are the initial state of the LEDs at boot. Ideally
// we should read them from the driver, but our current hardware returns 0
@@ -143,6 +163,11 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
private int mUserState;
private boolean mKeyboardVisible = false;
private boolean mUserActivityAllowed = true;
+ private int mProximityWakeLockCount = 0;
+ private boolean mProximitySensorEnabled = false;
+ private boolean mProximitySensorActive = false;
+ private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active
+ private long mLastProximityEventTime;
private int mTotalDelaySetting;
private int mKeylightDelay;
private int mDimDelay;
@@ -175,16 +200,34 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
private IActivityManager mActivityService;
private IBatteryStats mBatteryStats;
private BatteryService mBatteryService;
+ private SensorManager mSensorManager;
+ private Sensor mProximitySensor;
+ private Sensor mLightSensor;
+ private boolean mLightSensorEnabled;
+ private float mLightSensorValue = -1;
+ private float mLightSensorPendingValue = -1;
+ private int mLightSensorBrightness = -1;
private boolean mDimScreen = true;
private long mNextTimeout;
private volatile int mPokey = 0;
private volatile boolean mPokeAwakeOnSet = false;
private volatile boolean mInitComplete = false;
private HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>();
+ // mScreenOnTime and mScreenOnStartTime are used for computing total time screen
+ // has been on since boot
private long mScreenOnTime;
private long mScreenOnStartTime;
+ // mLastScreenOnTime is the time the screen was last turned on
+ private long mLastScreenOnTime;
private boolean mPreventScreenOn;
private int mScreenBrightnessOverride = -1;
+ private boolean mUseSoftwareAutoBrightness;
+ private boolean mAutoBrightessEnabled;
+ private int[] mAutoBrightnessLevels;
+ private int[] mLcdBacklightValues;
+ private int[] mButtonBacklightValues;
+ private int[] mKeyboardBacklightValues;
+ private int mLightSensorWarmupTime;
// Used when logging number and duration of touch-down cycles
private long mTotalTouchDownTime;
@@ -193,6 +236,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
// 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 mDebugLightSensor = (false || mSpew);
/*
static PrintStream mLog;
@@ -290,10 +335,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
// temporarily set mUserActivityAllowed to true so this will work
// even when the keyguard is on.
synchronized (mLocks) {
- boolean savedActivityAllowed = mUserActivityAllowed;
- mUserActivityAllowed = true;
- userActivity(SystemClock.uptimeMillis(), false);
- mUserActivityAllowed = savedActivityAllowed;
+ forceUserActivityLocked();
}
}
}
@@ -333,6 +375,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
// DIM_SCREEN
//mDimScreen = getInt(DIM_SCREEN) != 0;
+ // SCREEN_BRIGHTNESS_MODE
+ setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE));
+
// recalculate everything
setScreenOffTimeoutsLocked();
}
@@ -404,12 +449,32 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- ContentResolver resolver = mContext.getContentResolver();
+ Resources resources = mContext.getResources();
+
+ // read settings for auto-brightness
+ mUseSoftwareAutoBrightness = resources.getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available);
+ if (mUseSoftwareAutoBrightness) {
+ mAutoBrightnessLevels = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevels);
+ mLcdBacklightValues = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+ mButtonBacklightValues = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessButtonBacklightValues);
+ mKeyboardBacklightValues = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessKeyboardBacklightValues);
+ mLightSensorWarmupTime = resources.getInteger(
+ com.android.internal.R.integer.config_lightSensorWarmupTime);
+ }
+
+ ContentResolver resolver = mContext.getContentResolver();
Cursor settingsCursor = resolver.query(Settings.System.CONTENT_URI, null,
"(" + 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},
+ new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN,
+ SCREEN_BRIGHTNESS_MODE},
null);
mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler);
SettingsObserver settingsObserver = new SettingsObserver();
@@ -430,9 +495,14 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
// And explicitly do the initial update of our cached settings
updateGservicesValues();
- // turn everything on
- setPowerState(ALL_BRIGHT);
-
+ if (mUseSoftwareAutoBrightness) {
+ // turn the screen on
+ setPowerState(SCREEN_BRIGHT);
+ } else {
+ // turn everything on
+ setPowerState(ALL_BRIGHT);
+ }
+
synchronized (mHandlerThread) {
mInitComplete = true;
mHandlerThread.notifyAll();
@@ -527,7 +597,11 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
switch (wl.flags & LOCK_MASK)
{
case PowerManager.FULL_WAKE_LOCK:
- wl.minState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
+ if (mUseSoftwareAutoBrightness) {
+ wl.minState = SCREEN_BRIGHT;
+ } else {
+ wl.minState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
+ }
break;
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
wl.minState = SCREEN_BRIGHT;
@@ -536,6 +610,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
wl.minState = SCREEN_DIM;
break;
case PowerManager.PARTIAL_WAKE_LOCK:
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
break;
default:
// just log and bail. we're in the server, so don't
@@ -583,6 +658,11 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
}
}
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;
@@ -639,6 +719,18 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
if (LOG_PARTIAL_WL) EventLog.writeEvent(LOG_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) {
+ // wait for proximity sensor to go negative before disabling sensor
+ if (mDebugProximitySensor) {
+ Log.d(TAG, "waiting for proximity sensor to go negative");
+ }
+ } else {
+ disableProximityLockLocked();
+ }
+ }
}
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
@@ -745,15 +837,17 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
switch (type)
{
case PowerManager.FULL_WAKE_LOCK:
- return "FULL_WAKE_LOCK ";
+ return "FULL_WAKE_LOCK ";
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
- return "SCREEN_BRIGHT_WAKE_LOCK";
+ return "SCREEN_BRIGHT_WAKE_LOCK ";
case PowerManager.SCREEN_DIM_WAKE_LOCK:
- return "SCREEN_DIM_WAKE_LOCK ";
+ return "SCREEN_DIM_WAKE_LOCK ";
case PowerManager.PARTIAL_WAKE_LOCK:
- return "PARTIAL_WAKE_LOCK ";
+ return "PARTIAL_WAKE_LOCK ";
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
default:
- return "??? ";
+ return "??? ";
}
}
@@ -808,10 +902,21 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
pw.println(" mPreventScreenOn=" + mPreventScreenOn
+ " mScreenBrightnessOverride=" + mScreenBrightnessOverride);
pw.println(" mTotalDelaySetting=" + mTotalDelaySetting);
+ pw.println(" mLastScreenOnTime=" + mLastScreenOnTime);
pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock);
pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);
pw.println(" mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);
pw.println(" mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock);
+ pw.println(" mProximityWakeLockCount=" + mProximityWakeLockCount);
+ pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
+ pw.println(" mProximitySensorActive=" + mProximitySensorActive);
+ pw.println(" mProximityPendingValue=" + mProximityPendingValue);
+ pw.println(" mLastProximityEventTime=" + mLastProximityEventTime);
+ pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
+ pw.println(" mLightSensorValue=" + mLightSensorValue);
+ pw.println(" mLightSensorPendingValue=" + mLightSensorPendingValue);
+ pw.println(" mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness);
+ pw.println(" mAutoBrightessEnabled=" + mAutoBrightessEnabled);
mScreenBrightness.dump(pw, " mScreenBrightness: ");
mKeyboardBrightness.dump(pw, " mKeyboardBrightness: ");
mButtonBrightness.dump(pw, " mButtonBrightness: ");
@@ -1136,7 +1241,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
// Finally, set the flag that prevents the screen from turning on.
// (Below, in setPowerState(), we'll check mPreventScreenOn and
- // we *won't* call Power.setScreenState(true) if it's set.)
+ // we *won't* call setScreenStateLocked(true) if it's set.)
mPreventScreenOn = true;
} else {
// (Re)enable the screen.
@@ -1154,9 +1259,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
Log.d(TAG,
"preventScreenOn: turning on after a prior preventScreenOn(true)!");
}
- int err = Power.setScreenState(true);
+ int err = setScreenStateLocked(true);
if (err != 0) {
- Log.w(TAG, "preventScreenOn: error from Power.setScreenState(): " + err);
+ Log.w(TAG, "preventScreenOn: error from setScreenStateLocked(): " + err);
}
}
@@ -1211,6 +1316,30 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
}
};
+ private int setScreenStateLocked(boolean on) {
+ int err = Power.setScreenState(on);
+ if (err == 0) {
+ mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0);
+ if (mUseSoftwareAutoBrightness) {
+ enableLightSensor(on);
+ if (!on) {
+ // make sure button and key backlights are off too
+ int brightnessMode = (mUseSoftwareAutoBrightness
+ ? HardwareService.BRIGHTNESS_MODE_SENSOR
+ : HardwareService.BRIGHTNESS_MODE_USER);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, 0,
+ brightnessMode);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, 0,
+ brightnessMode);
+ // clear current value so we will update based on the new conditions
+ // when the sensor is reenabled.
+ mLightSensorValue = -1;
+ }
+ }
+ }
+ return err;
+ }
+
private void setPowerState(int state)
{
setPowerState(state, false, false);
@@ -1230,6 +1359,10 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
if (noChangeLights) {
newState = (newState & ~LIGHTS_MASK) | (mPowerState & LIGHTS_MASK);
}
+ if (mProximitySensorActive) {
+ // don't turn on the screen when the proximity sensor lock is held
+ newState = (newState & ~SCREEN_BRIGHT);
+ }
if (batteryIsLow()) {
newState |= BATTERY_LOW_BIT;
@@ -1239,15 +1372,15 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
if (newState == mPowerState) {
return;
}
-
- if (!mDoneBooting) {
+
+ if (!mDoneBooting && !mUseSoftwareAutoBrightness) {
newState |= ALL_BRIGHT;
}
boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0;
boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0;
- if (mSpew) {
+ if (mPowerState != newState) {
Log.d(TAG, "setPowerState: mPowerState=" + mPowerState
+ " newState=" + newState + " noChangeLights=" + noChangeLights);
Log.d(TAG, " oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0)
@@ -1295,7 +1428,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
reallyTurnScreenOn = false;
}
if (reallyTurnScreenOn) {
- err = Power.setScreenState(true);
+ err = setScreenStateLocked(true);
long identity = Binder.clearCallingIdentity();
try {
mBatteryStats.noteScreenBrightness(
@@ -1307,7 +1440,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
Binder.restoreCallingIdentity(identity);
}
} else {
- Power.setScreenState(false);
+ setScreenStateLocked(false);
// But continue as if we really did turn the screen on...
err = 0;
}
@@ -1323,6 +1456,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
sendNotificationLocked(true, -1);
}
} else {
+ // cancel light sensor task
+ mHandler.removeCallbacks(mAutoBrightnessTask);
mScreenOffTime = SystemClock.elapsedRealtime();
long identity = Binder.clearCallingIdentity();
try {
@@ -1352,7 +1487,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
EventLog.writeEvent(LOG_POWER_SCREEN_STATE, 0, becauseOfUser ? 1 : 0,
mTotalTouchDownTime, mTouchCycles);
mLastTouchDown = 0;
- int err = Power.setScreenState(false);
+ int err = setScreenStateLocked(false);
if (mScreenOnStartTime != 0) {
mScreenOnTime += SystemClock.elapsedRealtime() - mScreenOnStartTime;
mScreenOnStartTime = 0;
@@ -1499,9 +1634,10 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
} finally {
Binder.restoreCallingIdentity(identity);
}
- mScreenBrightness.setTargetLocked(brightness,
- steps, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue);
- startAnimation = true;
+ 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
@@ -1549,14 +1685,23 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
}
private void setLightBrightness(int mask, int value) {
+ int brightnessMode = (mAutoBrightessEnabled
+ ? HardwareService.BRIGHTNESS_MODE_SENSOR
+ : HardwareService.BRIGHTNESS_MODE_USER);
if ((mask & SCREEN_BRIGHT_BIT) != 0) {
- mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, value);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, value,
+ brightnessMode);
}
+ brightnessMode = (mUseSoftwareAutoBrightness
+ ? HardwareService.BRIGHTNESS_MODE_SENSOR
+ : HardwareService.BRIGHTNESS_MODE_USER);
if ((mask & BUTTON_BRIGHT_BIT) != 0) {
- mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, value);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, value,
+ brightnessMode);
}
if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
- mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, value);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, value,
+ brightnessMode);
}
}
@@ -1580,11 +1725,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
+ " delta=" + delta);
}
- void setTargetLocked(int target, int stepsToTarget, int initialValue,
+ boolean setTargetLocked(int target, int stepsToTarget, int initialValue,
int nominalCurrentValue) {
if (!initialized) {
initialized = true;
curValue = (float)initialValue;
+ } else if (targetValue == target) {
+ return false;
}
targetValue = target;
delta = (targetValue -
@@ -1598,6 +1745,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
+ noticeMe);
}
animating = true;
+ return true;
}
boolean stepLocked() {
@@ -1657,6 +1805,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
try {
if (mScreenBrightnessOverride >= 0) {
return mScreenBrightnessOverride;
+ } else if (mLightSensorBrightness >= 0 && mUseSoftwareAutoBrightness
+ && mAutoBrightessEnabled) {
+ return mLightSensorBrightness;
}
final int brightness = Settings.System.getInt(mContext.getContentResolver(),
SCREEN_BRIGHTNESS);
@@ -1667,18 +1818,31 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
}
}
- boolean screenIsOn() {
+ public boolean isScreenOn() {
synchronized (mLocks) {
return (mPowerState & SCREEN_ON_BIT) != 0;
}
}
- boolean screenIsBright() {
+ boolean isScreenBright() {
synchronized (mLocks) {
return (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT;
}
}
+ private boolean isScreenTurningOffLocked() {
+ return (mScreenBrightness.animating && mScreenBrightness.targetValue == 0);
+ }
+
+ private void forceUserActivityLocked() {
+ // cancel animation so userActivity will succeed
+ mScreenBrightness.animating = false;
+ boolean savedActivityAllowed = mUserActivityAllowed;
+ mUserActivityAllowed = true;
+ userActivity(SystemClock.uptimeMillis(), false);
+ mUserActivityAllowed = savedActivityAllowed;
+ }
+
public void userActivityWithForce(long time, boolean noChangeLights, boolean force) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
userActivity(time, noChangeLights, OTHER_EVENT, force);
@@ -1712,7 +1876,6 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
return;
}
-
if (false) {
if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)) {
Log.d(TAG, "userActivity !!!");//, new RuntimeException());
@@ -1726,13 +1889,21 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
Log.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time
+ " mUserActivityAllowed=" + mUserActivityAllowed
+ " mUserState=0x" + Integer.toHexString(mUserState)
- + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
+ + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
+ + " mProximitySensorActive=" + mProximitySensorActive
+ + " force=" + force);
+ }
+ // ignore user activity if we are in the process of turning off the screen
+ if (isScreenTurningOffLocked()) {
+ Log.d(TAG, "ignoring user activity while turning off screen");
+ return;
}
if (mLastEventTime <= time || force) {
mLastEventTime = time;
- if (mUserActivityAllowed || force) {
- // Only turn on button backlights if a button was pressed.
- if (eventType == BUTTON_EVENT) {
+ if ((mUserActivityAllowed && !mProximitySensorActive) || force) {
+ // Only turn on button backlights if a button was pressed
+ // and auto brightness is disabled
+ if (eventType == BUTTON_EVENT && !mUseSoftwareAutoBrightness) {
mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
} else {
// don't clear button/keyboard backlights when the screen is touched.
@@ -1757,6 +1928,122 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
}
}
+ private int getAutoBrightnessValue(int sensorValue, int[] values) {
+ try {
+ int i;
+ for (i = 0; i < mAutoBrightnessLevels.length; i++) {
+ if (sensorValue < mAutoBrightnessLevels[i]) {
+ break;
+ }
+ }
+ return values[i];
+ } catch (Exception e) {
+ // guard against null pointer or index out of bounds errors
+ Log.e(TAG, "getAutoBrightnessValue", e);
+ return 255;
+ }
+ }
+
+ private Runnable mProximityTask = new Runnable() {
+ public void run() {
+ synchronized (mLocks) {
+ if (mProximityPendingValue != -1) {
+ proximityChangedLocked(mProximityPendingValue == 1);
+ mProximityPendingValue = -1;
+ }
+ }
+ }
+ };
+
+ private Runnable mAutoBrightnessTask = new Runnable() {
+ public void run() {
+ synchronized (mLocks) {
+ int value = (int)mLightSensorPendingValue;
+ if (value >= 0) {
+ mLightSensorPendingValue = -1;
+ lightSensorChangedLocked(value);
+ }
+ }
+ }
+ };
+
+ private void lightSensorChangedLocked(int value) {
+ if (mDebugLightSensor) {
+ Log.d(TAG, "lightSensorChangedLocked " + value);
+ }
+
+ if (mLightSensorValue != value) {
+ mLightSensorValue = value;
+ if ((mPowerState & BATTERY_LOW_BIT) == 0) {
+ int lcdValue = getAutoBrightnessValue(value, mLcdBacklightValues);
+ int buttonValue = getAutoBrightnessValue(value, mButtonBacklightValues);
+ int keyboardValue;
+ if (mKeyboardVisible) {
+ keyboardValue = getAutoBrightnessValue(value, mKeyboardBacklightValues);
+ } else {
+ keyboardValue = 0;
+ }
+ mLightSensorBrightness = lcdValue;
+
+ if (mDebugLightSensor) {
+ Log.d(TAG, "lcdValue " + lcdValue);
+ Log.d(TAG, "buttonValue " + buttonValue);
+ Log.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
+ ? HardwareService.BRIGHTNESS_MODE_SENSOR
+ : HardwareService.BRIGHTNESS_MODE_USER);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT,
+ lcdValue, brightnessMode);
+ }
+ }
+ if (ANIMATE_BUTTON_LIGHTS) {
+ if (mButtonBrightness.setTargetLocked(buttonValue,
+ AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
+ (int)mButtonBrightness.curValue)) {
+ startAnimation = true;
+ }
+ } else {
+ int brightnessMode = (mUseSoftwareAutoBrightness
+ ? HardwareService.BRIGHTNESS_MODE_SENSOR
+ : HardwareService.BRIGHTNESS_MODE_USER);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS,
+ buttonValue, brightnessMode);
+ }
+ if (ANIMATE_KEYBOARD_LIGHTS) {
+ if (mKeyboardBrightness.setTargetLocked(keyboardValue,
+ AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
+ (int)mKeyboardBrightness.curValue)) {
+ startAnimation = true;
+ }
+ } else {
+ int brightnessMode = (mUseSoftwareAutoBrightness
+ ? HardwareService.BRIGHTNESS_MODE_SENSOR
+ : HardwareService.BRIGHTNESS_MODE_USER);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD,
+ keyboardValue, brightnessMode);
+ }
+ if (startAnimation) {
+ if (mDebugLightSensor) {
+ Log.i(TAG, "lightSensorChangedLocked scheduling light animator");
+ }
+ mHandler.removeCallbacks(mLightAnimator);
+ mHandler.post(mLightAnimator);
+ }
+ }
+ }
+ }
+
/**
* The user requested that we go to sleep (probably with the power button).
* This overrides all wake locks that are held.
@@ -1816,16 +2103,60 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
}
public void setKeyboardVisibility(boolean visible) {
- mKeyboardVisible = visible;
+ synchronized (mLocks) {
+ if (mSpew) {
+ Log.d(TAG, "setKeyboardVisibility: " + visible);
+ }
+ if (mKeyboardVisible != visible) {
+ mKeyboardVisible = visible;
+ // don't signal user activity if the screen is off; other code
+ // will take care of turning on due to a true change to the lid
+ // switch and synchronized with the lock screen.
+ if ((mPowerState & SCREEN_ON_BIT) != 0) {
+ if (mUseSoftwareAutoBrightness) {
+ // force recompute of backlight values
+ if (mLightSensorValue >= 0) {
+ int value = (int)mLightSensorValue;
+ mLightSensorValue = -1;
+ lightSensorChangedLocked(value);
+ }
+ }
+ userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);
+ }
+ }
+ }
}
/**
* When the keyguard is up, it manages the power state, and userActivity doesn't do anything.
+ * When disabling user activity we also reset user power state so the keyguard can reset its
+ * short screen timeout when keyguard is unhidden.
*/
public void enableUserActivity(boolean enabled) {
+ if (mSpew) {
+ Log.d(TAG, "enableUserActivity " + enabled);
+ }
synchronized (mLocks) {
mUserActivityAllowed = enabled;
- mLastEventTime = SystemClock.uptimeMillis(); // we might need to pass this in
+ if (!enabled) {
+ // cancel timeout and clear mUserState so the keyguard can set a short timeout
+ setTimeoutLocked(SystemClock.uptimeMillis(), 0);
+ }
+ }
+ }
+
+ private void setScreenBrightnessMode(int mode) {
+ boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) {
+ mAutoBrightessEnabled = enabled;
+ if (isScreenOn()) {
+ // force recompute of backlight values
+ if (mLightSensorValue >= 0) {
+ int value = (int)mLightSensorValue;
+ mLightSensorValue = -1;
+ lightSensorChangedLocked(value);
+ }
+ }
}
}
@@ -1975,6 +2306,14 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
}
void systemReady() {
+ mSensorManager = new SensorManager(mHandlerThread.getLooper());
+ mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ // don't bother with the light sensor if auto brightness is handled in hardware
+ if (mUseSoftwareAutoBrightness) {
+ mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ enableLightSensor(true);
+ }
+
synchronized (mLocks) {
Log.d(TAG, "system ready!");
mDoneBooting = true;
@@ -1996,4 +2335,206 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
public void monitor() {
synchronized (mLocks) { }
}
+
+ public int getSupportedWakeLockFlags() {
+ int result = PowerManager.PARTIAL_WAKE_LOCK
+ | PowerManager.FULL_WAKE_LOCK
+ | PowerManager.SCREEN_DIM_WAKE_LOCK;
+
+ if (mProximitySensor != null) {
+ result |= PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
+ }
+
+ return result;
+ }
+
+ 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);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, brightness,
+ HardwareService.BRIGHTNESS_MODE_USER);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD,
+ (mKeyboardVisible ? brightness : 0), HardwareService.BRIGHTNESS_MODE_USER);
+ mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, brightness,
+ HardwareService.BRIGHTNESS_MODE_USER);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteScreenBrightness(brightness);
+ } catch (RemoteException e) {
+ Log.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;
+ }
+ }
+
+ private void enableProximityLockLocked() {
+ if (mDebugProximitySensor) {
+ Log.d(TAG, "enableProximityLockLocked");
+ }
+ if (!mProximitySensorEnabled) {
+ // clear calling identity so sensor manager battery stats are accurate
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mSensorManager.registerListener(mProximityListener, mProximitySensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ mProximitySensorEnabled = true;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ private void disableProximityLockLocked() {
+ if (mDebugProximitySensor) {
+ Log.d(TAG, "disableProximityLockLocked");
+ }
+ if (mProximitySensorEnabled) {
+ // clear calling identity so sensor manager battery stats are accurate
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mSensorManager.unregisterListener(mProximityListener);
+ mHandler.removeCallbacks(mProximityTask);
+ mProximitySensorEnabled = false;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ if (mProximitySensorActive) {
+ mProximitySensorActive = false;
+ forceUserActivityLocked();
+ }
+ }
+ }
+
+ private void proximityChangedLocked(boolean active) {
+ if (mDebugProximitySensor) {
+ Log.d(TAG, "proximityChangedLocked, active: " + active);
+ }
+ if (!mProximitySensorEnabled) {
+ Log.d(TAG, "Ignoring proximity change after sensor is disabled");
+ return;
+ }
+ if (active) {
+ goToSleepLocked(SystemClock.uptimeMillis());
+ 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 (mProximityWakeLockCount == 0) {
+ // disable sensor if we have no listeners left after proximity negative
+ disableProximityLockLocked();
+ }
+ }
+ }
+
+ private void enableLightSensor(boolean enable) {
+ if (mDebugLightSensor) {
+ Log.d(TAG, "enableLightSensor " + enable);
+ }
+ if (mSensorManager != null && mLightSensorEnabled != enable) {
+ mLightSensorEnabled = enable;
+ // clear calling identity so sensor manager battery stats are accurate
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (enable) {
+ mSensorManager.registerListener(mLightListener, mLightSensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ } else {
+ mSensorManager.unregisterListener(mLightListener);
+ mHandler.removeCallbacks(mAutoBrightnessTask);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ SensorEventListener mProximityListener = new SensorEventListener() {
+ public void onSensorChanged(SensorEvent event) {
+ long milliseconds = SystemClock.elapsedRealtime();
+ synchronized (mLocks) {
+ float distance = event.values[0];
+ long timeSinceLastEvent = milliseconds - mLastProximityEventTime;
+ mLastProximityEventTime = milliseconds;
+ mHandler.removeCallbacks(mProximityTask);
+
+ // compare against getMaximumRange to support sensors that only return 0 or 1
+ boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD &&
+ distance < mProximitySensor.getMaximumRange());
+
+ if (mDebugProximitySensor) {
+ Log.d(TAG, "mProximityListener.onSensorChanged active: " + active);
+ }
+ if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) {
+ // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing
+ mProximityPendingValue = (active ? 1 : 0);
+ mHandler.postDelayed(mProximityTask, PROXIMITY_SENSOR_DELAY - timeSinceLastEvent);
+ } else {
+ // process the value immediately
+ mProximityPendingValue = -1;
+ proximityChangedLocked(active);
+ }
+ }
+ }
+
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // ignore
+ }
+ };
+
+ SensorEventListener mLightListener = new SensorEventListener() {
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (mLocks) {
+ // ignore light sensor while screen is turning off
+ if (isScreenTurningOffLocked()) {
+ return;
+ }
+
+ int value = (int)event.values[0];
+ long milliseconds = SystemClock.elapsedRealtime();
+ if (mDebugLightSensor) {
+ Log.d(TAG, "onSensorChanged: light value: " + value);
+ }
+ mHandler.removeCallbacks(mAutoBrightnessTask);
+ if (mLightSensorValue != value) {
+ if (mLightSensorValue == -1 ||
+ milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
+ // process the value immediately if screen has just turned on
+ lightSensorChangedLocked(value);
+ } else {
+ // delay processing to debounce the sensor
+ mLightSensorPendingValue = value;
+ mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
+ }
+ } else {
+ mLightSensorPendingValue = -1;
+ }
+ }
+ }
+
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // ignore
+ }
+ };
}
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index af80e20..ac3b723 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -30,6 +30,7 @@ import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.StringTokenizer;
public class ProcessStats {
private static final String TAG = "ProcessStats";
@@ -138,7 +139,22 @@ public class ProcessStats {
private boolean mFirst = true;
private byte[] mBuffer = new byte[256];
-
+
+ /**
+ * The time in microseconds that the CPU has been running at each speed.
+ */
+ private long[] mCpuSpeedTimes;
+
+ /**
+ * The relative time in microseconds that the CPU has been running at each speed.
+ */
+ private long[] mRelCpuSpeedTimes;
+
+ /**
+ * The different speeds that the CPU can be running at.
+ */
+ private long[] mCpuSpeeds;
+
public static class Stats {
public final int pid;
final String statFile;
@@ -460,6 +476,70 @@ public class ProcessStats {
return 0;
}
+ /**
+ * Returns the times spent at each CPU speed, since the last call to this method. If this
+ * is the first time, it will return 1 for each value.
+ * @return relative times spent at different speed steps.
+ */
+ public long[] getLastCpuSpeedTimes() {
+ if (mCpuSpeedTimes == null) {
+ mCpuSpeedTimes = getCpuSpeedTimes(null);
+ mRelCpuSpeedTimes = new long[mCpuSpeedTimes.length];
+ for (int i = 0; i < mCpuSpeedTimes.length; i++) {
+ mRelCpuSpeedTimes[i] = 1; // Initialize
+ }
+ } else {
+ getCpuSpeedTimes(mRelCpuSpeedTimes);
+ for (int i = 0; i < mCpuSpeedTimes.length; i++) {
+ long temp = mRelCpuSpeedTimes[i];
+ mRelCpuSpeedTimes[i] -= mCpuSpeedTimes[i];
+ mCpuSpeedTimes[i] = temp;
+ }
+ }
+ return mRelCpuSpeedTimes;
+ }
+
+ private long[] getCpuSpeedTimes(long[] out) {
+ long[] tempTimes = out;
+ long[] tempSpeeds = mCpuSpeeds;
+ final int MAX_SPEEDS = 20;
+ if (out == null) {
+ tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that
+ tempSpeeds = new long[MAX_SPEEDS];
+ }
+ int speed = 0;
+ String file = readFile("/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state", '\0');
+ // Note: file may be null on kernels without cpufreq (i.e. the emulator's)
+ if (file != null) {
+ StringTokenizer st = new StringTokenizer(file, "\n ");
+ while (st.hasMoreElements()) {
+ String token = st.nextToken();
+ try {
+ long val = Long.parseLong(token);
+ tempSpeeds[speed] = val;
+ token = st.nextToken();
+ val = Long.parseLong(token);
+ tempTimes[speed] = val;
+ speed++;
+ if (speed == MAX_SPEEDS) break; // No more
+ if (localLOGV && out == null) {
+ Log.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1]
+ + "\t" + tempTimes[speed - 1]);
+ }
+ } catch (NumberFormatException nfe) {
+ Log.i(TAG, "Unable to parse time_in_state");
+ }
+ }
+ }
+ if (out == null) {
+ out = new long[speed];
+ mCpuSpeeds = new long[speed];
+ System.arraycopy(tempSpeeds, 0, mCpuSpeeds, 0, speed);
+ System.arraycopy(tempTimes, 0, out, 0, speed);
+ }
+ return out;
+ }
+
final public int getLastUserTime() {
return mRelUserTime;
}
diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java
index 4ac1c6e..f7847ec 100644
--- a/services/java/com/android/server/RandomBlock.java
+++ b/services/java/com/android/server/RandomBlock.java
@@ -32,13 +32,14 @@ import java.io.RandomAccessFile;
class RandomBlock {
private static final String TAG = "RandomBlock";
+ private static final boolean DEBUG = false;
private static final int BLOCK_SIZE = 4096;
private byte[] block = new byte[BLOCK_SIZE];
private RandomBlock() { }
static RandomBlock fromFile(String filename) throws IOException {
- Log.v(TAG, "reading from file " + filename);
+ if (DEBUG) Log.v(TAG, "reading from file " + filename);
InputStream stream = null;
try {
stream = new FileInputStream(filename);
@@ -62,7 +63,7 @@ class RandomBlock {
}
void toFile(String filename) throws IOException {
- Log.v(TAG, "writing to file " + filename);
+ if (DEBUG) Log.v(TAG, "writing to file " + filename);
RandomAccessFile out = null;
try {
out = new RandomAccessFile(filename, "rws");
diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java
index ceef39f..4dfeb9d 100644
--- a/services/java/com/android/server/SensorService.java
+++ b/services/java/com/android/server/SensorService.java
@@ -84,12 +84,16 @@ class SensorService extends ISensorService.Stub {
if (hasSensor(sensor)) {
removeSensor(sensor);
try {
- deactivateIfUnused(sensor);
+ deactivateIfUnusedLocked(sensor);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in binderDied");
}
}
}
+ if (mListeners.size() == 0) {
+ _sensors_control_wake();
+ _sensors_control_close();
+ }
mListeners.notify();
}
}
@@ -102,9 +106,12 @@ class SensorService extends ISensorService.Stub {
}
public Bundle getDataChannel() throws RemoteException {
- return _sensors_control_open();
+ // 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) Log.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable);
@@ -163,7 +170,7 @@ class SensorService extends ISensorService.Stub {
l.addSensor(sensor, enable);
} else {
l.removeSensor(sensor);
- deactivateIfUnused(sensor);
+ deactivateIfUnusedLocked(sensor);
if (l.mSensors == 0) {
mListeners.remove(l);
binder.unlinkToDeath(l, 0);
@@ -173,12 +180,13 @@ class SensorService extends ISensorService.Stub {
if (mListeners.size() == 0) {
_sensors_control_wake();
+ _sensors_control_close();
}
}
return true;
}
- void deactivateIfUnused(int sensor) throws RemoteException {
+ private void deactivateIfUnusedLocked(int sensor) throws RemoteException {
int size = mListeners.size();
for (int i=0 ; i<size ; i++) {
if (mListeners.get(i).hasSensor(sensor))
@@ -187,10 +195,11 @@ class SensorService extends ISensorService.Stub {
_sensors_control_activate(sensor, false);
}
- ArrayList<Listener> mListeners = new ArrayList<Listener>();
+ 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/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java
new file mode 100644
index 0000000..7f0e90d
--- /dev/null
+++ b/services/java/com/android/server/ShutdownActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import com.android.internal.app.ShutdownThread;
+
+public class ShutdownActivity extends Activity {
+
+ private static final String TAG = "ShutdownActivity";
+ private boolean mConfirm;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ Log.i(TAG, "onCreate(): confirm=" + mConfirm);
+
+ Handler h = new Handler();
+ h.post(new Runnable() {
+ public void run() {
+ ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
+ }
+ });
+ }
+}
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
new file mode 100644
index 0000000..8903ebd
--- /dev/null
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.backup.AbsoluteFileBackupHelper;
+import android.backup.BackupDataInput;
+import android.backup.BackupDataInputStream;
+import android.backup.BackupDataOutput;
+import android.backup.BackupHelper;
+import android.backup.BackupHelperAgent;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.ServiceManager;
+import android.os.SystemService;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Backup agent for various system-managed data, currently just the system wallpaper
+ */
+public class SystemBackupAgent extends BackupHelperAgent {
+ private static final String TAG = "SystemBackupAgent";
+
+ // These paths must match what the WallpaperManagerService uses
+ private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper";
+ private static final String WALLPAPER_INFO = "/data/system/wallpaper_info.xml";
+
+ @Override
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) throws IOException {
+ // We only back up the data under the current "wallpaper" schema with metadata
+ addHelper("wallpaper", new AbsoluteFileBackupHelper(SystemBackupAgent.this,
+ new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }));
+ super.onBackup(oldState, data, newState);
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ // On restore, we also support a previous data schema "system_files"
+ addHelper("wallpaper", new AbsoluteFileBackupHelper(SystemBackupAgent.this,
+ new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }));
+ addHelper("system_files", new AbsoluteFileBackupHelper(SystemBackupAgent.this,
+ new String[] { WALLPAPER_IMAGE }));
+
+ boolean success = false;
+ try {
+ super.onRestore(data, appVersionCode, newState);
+
+ WallpaperManagerService wallpaper = (WallpaperManagerService)ServiceManager.getService(
+ Context.WALLPAPER_SERVICE);
+ wallpaper.settingsRestored();
+ } catch (IOException ex) {
+ // If there was a failure, delete everything for the wallpaper, this is too aggresive,
+ // but this is hopefully a rare failure.
+ Log.d(TAG, "restore failed", ex);
+ (new File(WALLPAPER_IMAGE)).delete();
+ (new File(WALLPAPER_INFO)).delete();
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b2848ac..b8cf844 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -18,10 +18,12 @@ package com.android.server;
import com.android.server.am.ActivityManagerService;
import com.android.server.status.StatusBarService;
+import com.android.internal.os.SamplingProfilerIntegration;
import dalvik.system.VMRuntime;
import android.app.ActivityManagerNative;
+import android.bluetooth.BluetoothAdapter;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentService;
@@ -31,23 +33,22 @@ import android.content.pm.IPackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.media.AudioService;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
+import android.os.*;
import android.provider.Contacts.People;
import android.provider.Settings;
import android.server.BluetoothA2dpService;
-import android.server.BluetoothDeviceService;
+import android.server.BluetoothService;
import android.server.search.SearchManagerService;
import android.util.EventLog;
import android.util.Log;
+import android.accounts.AccountManagerService;
+
+import java.util.Timer;
+import java.util.TimerTask;
class ServerThread extends Thread {
private static final String TAG = "SystemServer";
private final static boolean INCLUDE_DEMO = false;
- private final static boolean INCLUDE_BACKUP = false;
private static final int LOG_BOOT_PROGRESS_SYSTEM_RUN = 3010;
@@ -84,31 +85,34 @@ class ServerThread extends Thread {
HardwareService hardware = null;
PowerManagerService power = null;
+ BatteryService battery = null;
+ ConnectivityService connectivity = null;
IPackageManager pm = null;
Context context = null;
WindowManagerService wm = null;
- BluetoothDeviceService bluetooth = null;
+ BluetoothService bluetooth = null;
BluetoothA2dpService bluetoothA2dp = null;
HeadsetObserver headset = null;
+ DockObserver dock = null;
// Critical services...
try {
- Log.i(TAG, "Starting Entropy Service.");
+ Log.i(TAG, "Entropy Service");
ServiceManager.addService("entropy", new EntropyService());
- Log.i(TAG, "Starting Power Manager.");
+ Log.i(TAG, "Power Manager");
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);
- Log.i(TAG, "Starting Activity Manager.");
+ Log.i(TAG, "Activity Manager");
context = ActivityManagerService.main(factoryTest);
- Log.i(TAG, "Starting telephony registry");
+ Log.i(TAG, "Telephony Registry");
ServiceManager.addService("telephony.registry", new TelephonyRegistry(context));
AttributeCache.init(context);
- Log.i(TAG, "Starting Package Manager.");
+ Log.i(TAG, "Package Manager");
pm = PackageManagerService.main(context,
factoryTest != SystemServer.FACTORY_TEST_OFF);
@@ -116,18 +120,27 @@ class ServerThread extends Thread {
mContentResolver = context.getContentResolver();
- Log.i(TAG, "Starting Content Manager.");
+ // The AccountManager must come before the ContentService
+ try {
+ Log.i(TAG, "Account Manager");
+ ServiceManager.addService(Context.ACCOUNT_SERVICE,
+ new AccountManagerService(context));
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting Account Manager", e);
+ }
+
+ Log.i(TAG, "Content Manager");
ContentService.main(context,
factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);
- Log.i(TAG, "Starting System Content Providers.");
+ Log.i(TAG, "System Content Providers");
ActivityManagerService.installSystemProviders();
- Log.i(TAG, "Starting Battery Service.");
- BatteryService battery = new BatteryService(context);
+ Log.i(TAG, "Battery Service");
+ battery = new BatteryService(context);
ServiceManager.addService("battery", battery);
- Log.i(TAG, "Starting Hardware Service.");
+ Log.i(TAG, "Hardware Service");
hardware = new HardwareService(context);
ServiceManager.addService("hardware", hardware);
@@ -135,18 +148,19 @@ class ServerThread extends Thread {
// hardware service, content providers and the battery service.
power.init(context, hardware, ActivityManagerService.getDefault(), battery);
- Log.i(TAG, "Starting Alarm Manager.");
+ Log.i(TAG, "Alarm Manager");
AlarmManagerService alarm = new AlarmManagerService(context);
ServiceManager.addService(Context.ALARM_SERVICE, alarm);
+ Log.i(TAG, "Init Watchdog");
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
// Sensor Service is needed by Window Manager, so this goes first
- Log.i(TAG, "Starting Sensor Service.");
+ Log.i(TAG, "Sensor Service");
ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));
- Log.i(TAG, "Starting Window Manager.");
+ Log.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
@@ -159,16 +173,16 @@ class ServerThread extends Thread {
// support Bluetooth - see bug 988521
if (SystemProperties.get("ro.kernel.qemu").equals("1")) {
Log.i(TAG, "Registering null Bluetooth Service (emulator)");
- ServiceManager.addService(Context.BLUETOOTH_SERVICE, null);
+ ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, null);
} else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
Log.i(TAG, "Registering null Bluetooth Service (factory test)");
- ServiceManager.addService(Context.BLUETOOTH_SERVICE, null);
+ ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, null);
} else {
- Log.i(TAG, "Starting Bluetooth Service.");
- bluetooth = new BluetoothDeviceService(context);
- bluetooth.init();
- ServiceManager.addService(Context.BLUETOOTH_SERVICE, bluetooth);
- bluetoothA2dp = new BluetoothA2dpService(context);
+ Log.i(TAG, "Bluetooth Service");
+ bluetooth = new BluetoothService(context);
+ ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth);
+ bluetooth.initAfterRegistration();
+ bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
bluetoothA2dp);
@@ -187,10 +201,11 @@ class ServerThread extends Thread {
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
NotificationManagerService notification = null;
+ WallpaperManagerService wallpaper = null;
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
- Log.i(TAG, "Starting Status Bar Service.");
+ Log.i(TAG, "Status Bar");
statusBar = new StatusBarService(context);
ServiceManager.addService("statusbar", statusBar);
} catch (Throwable e) {
@@ -198,14 +213,14 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Clipboard Service.");
+ Log.i(TAG, "Clipboard Service");
ServiceManager.addService("clipboard", new ClipboardService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Clipboard Service", e);
}
try {
- Log.i(TAG, "Starting Input Method Service.");
+ Log.i(TAG, "Input Method Service");
imm = new InputMethodManagerService(context, statusBar);
ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
} catch (Throwable e) {
@@ -213,22 +228,22 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting NetStat Service.");
+ Log.i(TAG, "NetStat Service");
ServiceManager.addService("netstat", new NetStatService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting NetStat Service", e);
}
try {
- Log.i(TAG, "Starting Connectivity Service.");
- ServiceManager.addService(Context.CONNECTIVITY_SERVICE,
- ConnectivityService.getInstance(context));
+ Log.i(TAG, "Connectivity Service");
+ connectivity = ConnectivityService.getInstance(context);
+ ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
} catch (Throwable e) {
Log.e(TAG, "Failure starting Connectivity Service", e);
}
try {
- Log.i(TAG, "Starting Accessibility Manager.");
+ Log.i(TAG, "Accessibility Manager");
ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
new AccessibilityManagerService(context));
} catch (Throwable e) {
@@ -236,7 +251,7 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Notification Manager.");
+ Log.i(TAG, "Notification Manager");
notification = new NotificationManagerService(context, statusBar, hardware);
ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification);
} catch (Throwable e) {
@@ -245,14 +260,14 @@ class ServerThread extends Thread {
try {
// MountService must start after NotificationManagerService
- Log.i(TAG, "Starting Mount Service.");
+ Log.i(TAG, "Mount Service");
ServiceManager.addService("mount", new MountService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Mount Service", e);
}
try {
- Log.i(TAG, "Starting DeviceStorageMonitor service");
+ Log.i(TAG, "Device Storage Monitor");
ServiceManager.addService(DeviceStorageMonitorService.SERVICE,
new DeviceStorageMonitorService(context));
} catch (Throwable e) {
@@ -260,14 +275,14 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Location Manager.");
+ Log.i(TAG, "Location Manager");
ServiceManager.addService(Context.LOCATION_SERVICE, new LocationManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Location Manager", e);
}
try {
- Log.i(TAG, "Starting Search Service.");
+ Log.i(TAG, "Search Service");
ServiceManager.addService( Context.SEARCH_SERVICE, new SearchManagerService(context) );
} catch (Throwable e) {
Log.e(TAG, "Failure starting Search Service", e);
@@ -279,7 +294,7 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Checkin Service.");
+ Log.i(TAG, "Checkin Service");
Intent intent = new Intent().setComponent(new ComponentName(
"com.google.android.server.checkin",
"com.google.android.server.checkin.CheckinService"));
@@ -292,21 +307,22 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Wallpaper Service");
- ServiceManager.addService(Context.WALLPAPER_SERVICE, new WallpaperService(context));
+ Log.i(TAG, "Wallpaper Service");
+ wallpaper = new WallpaperManagerService(context);
+ ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
} catch (Throwable e) {
Log.e(TAG, "Failure starting Wallpaper Service", e);
}
try {
- Log.i(TAG, "Starting Audio Service");
+ Log.i(TAG, "Audio Service");
ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Audio Service", e);
}
try {
- Log.i(TAG, "Starting HeadsetObserver");
+ Log.i(TAG, "Headset Observer");
// Listen for wired headset changes
headset = new HeadsetObserver(context);
} catch (Throwable e) {
@@ -314,16 +330,22 @@ class ServerThread extends Thread {
}
try {
- if (INCLUDE_BACKUP) {
- Log.i(TAG, "Starting Backup Service");
- ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
- }
+ Log.i(TAG, "Dock Observer");
+ // Listen for dock station changes
+ dock = new DockObserver(context, power);
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting DockObserver", e);
+ }
+
+ try {
+ Log.i(TAG, "Backup Service");
+ ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Backup Service", e);
}
try {
- Log.i(TAG, "Starting AppWidget Service");
+ Log.i(TAG, "AppWidget Service");
appWidget = new AppWidgetService(context);
ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
} catch (Throwable e) {
@@ -345,8 +367,17 @@ class ServerThread extends Thread {
mContentResolver.registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
false, new AdbSettingsObserver());
+ // Before things start rolling, be sure we have decided whether
+ // we are in safe mode.
+ final boolean safeMode = wm.detectSafeMode();
+ if (safeMode) {
+ try {
+ ActivityManagerNative.getDefault().enterSafeMode();
+ } catch (RemoteException e) {
+ }
+ }
+
// It is now time to start up the app processes...
- boolean safeMode = wm.detectSafeMode();
if (notification != null) {
notification.systemReady();
@@ -355,27 +386,45 @@ class ServerThread extends Thread {
if (statusBar != null) {
statusBar.systemReady();
}
- if (imm != null) {
- imm.systemReady();
- }
wm.systemReady();
power.systemReady();
try {
pm.systemReady();
} catch (RemoteException e) {
}
- if (appWidget != null) {
- appWidget.systemReady(safeMode);
- }
-
- // After making the following code, third party code may be running...
- try {
- ActivityManagerNative.getDefault().systemReady();
- } catch (RemoteException e) {
- }
-
- Watchdog.getInstance().start();
+ // These are needed to propagate to the runnable below.
+ final BatteryService batteryF = battery;
+ final ConnectivityService connectivityF = connectivity;
+ final DockObserver dockF = dock;
+ final AppWidgetService appWidgetF = appWidget;
+ final WallpaperManagerService wallpaperF = wallpaper;
+ final InputMethodManagerService immF = imm;
+
+ // We now tell the activity manager it is okay to run third party
+ // code. It will call back into us once it has gotten to the state
+ // where third party code can really run (but before it has actually
+ // started launching the initial applications), for us to complete our
+ // initialization.
+ ((ActivityManagerService)ActivityManagerNative.getDefault())
+ .systemReady(new Runnable() {
+ public void run() {
+ Log.i(TAG, "Making services ready");
+
+ if (batteryF != null) batteryF.systemReady();
+ if (connectivityF != null) connectivityF.systemReady();
+ if (dockF != null) dockF.systemReady();
+ Watchdog.getInstance().start();
+
+ // It is now okay to let the various system services start their
+ // third party code...
+
+ if (appWidgetF != null) appWidgetF.systemReady(safeMode);
+ if (wallpaperF != null) wallpaperF.systemReady();
+ if (immF != null) immF.systemReady();
+ }
+ });
+
Looper.loop();
Log.d(TAG, "System ServerThread is exiting!");
}
@@ -417,19 +466,33 @@ public class SystemServer
public static final int FACTORY_TEST_OFF = 0;
public static final int FACTORY_TEST_LOW_LEVEL = 1;
public static final int FACTORY_TEST_HIGH_LEVEL = 2;
-
- /**
- * This method is called from Zygote to initialize the system. This will cause the native
+
+ static Timer timer;
+ static final long SNAPSHOT_INTERVAL = 60 * 60 * 1000; // 1hr
+
+ /**
+ * 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
* up into init2() to start the Android services.
- */
+ */
native public static void init1(String[] args);
public static void main(String[] args) {
+ if (SamplingProfilerIntegration.isEnabled()) {
+ SamplingProfilerIntegration.start();
+ timer = new Timer();
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ SamplingProfilerIntegration.writeSnapshot("system_server");
+ }
+ }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
+ }
+
// The system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-
+
System.loadLibrary("android_servers");
init1(args);
}
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 9f2856c..47cb6ad 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -88,10 +88,14 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private String mDataConnectionApn = "";
+ private String[] mDataConnectionApnTypes = null;
+
private String mDataConnectionInterfaceName = "";
private Bundle mCellLocation = new Bundle();
+ private int mDataConnectionNetworkType;
+
static final int PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
PhoneStateListener.LISTEN_CALL_STATE |
@@ -107,7 +111,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
// handler before they get to app code.
TelephonyRegistry(Context context) {
- CellLocation.getEmpty().fillInNotifierBundle(mCellLocation);
+ CellLocation location = CellLocation.getEmpty();
+
+ // Note that location can be null for non-phone builds like
+ // like the generic one.
+ if (location != null) {
+ location.fillInNotifierBundle(mCellLocation);
+ }
mContext = context;
mBatteryStats = BatteryStatsService.getService();
}
@@ -179,7 +189,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
try {
- r.callback.onDataConnectionStateChanged(mDataConnectionState);
+ r.callback.onDataConnectionStateChanged(mDataConnectionState,
+ mDataConnectionNetworkType);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -337,7 +348,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String interfaceName) {
+ String reason, String apn, String[] apnTypes, String interfaceName, int networkType) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
@@ -346,12 +357,14 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mDataConnectionPossible = isDataConnectivityPossible;
mDataConnectionReason = reason;
mDataConnectionApn = apn;
+ mDataConnectionApnTypes = apnTypes;
mDataConnectionInterfaceName = interfaceName;
+ mDataConnectionNetworkType = networkType;
for (int i = mRecords.size() - 1; i >= 0; i--) {
Record r = mRecords.get(i);
if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
try {
- r.callback.onDataConnectionStateChanged(state);
+ r.callback.onDataConnectionStateChanged(state, networkType);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -359,7 +372,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- interfaceName);
+ apnTypes, interfaceName);
}
public void notifyDataConnectionFailed(String reason) {
@@ -464,7 +477,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private void broadcastServiceStateChanged(ServiceState state) {
long ident = Binder.clearCallingIdentity();
try {
- mBatteryStats.noteAirplaneMode(state.getState() == ServiceState.STATE_POWER_OFF);
+ mBatteryStats.notePhoneState(state.getState());
} catch (RemoteException re) {
// Can't do much
} finally {
@@ -517,8 +530,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
}
- private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String interfaceName) {
+ private void broadcastDataConnectionStateChanged(int state,
+ boolean isDataConnectivityPossible,
+ String reason, String apn, String[] apnTypes, String interfaceName) {
// 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.
@@ -531,6 +545,14 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
}
intent.putExtra(Phone.DATA_APN_KEY, apn);
+ String types = new String("");
+ if (apnTypes.length > 0) {
+ types = apnTypes[0];
+ for (int i = 1; i < apnTypes.length; i++) {
+ types = types+","+apnTypes[i];
+ }
+ }
+ intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
mContext.sendStickyBroadcast(intent);
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
new file mode 100644
index 0000000..4b6049f
--- /dev/null
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -0,0 +1,702 @@
+/*
+ * 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 static android.os.FileObserver.*;
+import static android.os.ParcelFileDescriptor.*;
+
+import android.app.IWallpaperManager;
+import android.app.IWallpaperManagerCallback;
+import android.app.PendingIntent;
+import android.app.WallpaperInfo;
+import android.backup.BackupManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.FileObserver;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallbackList;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.service.wallpaper.IWallpaperConnection;
+import android.service.wallpaper.IWallpaperEngine;
+import android.service.wallpaper.IWallpaperService;
+import android.service.wallpaper.WallpaperService;
+import android.util.Log;
+import android.util.Xml;
+import android.view.IWindowManager;
+import android.view.WindowManager;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import com.android.internal.service.wallpaper.ImageWallpaper;
+import com.android.internal.util.FastXmlSerializer;
+
+class WallpaperManagerService extends IWallpaperManager.Stub {
+ static final String TAG = "WallpaperService";
+ static final boolean DEBUG = false;
+
+ Object mLock = new Object();
+
+ /**
+ * Minimum time between crashes of a wallpaper service for us to consider
+ * restarting it vs. just reverting to the static wallpaper.
+ */
+ static final long MIN_WALLPAPER_CRASH_TIME = 10000;
+
+ static final File WALLPAPER_DIR = new File(
+ "/data/data/com.android.settings/files");
+ static final String WALLPAPER = "wallpaper";
+ static final File WALLPAPER_FILE = new File(WALLPAPER_DIR, WALLPAPER);
+
+ /**
+ * List of callbacks registered they should each be notified
+ * when the wallpaper is changed.
+ */
+ private final RemoteCallbackList<IWallpaperManagerCallback> mCallbacks
+ = new RemoteCallbackList<IWallpaperManagerCallback>();
+
+ /**
+ * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
+ * that the wallpaper has changed. The CREATE is triggered when there is no
+ * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
+ * everytime the wallpaper is changed.
+ */
+ private final FileObserver mWallpaperObserver = new FileObserver(
+ WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE | DELETE | DELETE_SELF) {
+ @Override
+ public void onEvent(int event, String path) {
+ if (path == null) {
+ return;
+ }
+ synchronized (mLock) {
+ // changing the wallpaper means we'll need to back up the new one
+ long origId = Binder.clearCallingIdentity();
+ BackupManager bm = new BackupManager(mContext);
+ bm.dataChanged();
+ Binder.restoreCallingIdentity(origId);
+
+ File changedFile = new File(WALLPAPER_DIR, path);
+ if (WALLPAPER_FILE.equals(changedFile)) {
+ notifyCallbacksLocked();
+ }
+ }
+ }
+ };
+
+ final Context mContext;
+ final IWindowManager mIWindowManager;
+
+ int mWidth = -1;
+ int mHeight = -1;
+ String mName = "";
+ ComponentName mWallpaperComponent;
+ WallpaperConnection mWallpaperConnection;
+ long mLastDiedTime;
+
+ class WallpaperConnection extends IWallpaperConnection.Stub
+ implements ServiceConnection {
+ final WallpaperInfo mInfo;
+ final Binder mToken = new Binder();
+ IWallpaperService mService;
+ IWallpaperEngine mEngine;
+
+ public WallpaperConnection(WallpaperInfo info) {
+ mInfo = info;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ if (mWallpaperConnection == this) {
+ mService = IWallpaperService.Stub.asInterface(service);
+ attachServiceLocked(this);
+ // XXX should probably do saveSettingsLocked() later
+ // when we have an engine, but I'm not sure about
+ // locking there and anyway we always need to be able to
+ // recover if there is something wrong.
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ mService = null;
+ mEngine = null;
+ if (mWallpaperConnection == this) {
+ Log.w(TAG, "Wallpaper service gone: " + mWallpaperComponent);
+ if ((mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
+ < SystemClock.uptimeMillis()) {
+ Log.w(TAG, "Reverting to built-in wallpaper!");
+ bindWallpaperComponentLocked(null);
+ }
+ }
+ }
+ }
+
+ public void attachEngine(IWallpaperEngine engine) {
+ mEngine = engine;
+ }
+
+ public ParcelFileDescriptor setWallpaper(String name) {
+ synchronized (mLock) {
+ if (mWallpaperConnection == this) {
+ return updateWallpaperBitmapLocked(name);
+ }
+ return null;
+ }
+ }
+ }
+
+ public WallpaperManagerService(Context context) {
+ if (DEBUG) Log.d(TAG, "WallpaperService startup");
+ mContext = context;
+ mIWindowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService(Context.WINDOW_SERVICE));
+ WALLPAPER_DIR.mkdirs();
+ loadSettingsLocked();
+ mWallpaperObserver.startWatching();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mWallpaperObserver.stopWatching();
+ }
+
+ public void systemReady() {
+ synchronized (mLock) {
+ try {
+ bindWallpaperComponentLocked(mWallpaperComponent);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failure starting previous wallpaper", e);
+ try {
+ bindWallpaperComponentLocked(null);
+ } catch (RuntimeException e2) {
+ Log.w(TAG, "Failure starting default wallpaper", e2);
+ clearWallpaperComponentLocked();
+ }
+ }
+ }
+ }
+
+ public void clearWallpaper() {
+ synchronized (mLock) {
+ File f = WALLPAPER_FILE;
+ if (f.exists()) {
+ f.delete();
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ bindWallpaperComponentLocked(null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void setDimensionHints(int width, int height) throws RemoteException {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
+
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width and height must be > 0");
+ }
+
+ synchronized (mLock) {
+ if (width != mWidth || height != mHeight) {
+ mWidth = width;
+ mHeight = height;
+ saveSettingsLocked();
+ if (mWallpaperConnection != null) {
+ if (mWallpaperConnection.mEngine != null) {
+ try {
+ mWallpaperConnection.mEngine.setDesiredSize(
+ width, height);
+ } catch (RemoteException e) {
+ }
+ notifyCallbacksLocked();
+ }
+ }
+ }
+ }
+ }
+
+ public int getWidthHint() throws RemoteException {
+ synchronized (mLock) {
+ return mWidth;
+ }
+ }
+
+ public int getHeightHint() throws RemoteException {
+ synchronized (mLock) {
+ return mHeight;
+ }
+ }
+
+ public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
+ Bundle outParams) {
+ synchronized (mLock) {
+ try {
+ if (outParams != null) {
+ outParams.putInt("width", mWidth);
+ outParams.putInt("height", mHeight);
+ }
+ mCallbacks.register(cb);
+ File f = WALLPAPER_FILE;
+ if (!f.exists()) {
+ return null;
+ }
+ return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ /* Shouldn't happen as we check to see if the file exists */
+ Log.w(TAG, "Error getting wallpaper", e);
+ }
+ return null;
+ }
+ }
+
+ public WallpaperInfo getWallpaperInfo() {
+ synchronized (mLock) {
+ if (mWallpaperConnection != null) {
+ return mWallpaperConnection.mInfo;
+ }
+ return null;
+ }
+ }
+
+ public ParcelFileDescriptor setWallpaper(String name) {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER);
+ synchronized (mLock) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name);
+ if (pfd != null) {
+ bindWallpaperComponentLocked(null);
+ saveSettingsLocked();
+ }
+ return pfd;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ ParcelFileDescriptor updateWallpaperBitmapLocked(String name) {
+ if (name == null) name = "";
+ try {
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+ MODE_CREATE|MODE_READ_WRITE);
+ mName = name;
+ return fd;
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Error setting wallpaper", e);
+ }
+ return null;
+ }
+
+ public void setWallpaperComponent(ComponentName name) {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
+ synchronized (mLock) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ bindWallpaperComponentLocked(name);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ void bindWallpaperComponentLocked(ComponentName name) {
+ // Has the component changed?
+ if (mWallpaperConnection != null) {
+ if (mWallpaperComponent == null) {
+ if (name == null) {
+ // Still using default wallpaper.
+ return;
+ }
+ } else if (mWallpaperComponent.equals(name)) {
+ // Changing to same wallpaper.
+ return;
+ }
+ }
+
+ try {
+ ComponentName realName = name;
+ if (realName == null) {
+ // The default component is our static image wallpaper.
+ realName = new ComponentName("android",
+ ImageWallpaper.class.getName());
+ //clearWallpaperComponentLocked();
+ //return;
+ }
+ ServiceInfo si = mContext.getPackageManager().getServiceInfo(realName,
+ PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS);
+ if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
+ throw new SecurityException("Selected service does not require "
+ + android.Manifest.permission.BIND_WALLPAPER
+ + ": " + realName);
+ }
+
+ WallpaperInfo wi = null;
+
+ Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ if (name != null) {
+ // Make sure the selected service is actually a wallpaper service.
+ List<ResolveInfo> ris = mContext.getPackageManager()
+ .queryIntentServices(intent, PackageManager.GET_META_DATA);
+ for (int i=0; i<ris.size(); i++) {
+ ServiceInfo rsi = ris.get(i).serviceInfo;
+ if (rsi.name.equals(si.name) &&
+ rsi.packageName.equals(si.packageName)) {
+ try {
+ wi = new WallpaperInfo(mContext, ris.get(i));
+ } catch (XmlPullParserException e) {
+ throw new IllegalArgumentException(e);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ break;
+ }
+ }
+ if (wi == null) {
+ throw new SecurityException("Selected service is not a wallpaper: "
+ + realName);
+ }
+ }
+
+ // Bind the service!
+ WallpaperConnection newConn = new WallpaperConnection(wi);
+ intent.setComponent(realName);
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.wallpaper_binding_label);
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0,
+ Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
+ mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
+ 0));
+ if (!mContext.bindService(intent, newConn,
+ Context.BIND_AUTO_CREATE)) {
+ throw new IllegalArgumentException("Unable to bind service: "
+ + name);
+ }
+
+ clearWallpaperComponentLocked();
+ mWallpaperComponent = name;
+ mWallpaperConnection = newConn;
+ mLastDiedTime = SystemClock.uptimeMillis();
+ try {
+ if (DEBUG) Log.v(TAG, "Adding window token: " + newConn.mToken);
+ mIWindowManager.addWindowToken(newConn.mToken,
+ WindowManager.LayoutParams.TYPE_WALLPAPER);
+ } catch (RemoteException e) {
+ }
+
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown component " + name);
+ }
+ }
+
+ void clearWallpaperComponentLocked() {
+ mWallpaperComponent = null;
+ if (mWallpaperConnection != null) {
+ if (mWallpaperConnection.mEngine != null) {
+ try {
+ mWallpaperConnection.mEngine.destroy();
+ } catch (RemoteException e) {
+ }
+ }
+ mContext.unbindService(mWallpaperConnection);
+ try {
+ if (DEBUG) Log.v(TAG, "Removing window token: "
+ + mWallpaperConnection.mToken);
+ mIWindowManager.removeWindowToken(mWallpaperConnection.mToken);
+ } catch (RemoteException e) {
+ }
+ mWallpaperConnection = null;
+ }
+ }
+
+ void attachServiceLocked(WallpaperConnection conn) {
+ try {
+ conn.mService.attach(conn, conn.mToken,
+ WindowManager.LayoutParams.TYPE_WALLPAPER, false,
+ mWidth, mHeight);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed attaching wallpaper; clearing", e);
+ bindWallpaperComponentLocked(null);
+ }
+ }
+
+ private void notifyCallbacksLocked() {
+ final int n = mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onWallpaperChanged();
+ } catch (RemoteException e) {
+
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
+ }
+ }
+ mCallbacks.finishBroadcast();
+ final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void checkPermission(String permission) {
+ if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
+ throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
+ + ", must have permission " + permission);
+ }
+ }
+
+ private static JournaledFile makeJournaledFile() {
+ final String base = "/data/system/wallpaper_info.xml";
+ return new JournaledFile(new File(base), new File(base + ".tmp"));
+ }
+
+ private void saveSettingsLocked() {
+ JournaledFile journal = makeJournaledFile();
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(journal.chooseForWrite(), false);
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+
+ out.startTag(null, "wp");
+ out.attribute(null, "width", Integer.toString(mWidth));
+ out.attribute(null, "height", Integer.toString(mHeight));
+ out.attribute(null, "name", mName);
+ if (mWallpaperComponent != null) {
+ out.attribute(null, "component",
+ mWallpaperComponent.flattenToShortString());
+ }
+ out.endTag(null, "wp");
+
+ out.endDocument();
+ stream.close();
+ journal.commit();
+ } catch (IOException e) {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ex) {
+ // Ignore
+ }
+ journal.rollback();
+ }
+ }
+
+ private void loadSettingsLocked() {
+ JournaledFile journal = makeJournaledFile();
+ FileInputStream stream = null;
+ File file = journal.chooseForRead();
+ boolean success = false;
+ try {
+ stream = new FileInputStream(file);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type;
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if ("wp".equals(tag)) {
+ mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
+ mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
+ mName = parser.getAttributeValue(null, "name");
+ String comp = parser.getAttributeValue(null, "component");
+ mWallpaperComponent = comp != null
+ ? ComponentName.unflattenFromString(comp)
+ : null;
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ } catch (NullPointerException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IOException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ }
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ if (!success) {
+ mWidth = -1;
+ mHeight = -1;
+ mName = "";
+ }
+ }
+
+ void settingsRestored() {
+ boolean success = false;
+ synchronized (mLock) {
+ loadSettingsLocked();
+ // If there's a wallpaper name, we use that. If that can't be loaded, then we
+ // use the default.
+ if ("".equals(mName)) {
+ success = true;
+ } else {
+ success = restoreNamedResourceLocked();
+ }
+ }
+
+ if (!success) {
+ Log.e(TAG, "Failed to restore wallpaper: '" + mName + "'");
+ mName = "";
+ WALLPAPER_FILE.delete();
+ }
+ saveSettingsLocked();
+ }
+
+ boolean restoreNamedResourceLocked() {
+ if (mName.length() > 4 && "res:".equals(mName.substring(0, 4))) {
+ String resName = mName.substring(4);
+
+ String pkg = null;
+ int colon = resName.indexOf(':');
+ if (colon > 0) {
+ pkg = resName.substring(0, colon);
+ }
+
+ String ident = null;
+ int slash = resName.lastIndexOf('/');
+ if (slash > 0) {
+ ident = resName.substring(slash+1);
+ }
+
+ String type = null;
+ if (colon > 0 && slash > 0 && (slash-colon) > 1) {
+ type = resName.substring(colon+1, slash);
+ }
+
+ if (pkg != null && ident != null && type != null) {
+ int resId = -1;
+ InputStream res = null;
+ FileOutputStream fos = null;
+ try {
+ Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
+ Resources r = c.getResources();
+ resId = r.getIdentifier(resName, null, null);
+ if (resId == 0) {
+ Log.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
+ + " ident=" + ident);
+ return false;
+ }
+
+ res = r.openRawResource(resId);
+ fos = new FileOutputStream(WALLPAPER_FILE);
+
+ byte[] buffer = new byte[32768];
+ int amt;
+ while ((amt=res.read(buffer)) > 0) {
+ fos.write(buffer, 0, amt);
+ }
+ // mWallpaperObserver will notice the close and send the change broadcast
+
+ Log.d(TAG, "Restored wallpaper: " + resName);
+ return true;
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Package name " + pkg + " not found");
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Resource not found: " + resId);
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while restoring wallpaper ", e);
+ } finally {
+ if (res != null) {
+ try {
+ res.close();
+ } catch (IOException ex) {}
+ }
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ex) {}
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @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 wallpaper service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mLock) {
+ pw.println("Current Wallpaper Service state:");
+ pw.print(" mWidth="); pw.print(mWidth);
+ pw.print(" mHeight="); pw.println(mHeight);
+ pw.print(" mName="); pw.println(mName);
+ pw.print(" mWallpaperComponent="); pw.println(mWallpaperComponent);
+ if (mWallpaperConnection != null) {
+ WallpaperConnection conn = mWallpaperConnection;
+ pw.print(" Wallpaper connection ");
+ pw.print(conn); pw.println(":");
+ pw.print(" mInfo.component="); pw.println(conn.mInfo.getComponent());
+ pw.print(" mToken="); pw.println(conn.mToken);
+ pw.print(" mService="); pw.println(conn.mService);
+ pw.print(" mEngine="); pw.println(conn.mEngine);
+ pw.print(" mLastDiedTime=");
+ pw.println(mLastDiedTime - SystemClock.uptimeMillis());
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java
deleted file mode 100644
index d921baf..0000000
--- a/services/java/com/android/server/WallpaperService.java
+++ /dev/null
@@ -1,203 +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 static android.os.FileObserver.*;
-import static android.os.ParcelFileDescriptor.*;
-
-import android.app.IWallpaperService;
-import android.app.IWallpaperServiceCallback;
-import android.backup.BackupManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.os.FileObserver;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteCallbackList;
-import android.util.Config;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-class WallpaperService extends IWallpaperService.Stub {
- private static final String TAG = WallpaperService.class.getSimpleName();
-
- private static final File WALLPAPER_DIR = new File(
- "/data/data/com.android.settings/files");
- private static final String WALLPAPER = "wallpaper";
- private static final File WALLPAPER_FILE = new File(WALLPAPER_DIR, WALLPAPER);
-
- private static final String PREFERENCES = "wallpaper-hints";
-
- private static final String HINT_WIDTH = "hintWidth";
- private static final String HINT_HEIGHT = "hintHeight";
-
- /**
- * List of callbacks registered they should each be notified
- * when the wallpaper is changed.
- */
- private final RemoteCallbackList<IWallpaperServiceCallback> mCallbacks
- = new RemoteCallbackList<IWallpaperServiceCallback>();
-
- /**
- * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
- * that the wallpaper has changed. The CREATE is triggered when there is no
- * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
- * everytime the wallpaper is changed.
- */
- private final FileObserver mWallpaperObserver = new FileObserver(
- WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE) {
- @Override
- public void onEvent(int event, String path) {
- if (path == null) {
- return;
- }
-
- File changedFile = new File(WALLPAPER_DIR, path);
- if (WALLPAPER_FILE.equals(changedFile)) {
- notifyCallbacks();
- }
- }
- };
-
- private final Context mContext;
-
- private int mWidth = -1;
- private int mHeight = -1;
-
- public WallpaperService(Context context) {
- if (Config.LOGD) Log.d(TAG, "WallpaperService startup");
- mContext = context;
- createFilesDir();
- mWallpaperObserver.startWatching();
-
- SharedPreferences preferences = mContext.getSharedPreferences(PREFERENCES,
- Context.MODE_PRIVATE);
- mWidth = preferences.getInt(HINT_WIDTH, -1);
- mHeight = preferences.getInt(HINT_HEIGHT, -1);
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- mWallpaperObserver.stopWatching();
- }
-
- public void clearWallpaper() {
- File f = WALLPAPER_FILE;
- if (f.exists()) {
- f.delete();
- }
- }
-
- public void setDimensionHints(int width, int height) throws RemoteException {
- checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
-
- if (width <= 0 || height <= 0) {
- throw new IllegalArgumentException("width and height must be > 0");
- }
-
- if (width != mWidth || height != mHeight) {
- mWidth = width;
- mHeight = height;
-
- SharedPreferences preferences = mContext.getSharedPreferences(PREFERENCES,
- Context.MODE_PRIVATE);
-
- final SharedPreferences.Editor editor = preferences.edit();
- editor.putInt(HINT_WIDTH, width);
- editor.putInt(HINT_HEIGHT, height);
- editor.commit();
- }
- }
-
- public int getWidthHint() throws RemoteException {
- return mWidth;
- }
-
- public int getHeightHint() throws RemoteException {
- return mHeight;
- }
-
- public ParcelFileDescriptor getWallpaper(IWallpaperServiceCallback cb) {
- try {
- mCallbacks.register(cb);
- File f = WALLPAPER_FILE;
- if (!f.exists()) {
- return null;
- }
- return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
- } catch (FileNotFoundException e) {
-
- /* Shouldn't happen as we check to see if the file exists */
- if (Config.LOGD) Log.d(TAG, "Error getting wallpaper", e);
- }
- return null;
- }
-
- public ParcelFileDescriptor setWallpaper() {
- checkPermission(android.Manifest.permission.SET_WALLPAPER);
- try {
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
- MODE_CREATE|MODE_READ_WRITE);
-
- // changing the wallpaper means we'll need to back up the new one
- long origId = Binder.clearCallingIdentity();
- BackupManager bm = new BackupManager(mContext);
- bm.dataChanged();
- Binder.restoreCallingIdentity(origId);
-
- return fd;
- } catch (FileNotFoundException e) {
- if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e);
- }
- return null;
- }
-
- private void createFilesDir() {
- if (!WALLPAPER_DIR.exists()) {
- WALLPAPER_DIR.mkdirs();
- }
- }
-
- private void notifyCallbacks() {
- final int n = mCallbacks.beginBroadcast();
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onWallpaperChanged();
- } catch (RemoteException e) {
-
- // The RemoteCallbackList will take care of removing
- // the dead object for us.
- }
- }
- mCallbacks.finishBroadcast();
- final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
- mContext.sendBroadcast(intent);
- }
-
- private void checkPermission(String permission) {
- if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) {
- throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
- + ", must have permission " + permission);
- }
- }
-}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 394ed3a..32ad6c6 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -41,6 +41,7 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.SupplicantState;
import android.net.NetworkStateTracker;
import android.net.DhcpInfo;
+import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -92,6 +93,9 @@ public class WifiService extends IWifiManager.Stub {
private boolean mDeviceIdle;
private int mPluggedType;
+ // true if the user enabled Wifi while in airplane mode
+ private boolean mAirplaneModeOverwridden;
+
private final LockList mLocks = new LockList();
// some wifi lock statistics
private int mFullLocksAcquired;
@@ -142,28 +146,6 @@ public class WifiService extends IWifiManager.Stub {
private final WifiHandler mWifiHandler;
/*
- * Map used to keep track of hidden networks presence, which
- * is needed to switch between active and passive scan modes.
- * If there is at least one hidden network that is currently
- * present (enabled), we want to do active scans instead of
- * passive.
- */
- private final Map<Integer, Boolean> mIsHiddenNetworkPresent;
- /*
- * The number of currently present hidden networks. When this
- * counter goes from 0 to 1 or from 1 to 0, we change the
- * scan mode to active or passive respectively. Initially, we
- * set the counter to 0 and we increment it every time we add
- * a new present (enabled) hidden network.
- */
- private int mNumHiddenNetworkPresent;
- /*
- * Whether we change the scan mode is due to a hidden network
- * (in this class, this is always the case)
- */
- private final static boolean SET_DUE_TO_A_HIDDEN_NETWORK = true;
-
- /*
* Cache of scan results objects (size is somewhat arbitrary)
*/
private static final int SCAN_RESULT_CACHE_SIZE = 80;
@@ -195,12 +177,6 @@ public class WifiService extends IWifiManager.Stub {
mWifiStateTracker.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
- /*
- * Initialize the hidden-networks state
- */
- mIsHiddenNetworkPresent = new HashMap<Integer, Boolean>();
- mNumHiddenNetworkPresent = 0;
-
mScanResultCache = new LinkedHashMap<String, ScanResult>(
SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
/*
@@ -246,6 +222,8 @@ public class WifiService extends IWifiManager.Stub {
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // clear our flag indicating the user has overwridden airplane mode
+ mAirplaneModeOverwridden = false;
updateWifiState();
}
},
@@ -254,155 +232,6 @@ public class WifiService extends IWifiManager.Stub {
setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
}
- /**
- * Initializes the hidden networks state. Must be called when we
- * enable Wi-Fi.
- */
- private synchronized void initializeHiddenNetworksState() {
- // First, reset the state
- resetHiddenNetworksState();
-
- // ... then add networks that are marked as hidden
- List<WifiConfiguration> networks = getConfiguredNetworks();
- if (!networks.isEmpty()) {
- for (WifiConfiguration config : networks) {
- if (config != null && config.hiddenSSID) {
- addOrUpdateHiddenNetwork(
- config.networkId,
- config.status != WifiConfiguration.Status.DISABLED);
- }
- }
-
- }
- }
-
- /**
- * Resets the hidden networks state.
- */
- private synchronized void resetHiddenNetworksState() {
- mNumHiddenNetworkPresent = 0;
- mIsHiddenNetworkPresent.clear();
- }
-
- /**
- * Marks all but netId network as not present.
- */
- private synchronized void markAllHiddenNetworksButOneAsNotPresent(int netId) {
- for (Map.Entry<Integer, Boolean> entry : mIsHiddenNetworkPresent.entrySet()) {
- if (entry != null) {
- Integer networkId = entry.getKey();
- if (networkId != netId) {
- updateNetworkIfHidden(
- networkId, false);
- }
- }
- }
- }
-
- /**
- * Updates the netId network presence status if netId is an existing
- * hidden network.
- */
- private synchronized void updateNetworkIfHidden(int netId, boolean present) {
- if (isHiddenNetwork(netId)) {
- addOrUpdateHiddenNetwork(netId, present);
- }
- }
-
- /**
- * Updates the netId network presence status if netId is an existing
- * hidden network. If the network does not exist, adds the network.
- */
- private synchronized void addOrUpdateHiddenNetwork(int netId, boolean present) {
- if (0 <= netId) {
-
- // If we are adding a new entry or modifying an existing one
- Boolean isPresent = mIsHiddenNetworkPresent.get(netId);
- if (isPresent == null || isPresent != present) {
- if (present) {
- incrementHiddentNetworkPresentCounter();
- } else {
- // If we add a new hidden network, no need to change
- // the counter (it must be 0)
- if (isPresent != null) {
- decrementHiddentNetworkPresentCounter();
- }
- }
- mIsHiddenNetworkPresent.put(netId, present);
- }
- } else {
- Log.e(TAG, "addOrUpdateHiddenNetwork(): Invalid (negative) network id!");
- }
- }
-
- /**
- * Removes the netId network if it is hidden (being kept track of).
- */
- private synchronized void removeNetworkIfHidden(int netId) {
- if (isHiddenNetwork(netId)) {
- removeHiddenNetwork(netId);
- }
- }
-
- /**
- * Removes the netId network. For the call to be successful, the network
- * must be hidden.
- */
- private synchronized void removeHiddenNetwork(int netId) {
- if (0 <= netId) {
- Boolean isPresent =
- mIsHiddenNetworkPresent.remove(netId);
- if (isPresent != null) {
- // If we remove an existing hidden network that is not
- // present, no need to change the counter
- if (isPresent) {
- decrementHiddentNetworkPresentCounter();
- }
- } else {
- if (DBG) {
- Log.d(TAG, "removeHiddenNetwork(): Removing a non-existent network!");
- }
- }
- } else {
- Log.e(TAG, "removeHiddenNetwork(): Invalid (negative) network id!");
- }
- }
-
- /**
- * Returns true if netId is an existing hidden network.
- */
- private synchronized boolean isHiddenNetwork(int netId) {
- return mIsHiddenNetworkPresent.containsKey(netId);
- }
-
- /**
- * Increments the present (enabled) hidden networks counter. If the
- * counter value goes from 0 to 1, changes the scan mode to active.
- */
- private void incrementHiddentNetworkPresentCounter() {
- ++mNumHiddenNetworkPresent;
- if (1 == mNumHiddenNetworkPresent) {
- // Switch the scan mode to "active"
- mWifiStateTracker.setScanMode(true, SET_DUE_TO_A_HIDDEN_NETWORK);
- }
- }
-
- /**
- * Decrements the present (enabled) hidden networks counter. If the
- * counter goes from 1 to 0, changes the scan mode back to passive.
- */
- private void decrementHiddentNetworkPresentCounter() {
- if (0 < mNumHiddenNetworkPresent) {
- --mNumHiddenNetworkPresent;
- if (0 == mNumHiddenNetworkPresent) {
- // Switch the scan mode to "passive"
- mWifiStateTracker.setScanMode(false, SET_DUE_TO_A_HIDDEN_NETWORK);
- }
- } else {
- Log.e(TAG, "Hidden-network counter invariant violation!");
- }
- }
-
private boolean getPersistedWifiEnabled() {
final ContentResolver cr = mContext.getContentResolver();
try {
@@ -466,8 +295,14 @@ public class WifiService extends IWifiManager.Stub {
if (mWifiHandler == null) return false;
synchronized (mWifiHandler) {
+ // caller may not have WAKE_LOCK permission - it's not required here
+ long ident = Binder.clearCallingIdentity();
sWakeLock.acquire();
+ Binder.restoreCallingIdentity(ident);
+
mLastEnableUid = Binder.getCallingUid();
+ // set a flag if the user is enabling Wifi while in airplane mode
+ mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable());
sendEnableMessage(enable, true, Binder.getCallingUid());
}
@@ -488,7 +323,7 @@ public class WifiService extends IWifiManager.Stub {
if (mWifiState == eventualWifiState) {
return true;
}
- if (enable && isAirplaneModeOn()) {
+ if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) {
return false;
}
@@ -522,7 +357,7 @@ public class WifiService extends IWifiManager.Stub {
}
// We must reset the interface before we unload the driver
- mWifiStateTracker.resetInterface();
+ mWifiStateTracker.resetInterface(false);
if (!WifiNative.unloadDriver()) {
Log.e(TAG, "Failed to unload Wi-Fi driver.");
@@ -543,15 +378,6 @@ public class WifiService extends IWifiManager.Stub {
}
setWifiEnabledState(eventualWifiState, uid);
- /*
- * Initialize the hidden networks state and the number of allowed
- * radio channels if Wi-Fi is being turned on.
- */
- if (enable) {
- mWifiStateTracker.setNumAllowedChannels();
- initializeHiddenNetworksState();
- }
-
return true;
}
@@ -843,6 +669,15 @@ public class WifiService extends IWifiManager.Stub {
}
}
}
+
+ for (WifiConfiguration.EnterpriseField field :
+ config.enterpriseFields) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ field.varName());
+ if (!TextUtils.isEmpty(value)) {
+ field.setValue(value);
+ }
+ }
}
/**
@@ -884,15 +719,6 @@ public class WifiService extends IWifiManager.Stub {
}
mNeedReconfig = mNeedReconfig || doReconfig;
- /*
- * If we have hidden networks, we may have to change the scan mode
- */
- if (config.hiddenSSID) {
- // Mark the network as present unless it is disabled
- addOrUpdateHiddenNetwork(
- netId, config.status != WifiConfiguration.Status.DISABLED);
- }
-
setVariables: {
/*
* Note that if a networkId for a non-existent network
@@ -1064,103 +890,20 @@ public class WifiService extends IWifiManager.Stub {
break setVariables;
}
- if ((config.eap != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.eapVarName,
- config.eap)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set eap: "+
- config.eap);
- }
- break setVariables;
- }
-
- if ((config.phase2 != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.phase2VarName,
- config.phase2)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set phase2: "+
- config.phase2);
- }
- break setVariables;
- }
-
- if ((config.identity != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.identityVarName,
- config.identity)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set identity: "+
- config.identity);
- }
- break setVariables;
- }
-
- if ((config.anonymousIdentity != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.anonymousIdentityVarName,
- config.anonymousIdentity)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set anonymousIdentity: "+
- config.anonymousIdentity);
- }
- break setVariables;
- }
-
- if ((config.password != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.passwordVarName,
- config.password)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set password: "+
- config.password);
- }
- break setVariables;
- }
-
- if ((config.clientCert != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.clientCertVarName,
- config.clientCert)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set clientCert: "+
- config.clientCert);
- }
- break setVariables;
- }
-
- if ((config.caCert != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.caCertVarName,
- config.caCert)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set caCert: "+
- config.caCert);
- }
- break setVariables;
- }
-
- if ((config.privateKey != null) && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.privateKeyVarName,
- config.privateKey)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set privateKey: "+
- config.privateKey);
- }
- break setVariables;
- }
-
- if ((config.privateKeyPasswd != null) && !WifiNative.setNetworkVariableCommand(
+ for (WifiConfiguration.EnterpriseField field
+ : config.enterpriseFields) {
+ String varName = field.varName();
+ String value = field.value();
+ if ((value != null) && !WifiNative.setNetworkVariableCommand(
netId,
- WifiConfiguration.privateKeyPasswdVarName,
- config.privateKeyPasswd)) {
- if (DBG) {
- Log.d(TAG, config.SSID + ": failed to set privateKeyPasswd: "+
- config.privateKeyPasswd);
+ varName,
+ value)) {
+ if (DBG) {
+ Log.d(TAG, config.SSID + ": failed to set " + varName +
+ ": " + value);
+ }
+ break setVariables;
}
- break setVariables;
}
return netId;
@@ -1231,11 +974,6 @@ public class WifiService extends IWifiManager.Stub {
public boolean removeNetwork(int netId) {
enforceChangePermission();
- /*
- * If we have hidden networks, we may have to change the scan mode
- */
- removeNetworkIfHidden(netId);
-
return mWifiStateTracker.removeNetwork(netId);
}
@@ -1249,18 +987,14 @@ public class WifiService extends IWifiManager.Stub {
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
- /*
- * If we have hidden networks, we may have to change the scan mode
- */
- synchronized(this) {
- if (disableOthers) {
- markAllHiddenNetworksButOneAsNotPresent(netId);
- }
- updateNetworkIfHidden(netId, true);
- }
-
synchronized (mWifiStateTracker) {
- return WifiNative.enableNetworkCommand(netId, disableOthers);
+ String ifname = mWifiStateTracker.getInterfaceName();
+ NetworkUtils.enableInterface(ifname);
+ boolean result = WifiNative.enableNetworkCommand(netId, disableOthers);
+ if (!result) {
+ NetworkUtils.disableInterface(ifname);
+ }
+ return result;
}
}
@@ -1273,11 +1007,6 @@ public class WifiService extends IWifiManager.Stub {
public boolean disableNetwork(int netId) {
enforceChangePermission();
- /*
- * If we have hidden networks, we may have to change the scan mode
- */
- updateNetworkIfHidden(netId, false);
-
synchronized (mWifiStateTracker) {
return WifiNative.disableNetworkCommand(netId);
}
@@ -1375,45 +1104,49 @@ public class WifiService extends IWifiManager.Stub {
level = 0;
}
- // bssid is the hash key
- scanResult = mScanResultCache.get(bssid);
- if (scanResult != null) {
- scanResult.level = level;
- } else {
- /*
- * The formatting of the results returned by
- * wpa_supplicant is intended to make the fields
- * line up nicely when printed,
- * not to make them easy to parse. So we have to
- * apply some heuristics to figure out which field
- * is the SSID and which field is the flags.
- */
- String ssid;
- String flags;
- if (result.length == 4) {
- if (result[3].charAt(0) == '[') {
- flags = result[3];
- ssid = "";
- } else {
- flags = "";
- ssid = result[3];
- }
- } else if (result.length == 5) {
+ /*
+ * The formatting of the results returned by
+ * wpa_supplicant is intended to make the fields
+ * line up nicely when printed,
+ * not to make them easy to parse. So we have to
+ * apply some heuristics to figure out which field
+ * is the SSID and which field is the flags.
+ */
+ String ssid;
+ String flags;
+ if (result.length == 4) {
+ if (result[3].charAt(0) == '[') {
flags = result[3];
- ssid = result[4];
+ ssid = "";
} else {
- // Here, we must have 3 fields: no flags and ssid
- // set
flags = "";
- ssid = "";
+ ssid = result[3];
}
+ } else if (result.length == 5) {
+ flags = result[3];
+ ssid = result[4];
+ } else {
+ // Here, we must have 3 fields: no flags and ssid
+ // set
+ flags = "";
+ ssid = "";
+ }
+ // bssid + ssid is the hash key
+ String key = bssid + ssid;
+ scanResult = mScanResultCache.get(key);
+ if (scanResult != null) {
+ scanResult.level = level;
+ scanResult.SSID = ssid;
+ scanResult.capabilities = flags;
+ scanResult.frequency = frequency;
+ } else {
// Do not add scan results that have no SSID set
if (0 < ssid.trim().length()) {
scanResult =
new ScanResult(
ssid, bssid, flags, level, frequency);
- mScanResultCache.put(bssid, scanResult);
+ mScanResultCache.put(key, scanResult);
}
}
} else {
@@ -1479,14 +1212,17 @@ public class WifiService extends IWifiManager.Stub {
* Set the number of radio frequency channels that are allowed to be used
* in the current regulatory domain. This method should be used only
* if the correct number of channels cannot be determined automatically
- * for some reason. If the operation is successful, the new value is
+ * for some reason. If the operation is successful, the new value may be
* persisted as a Secure setting.
* @param numChannels the number of allowed channels. Must be greater than 0
* and less than or equal to 16.
+ * @param persist {@code true} if the setting should be remembered.
* @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
* {@code numChannels} is outside the valid range.
*/
- public boolean setNumAllowedChannels(int numChannels) {
+ public boolean setNumAllowedChannels(int numChannels, boolean persist) {
+ Log.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
+ " with persist set to "+persist);
enforceChangePermission();
/*
* Validate the argument. We'd like to let the Wi-Fi driver do this,
@@ -1505,9 +1241,11 @@ public class WifiService extends IWifiManager.Stub {
return false;
}
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
- numChannels);
+ if (persist) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
+ numChannels);
+ }
mWifiStateTracker.setNumAllowedChannels(numChannels);
return true;
}
@@ -1586,9 +1324,16 @@ public class WifiService extends IWifiManager.Stub {
if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
WifiInfo info = mWifiStateTracker.requestConnectionInfo();
if (info.getSupplicantState() != SupplicantState.COMPLETED) {
- // do not keep Wifi awake when screen is off if Wifi is not associated
- mDeviceIdle = true;
- updateWifiState();
+ // we used to go to sleep immediately, but this caused some race conditions
+ // we don't have time to track down for this release. Delay instead, but not
+ // 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
+ Log.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;
Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
@@ -1619,10 +1364,10 @@ public class WifiService extends IWifiManager.Stub {
return;
}
mPluggedType = pluggedType;
- } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
+ } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
boolean isBluetoothPlaying =
intent.getIntExtra(
- BluetoothA2dp.SINK_STATE,
+ BluetoothA2dp.EXTRA_SINK_STATE,
BluetoothA2dp.STATE_DISCONNECTED) == BluetoothA2dp.STATE_PLAYING;
mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
} else {
@@ -1688,7 +1433,7 @@ public class WifiService extends IWifiManager.Stub {
private void updateWifiState() {
boolean wifiEnabled = getPersistedWifiEnabled();
- boolean airplaneMode = isAirplaneModeOn();
+ boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
boolean lockHeld = mLocks.hasLocks();
int strongestLockMode;
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
@@ -1741,7 +1486,7 @@ public class WifiService extends IWifiManager.Stub {
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
intentFilter.addAction(ACTION_DEVICE_IDLE);
- intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
+ intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
mContext.registerReceiver(mReceiver, intentFilter);
}
@@ -1752,6 +1497,13 @@ public class WifiService extends IWifiManager.Stub {
|| airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
}
+ private boolean isAirplaneToggleable() {
+ String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+ return toggleableRadios != null
+ && toggleableRadios.contains(Settings.System.RADIO_WIFI);
+ }
+
/**
* Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
* currently on.
@@ -1950,8 +1702,10 @@ public class WifiService extends IWifiManager.Stub {
}
private boolean acquireWifiLockLocked(WifiLock wifiLock) {
+ Log.d(TAG, "acquireWifiLockLocked: " + wifiLock);
+
mLocks.addLock(wifiLock);
-
+
int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
@@ -1969,7 +1723,7 @@ public class WifiService extends IWifiManager.Stub {
} finally {
Binder.restoreCallingIdentity(ident);
}
-
+
updateWifiState();
return true;
}
@@ -1983,8 +1737,11 @@ public class WifiService extends IWifiManager.Stub {
private boolean releaseWifiLockLocked(IBinder lock) {
boolean hadLock;
-
+
WifiLock wifiLock = mLocks.removeLock(lock);
+
+ Log.d(TAG, "releaseWifiLockLocked: " + wifiLock);
+
hadLock = (wifiLock != null);
if (hadLock) {
@@ -2006,7 +1763,7 @@ public class WifiService extends IWifiManager.Stub {
Binder.restoreCallingIdentity(ident);
}
}
-
+ // TODO - should this only happen if you hadLock?
updateWifiState();
return hadLock;
}
@@ -2068,7 +1825,9 @@ public class WifiService extends IWifiManager.Stub {
// our new size == 1 (first call), but this function won't
// be called often and by making the stopPacket call each
// time we're less fragile and self-healing.
- WifiNative.stopPacketFiltering();
+ synchronized (mWifiStateTracker) {
+ WifiNative.stopPacketFiltering();
+ }
}
int uid = Binder.getCallingUid();
@@ -2104,7 +1863,9 @@ public class WifiService extends IWifiManager.Stub {
removed.unlinkDeathRecipient();
}
if (mMulticasters.size() == 0) {
- WifiNative.startPacketFiltering();
+ synchronized (mWifiStateTracker) {
+ WifiNative.startPacketFiltering();
+ }
}
Long ident = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index d4c27b7..327cd72 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -31,15 +31,15 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_GPU;
-import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_HARDWARE;
import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_PUSH_BUFFERS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
@@ -63,9 +63,11 @@ import android.graphics.Rect;
import android.graphics.Region;
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;
@@ -98,6 +100,7 @@ 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;
@@ -124,7 +127,8 @@ import java.util.Iterator;
import java.util.List;
/** {@hide} */
-public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor {
+public class WindowManagerService extends IWindowManager.Stub
+ implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
@@ -133,11 +137,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
+ static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
+ static final boolean DEBUG_WALLPAPER = 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;
@@ -145,9 +154,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
static final int LOG_WM_NO_SURFACE_MEMORY = 31000;
- /** How long to wait for first key repeat, in milliseconds */
- static final int KEY_REPEAT_FIRST_DELAY = 750;
-
/** How long to wait for subsequent key repeats, in milliseconds */
static final int KEY_REPEAT_DELAY = 50;
@@ -228,8 +234,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mPolicy.enableKeyguard(false);
}
public void released() {
+ mPolicy.enableKeyguard(true);
synchronized (mKeyguardDisabled) {
- mPolicy.enableKeyguard(true);
mWaitingUntilKeyguardReenabled = false;
mKeyguardDisabled.notifyAll();
}
@@ -297,6 +303,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>();
/**
+ * This was the app token that was used to retrieve the last enter
+ * animation. It will be used for the next exit animation.
+ */
+ AppWindowToken mLastEnterAnimToken;
+
+ /**
+ * These were the layout params used to retrieve the last enter animation.
+ * They will be used for the next exit animation.
+ */
+ LayoutParams mLastEnterAnimParams;
+
+ /**
* Z-ordered (bottom-most first) list of all Window objects.
*/
final ArrayList mWindows = new ArrayList();
@@ -368,13 +386,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// perform or TRANSIT_NONE if we are not waiting. If we are waiting,
// mOpeningApps and mClosingApps are the lists of tokens that will be
// made visible or hidden at the next transition.
- int mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+ int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
+ String mNextAppTransitionPackage;
+ int mNextAppTransitionEnter;
+ int mNextAppTransitionExit;
boolean mAppTransitionReady = false;
+ boolean mAppTransitionRunning = false;
boolean mAppTransitionTimeout = false;
boolean mStartingIconInTransition = false;
boolean mSkipAppTransitionAnimation = false;
final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>();
//flag to detect fat touch events
boolean mFatTouch = false;
@@ -395,6 +419,33 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
WindowState mInputMethodWindow = null;
final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>();
+ final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>();
+
+ // If non-null, this is the currently visible window that is associated
+ // with the wallpaper.
+ WindowState mWallpaperTarget = null;
+ // If non-null, we are in the middle of animating from one wallpaper target
+ // to another, and this is the lower one in Z-order.
+ WindowState mLowerWallpaperTarget = null;
+ // If non-null, we are in the middle of animating from one wallpaper target
+ // to another, and this is the higher one in Z-order.
+ WindowState mUpperWallpaperTarget = null;
+ int mWallpaperAnimLayerAdjustment;
+ float mLastWallpaperX = -1;
+ 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;
+ // The last time we had a timeout when waiting for a wallpaper.
+ long mLastWallpaperTimeoutTime;
+ // We give a wallpaper up to 150ms to finish scrolling.
+ static final long WALLPAPER_TIMEOUT = 150;
+ // Time we wait after a timeout before trying to wait again.
+ static final long WALLPAPER_TIMEOUT_RECOVERY = 10000;
+
AppWindowToken mFocusedApp = null;
PowerManagerService mPowerManager;
@@ -409,6 +460,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// Who is holding the screen on.
Session mHoldingScreenOn;
+ boolean mTurnOnScreen;
+
/**
* Whether the UI is currently running in touch mode (not showing
* navigational focus because the user is directly pressing the screen).
@@ -512,6 +565,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
private WindowManagerService(Context context, PowerManagerService pm,
boolean haveInputMethods) {
+ if (MEASURE_LATENCY) {
+ lt = new LatencyTimer(100, 1000);
+ }
+
mContext = context;
mHaveInputMethods = haveInputMethods;
mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -583,7 +640,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
private void placeWindowAfter(Object pos, WindowState window) {
final int i = mWindows.indexOf(pos);
- if (localLOGV || DEBUG_FOCUS) Log.v(
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
TAG, "Adding window " + window + " at "
+ (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
mWindows.add(i+1, window);
@@ -591,7 +648,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
private void placeWindowBefore(Object pos, WindowState window) {
final int i = mWindows.indexOf(pos);
- if (localLOGV || DEBUG_FOCUS) Log.v(
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
TAG, "Adding window " + window + " at "
+ i + " of " + mWindows.size() + " (before " + pos + ")");
mWindows.add(i, window);
@@ -648,6 +705,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
+ TAG, "Adding window " + win + " at "
+ + (newIdx+1) + " of " + N);
localmWindows.add(newIdx+1, win);
}
}
@@ -666,7 +726,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
i--;
break;
}
- if (t.windows.size() > 0) {
+
+ // We haven't reached the token yet; if this token
+ // is not going to the bottom and has windows, we can
+ // use it as an anchor for when we do reach the token.
+ if (!t.sendingToBottom && t.windows.size() > 0) {
pos = t.windows.get(0);
}
}
@@ -688,6 +752,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
placeWindowBefore(pos, win);
} else {
+ // Continue looking down until we find the first
+ // token that has windows.
while (i >= 0) {
AppWindowToken t = mAppTokens.get(i);
final int NW = t.windows.size();
@@ -721,9 +787,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
break;
}
}
- if (localLOGV || DEBUG_FOCUS) Log.v(
- TAG, "Adding window " + win + " at "
- + i + " of " + N);
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
+ TAG, "Adding window " + win + " at "
+ + i + " of " + N);
localmWindows.add(i, win);
}
}
@@ -738,9 +804,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
if (i < 0) i = 0;
- if (localLOGV || DEBUG_FOCUS) Log.v(
- TAG, "Adding window " + win + " at "
- + i + " of " + N);
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
+ TAG, "Adding window " + win + " at "
+ + i + " of " + N);
localmWindows.add(i, win);
}
if (addToToken) {
@@ -887,7 +953,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
+ " layer=" + highestTarget.mAnimLayer
+ " new layer=" + w.mAnimLayer);
- if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
// If we are currently setting up for an animation,
// hold everything until we can find out what will happen.
mInputMethodTargetWaitingAnim = true;
@@ -910,7 +976,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (w != null) {
if (willMove) {
RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
+ if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from "
+ mInputMethodTarget + " to " + w, e);
mInputMethodTarget = w;
@@ -924,7 +990,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
if (willMove) {
RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
+ if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from "
+ mInputMethodTarget + " to null", e);
mInputMethodTarget = null;
@@ -937,6 +1003,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
int pos = findDesiredInputMethodWindowIndexLocked(true);
if (pos >= 0) {
win.mTargetAppToken = mInputMethodTarget.mAppToken;
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(
+ TAG, "Adding input method window " + win + " at " + pos);
mWindows.add(pos, win);
moveInputMethodDialogsLocked(pos+1);
return;
@@ -977,6 +1045,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
int wpos = mWindows.indexOf(win);
if (wpos >= 0) {
if (wpos < interestingPos) interestingPos--;
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Temp removing at " + wpos + ": " + win);
mWindows.remove(wpos);
int NC = win.mChildWindows.size();
while (NC > 0) {
@@ -985,6 +1054,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
int cpos = mWindows.indexOf(cw);
if (cpos >= 0) {
if (cpos < interestingPos) interestingPos--;
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Temp removing child at "
+ + cpos + ": " + cw);
mWindows.remove(cpos);
}
}
@@ -999,6 +1070,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// this case should be rare, so it shouldn't be that big a deal.
int wpos = mWindows.indexOf(win);
if (wpos >= 0) {
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "ReAdd removing from " + wpos
+ + ": " + win);
mWindows.remove(wpos);
reAddWindowLocked(wpos, win);
}
@@ -1159,6 +1232,546 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
}
+ final boolean isWallpaperVisible(WindowState wallpaperTarget) {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper vis: target obscured="
+ + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
+ + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
+ ? wallpaperTarget.mAppToken.animation : null)
+ + " upper=" + mUpperWallpaperTarget
+ + " lower=" + mLowerWallpaperTarget);
+ return (wallpaperTarget != null
+ && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
+ && wallpaperTarget.mAppToken.animation != null)))
+ || mUpperWallpaperTarget != null
+ || mLowerWallpaperTarget != null;
+ }
+
+ static final int ADJUST_WALLPAPER_LAYERS_CHANGED = 1<<1;
+ static final int ADJUST_WALLPAPER_VISIBILITY_CHANGED = 1<<2;
+
+ int adjustWallpaperWindowsLocked() {
+ int changed = 0;
+
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+
+ // First find top-most window that has asked to be on top of the
+ // wallpaper; all wallpapers go behind it.
+ final ArrayList localmWindows = mWindows;
+ int N = localmWindows.size();
+ WindowState w = null;
+ WindowState foundW = null;
+ int foundI = 0;
+ WindowState topCurW = null;
+ int topCurI = 0;
+ int i = N;
+ while (i > 0) {
+ i--;
+ w = (WindowState)localmWindows.get(i);
+ if ((w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER)) {
+ if (topCurW == null) {
+ topCurW = w;
+ topCurI = i;
+ }
+ continue;
+ }
+ topCurW = null;
+ if (w.mAppToken != null) {
+ // If this window's app token is hidden and not animating,
+ // it is of no interest to us.
+ if (w.mAppToken.hidden && w.mAppToken.animation == null) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Skipping hidden or animating token: " + w);
+ topCurW = null;
+ continue;
+ }
+ }
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Win " + w + ": readyfordisplay="
+ + w.isReadyForDisplay() + " drawpending=" + w.mDrawPending
+ + " commitdrawpending=" + w.mCommitDrawPending);
+ if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay()
+ && (mWallpaperTarget == w
+ || (!w.mDrawPending && !w.mCommitDrawPending))) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Found wallpaper activity: #" + i + "=" + w);
+ foundW = w;
+ foundI = i;
+ if (w == mWallpaperTarget && ((w.mAppToken != null
+ && w.mAppToken.animation != null)
+ || w.mAnimation != null)) {
+ // The current wallpaper target is animating, so we'll
+ // look behind it for another possible target and figure
+ // out what is going on below.
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Win " + w
+ + ": token animating, looking behind.");
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ // If we are currently waiting for an app transition, and either
+ // the current target or the next target are involved with it,
+ // then hold off on doing anything with the wallpaper.
+ // Note that we are checking here for just whether the target
+ // is part of an app token... which is potentially overly aggressive
+ // (the app token may not be involved in the transition), but good
+ // enough (we'll just wait until whatever transition is pending
+ // executes).
+ if (mWallpaperTarget != null && mWallpaperTarget.mAppToken != null) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper not changing: waiting for app anim in current target");
+ return 0;
+ }
+ if (foundW != null && foundW.mAppToken != null) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper not changing: waiting for app anim in found target");
+ return 0;
+ }
+ }
+
+ if (mWallpaperTarget != foundW) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "New wallpaper target: " + foundW
+ + " oldTarget: " + mWallpaperTarget);
+ }
+
+ mLowerWallpaperTarget = null;
+ mUpperWallpaperTarget = null;
+
+ WindowState oldW = mWallpaperTarget;
+ mWallpaperTarget = foundW;
+
+ // Now what is happening... if the current and new targets are
+ // animating, then we are in our super special mode!
+ if (foundW != null && oldW != null) {
+ boolean oldAnim = oldW.mAnimation != null
+ || (oldW.mAppToken != null && oldW.mAppToken.animation != null);
+ boolean foundAnim = foundW.mAnimation != null
+ || (foundW.mAppToken != null && foundW.mAppToken.animation != null);
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "New animation: " + foundAnim
+ + " old animation: " + oldAnim);
+ }
+ if (foundAnim && oldAnim) {
+ int oldI = localmWindows.indexOf(oldW);
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "New i: " + foundI + " old i: " + oldI);
+ }
+ if (oldI >= 0) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "Animating wallpapers: old#" + oldI
+ + "=" + oldW + "; new#" + foundI
+ + "=" + foundW);
+ }
+
+ // Set the new target correctly.
+ if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "Old wallpaper still the target.");
+ }
+ mWallpaperTarget = oldW;
+ }
+
+ // Now set the upper and lower wallpaper targets
+ // correctly, and make sure that we are positioning
+ // the wallpaper below the lower.
+ if (foundI > oldI) {
+ // The new target is on top of the old one.
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "Found target above old target.");
+ }
+ mUpperWallpaperTarget = foundW;
+ mLowerWallpaperTarget = oldW;
+ foundW = oldW;
+ foundI = oldI;
+ } else {
+ // The new target is below the old one.
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "Found target below old target.");
+ }
+ mUpperWallpaperTarget = oldW;
+ mLowerWallpaperTarget = foundW;
+ }
+ }
+ }
+ }
+
+ } else if (mLowerWallpaperTarget != null) {
+ // Is it time to stop animating?
+ boolean lowerAnimating = mLowerWallpaperTarget.mAnimation != null
+ || (mLowerWallpaperTarget.mAppToken != null
+ && mLowerWallpaperTarget.mAppToken.animation != null);
+ boolean upperAnimating = mUpperWallpaperTarget.mAnimation != null
+ || (mUpperWallpaperTarget.mAppToken != null
+ && mUpperWallpaperTarget.mAppToken.animation != null);
+ if (!lowerAnimating || !upperAnimating) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "No longer animating wallpaper targets!");
+ }
+ mLowerWallpaperTarget = null;
+ mUpperWallpaperTarget = null;
+ }
+ }
+
+ boolean visible = foundW != null;
+ if (visible) {
+ // The window is visible to the compositor... but is it visible
+ // to the user? That is what the wallpaper cares about.
+ visible = isWallpaperVisible(foundW);
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper visibility: " + visible);
+
+ // If the wallpaper target is animating, we may need to copy
+ // its layer adjustment. Only do this if we are not transfering
+ // between two wallpaper targets.
+ mWallpaperAnimLayerAdjustment =
+ (mLowerWallpaperTarget == null && foundW.mAppToken != null)
+ ? foundW.mAppToken.animLayerAdjustment : 0;
+
+ final int maxLayer = mPolicy.getMaxWallpaperLayer()
+ * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
+
+ // Now w is the window we are supposed to be behind... but we
+ // need to be sure to also be behind any of its attached windows,
+ // 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);
+ if (wb.mBaseLayer < maxLayer &&
+ wb.mAttachedWindow != foundW &&
+ (wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
+ wb.mToken != foundW.mToken)) {
+ // This window is not related to the previous one in any
+ // interesting way, so stop here.
+ break;
+ }
+ foundW = wb;
+ foundI--;
+ }
+ } else {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "No wallpaper target");
+ }
+
+ if (foundW == null && topCurW != null) {
+ // There is no wallpaper target, so it goes at the bottom.
+ // We will assume it is the same place as last time, if known.
+ foundW = topCurW;
+ foundI = topCurI+1;
+ } 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;
+ }
+
+ if (visible) {
+ if (mWallpaperTarget.mWallpaperX >= 0) {
+ mLastWallpaperX = mWallpaperTarget.mWallpaperX;
+ mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
+ }
+ if (mWallpaperTarget.mWallpaperY >= 0) {
+ mLastWallpaperY = mWallpaperTarget.mWallpaperY;
+ mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
+ }
+ }
+
+ // Start stepping backwards from here, ensuring that our wallpaper windows
+ // are correctly placed.
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ if (token.hidden == visible) {
+ changed |= ADJUST_WALLPAPER_VISIBILITY_CHANGED;
+ token.hidden = !visible;
+ // Need to do a layout to ensure the wallpaper now has the
+ // correct size.
+ mLayoutNeeded = true;
+ }
+
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+
+ if (visible) {
+ updateWallpaperOffsetLocked(wallpaper, dw, dh, false);
+ }
+
+ // First, make sure the client has the current visibility
+ // state.
+ if (wallpaper.mWallpaperVisible != visible) {
+ wallpaper.mWallpaperVisible = visible;
+ try {
+ if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Log.v(TAG,
+ "Setting visibility of wallpaper " + wallpaper
+ + ": " + visible);
+ wallpaper.mClient.dispatchAppVisibility(visible);
+ } catch (RemoteException e) {
+ }
+ }
+
+ wallpaper.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper win "
+ + wallpaper + " anim layer: " + wallpaper.mAnimLayer);
+
+ // First, if this window is at the current index, then all
+ // is well.
+ if (wallpaper == foundW) {
+ foundI--;
+ foundW = foundI > 0
+ ? (WindowState)localmWindows.get(foundI-1) : null;
+ continue;
+ }
+
+ // The window didn't match... the current wallpaper window,
+ // wherever it is, is in the wrong place, so make sure it is
+ // not in the list.
+ int oldIndex = localmWindows.indexOf(wallpaper);
+ if (oldIndex >= 0) {
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Wallpaper removing at "
+ + oldIndex + ": " + wallpaper);
+ localmWindows.remove(oldIndex);
+ if (oldIndex < foundI) {
+ foundI--;
+ }
+ }
+
+ // Now stick it in.
+ if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT) Log.v(TAG,
+ "Moving wallpaper " + wallpaper
+ + " from " + oldIndex + " to " + foundI);
+
+ localmWindows.add(foundI, wallpaper);
+ changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
+ }
+ }
+
+ return changed;
+ }
+
+ void setWallpaperAnimLayerAdjustmentLocked(int adj) {
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG,
+ "Setting wallpaper layer adj to " + adj);
+ mWallpaperAnimLayerAdjustment = adj;
+ 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);
+ wallpaper.mAnimLayer = wallpaper.mLayer + adj;
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper win "
+ + wallpaper + " anim layer: " + wallpaper.mAnimLayer);
+ }
+ }
+ }
+
+ boolean updateWallpaperOffsetLocked(WindowState wallpaperWin, int dw, int dh,
+ boolean sync) {
+ boolean changed = false;
+ boolean rawChanged = false;
+ float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f;
+ float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
+ int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw;
+ int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0;
+ changed = wallpaperWin.mXOffset != offset;
+ if (changed) {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Update wallpaper "
+ + wallpaperWin + " x: " + offset);
+ wallpaperWin.mXOffset = offset;
+ }
+ if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
+ wallpaperWin.mWallpaperX = wpx;
+ wallpaperWin.mWallpaperXStep = wpxs;
+ rawChanged = true;
+ }
+
+ float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
+ float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
+ int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh;
+ offset = availh > 0 ? -(int)(availh*wpy+.5f) : 0;
+ if (wallpaperWin.mYOffset != offset) {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Update wallpaper "
+ + wallpaperWin + " y: " + offset);
+ changed = true;
+ wallpaperWin.mYOffset = offset;
+ }
+ if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
+ wallpaperWin.mWallpaperY = wpy;
+ wallpaperWin.mWallpaperYStep = wpys;
+ rawChanged = true;
+ }
+
+ if (rawChanged) {
+ try {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Report new wp offset "
+ + wallpaperWin + " x=" + wallpaperWin.mWallpaperX
+ + " y=" + wallpaperWin.mWallpaperY);
+ if (sync) {
+ mWaitingOnWallpaper = wallpaperWin;
+ }
+ wallpaperWin.mClient.dispatchWallpaperOffsets(
+ wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY,
+ wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync);
+ if (sync) {
+ if (mWaitingOnWallpaper != null) {
+ long start = SystemClock.uptimeMillis();
+ if ((mLastWallpaperTimeoutTime+WALLPAPER_TIMEOUT_RECOVERY)
+ < start) {
+ try {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Waiting for offset complete...");
+ mWindowMap.wait(WALLPAPER_TIMEOUT);
+ } catch (InterruptedException e) {
+ }
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Offset complete!");
+ if ((start+WALLPAPER_TIMEOUT)
+ < SystemClock.uptimeMillis()) {
+ Log.i(TAG, "Timeout waiting for wallpaper to offset: "
+ + wallpaperWin);
+ mLastWallpaperTimeoutTime = start;
+ }
+ }
+ mWaitingOnWallpaper = null;
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ return changed;
+ }
+
+ void wallpaperOffsetsComplete(IBinder window) {
+ synchronized (mWindowMap) {
+ if (mWaitingOnWallpaper != null &&
+ mWaitingOnWallpaper.mClient.asBinder() == window) {
+ mWaitingOnWallpaper = null;
+ mWindowMap.notifyAll();
+ }
+ }
+ }
+
+ boolean updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+
+ boolean changed = false;
+
+ WindowState target = mWallpaperTarget;
+ if (target != null) {
+ if (target.mWallpaperX >= 0) {
+ mLastWallpaperX = target.mWallpaperX;
+ } else if (changingTarget.mWallpaperX >= 0) {
+ mLastWallpaperX = changingTarget.mWallpaperX;
+ }
+ if (target.mWallpaperY >= 0) {
+ mLastWallpaperY = target.mWallpaperY;
+ } else if (changingTarget.mWallpaperY >= 0) {
+ mLastWallpaperY = changingTarget.mWallpaperY;
+ }
+ }
+
+ 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 (updateWallpaperOffsetLocked(wallpaper, dw, dh, sync)) {
+ wallpaper.computeShownFrameLocked();
+ changed = true;
+ // We only want to be synchronous with one wallpaper.
+ sync = false;
+ }
+ }
+ }
+
+ return changed;
+ }
+
+ void updateWallpaperVisibilityLocked() {
+ final boolean visible = isWallpaperVisible(mWallpaperTarget);
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ if (token.hidden == visible) {
+ token.hidden = !visible;
+ // Need to do a layout to ensure the wallpaper now has the
+ // correct size.
+ mLayoutNeeded = true;
+ }
+
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+ if (visible) {
+ updateWallpaperOffsetLocked(wallpaper, dw, dh, false);
+ }
+
+ if (wallpaper.mWallpaperVisible != visible) {
+ wallpaper.mWallpaperVisible = visible;
+ try {
+ if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Log.v(TAG,
+ "Updating visibility of wallpaper " + wallpaper
+ + ": " + visible);
+ wallpaper.mClient.dispatchAppVisibility(visible);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+ }
+
+ 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) {
+ Log.w(TAG, "Failure sending pointer to wallpaper", e);
+ }
+ }
+ }
+ }
+
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
@@ -1216,6 +1829,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
+ attrs.token + ". Aborting.");
return WindowManagerImpl.ADD_BAD_APP_TOKEN;
}
+ if (attrs.type == TYPE_WALLPAPER) {
+ Log.w(TAG, "Attempted to add wallpaper window with unknown token "
+ + attrs.token + ". Aborting.");
+ return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ }
token = new WindowToken(attrs.token, -1, false);
addToken = true;
} else if (attrs.type >= FIRST_APPLICATION_WINDOW
@@ -1242,6 +1860,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
+ attrs.token + ". Aborting.");
return WindowManagerImpl.ADD_BAD_APP_TOKEN;
}
+ } else if (attrs.type == TYPE_WALLPAPER) {
+ if (token.windowType != TYPE_WALLPAPER) {
+ Log.w(TAG, "Attempted to add wallpaper window with bad token "
+ + attrs.token + ". Aborting.");
+ return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ }
}
win = new WindowState(session, client, token,
@@ -1292,6 +1916,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
imMayMove = false;
} else {
addWindowToListInOrderLocked(win, true);
+ if (attrs.type == TYPE_WALLPAPER) {
+ mLastWallpaperTimeoutTime = 0;
+ adjustWallpaperWindowsLocked();
+ } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+ adjustWallpaperWindowsLocked();
+ }
}
win.mEnterAnimationPending = true;
@@ -1432,6 +2062,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
private void removeWindowInnerLocked(Session session, WindowState win) {
+ mKeyWaiter.finishedKey(session, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
mKeyWaiter.releasePendingPointerLocked(win.mSession);
mKeyWaiter.releasePendingTrackballLocked(win.mSession);
@@ -1441,11 +2073,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
moveInputMethodWindowsIfNeededLocked(false);
}
+ if (false) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "Removing window " + win, e);
+ }
+
mPolicy.removeWindowLw(win);
win.removeLocked();
mWindowMap.remove(win.mClient.asBinder());
mWindows.remove(win);
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Final remove of window: " + win);
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
@@ -1490,6 +2129,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ if (win.mAttrs.type == TYPE_WALLPAPER) {
+ mLastWallpaperTimeoutTime = 0;
+ adjustWallpaperWindowsLocked();
+ } else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+ adjustWallpaperWindowsLocked();
+ }
+
if (!mInLayout) {
assignLayersLocked();
mLayoutNeeded = true;
@@ -1506,10 +2152,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
synchronized (mWindowMap) {
WindowState w = windowForClientLocked(session, client);
if ((w != null) && (w.mSurface != null)) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION");
Surface.openTransaction();
try {
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface
+ + ": transparentRegionHint=" + region);
w.mSurface.setTransparentRegionHint(region);
} finally {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION");
Surface.closeTransaction();
}
}
@@ -1552,6 +2203,60 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ public void setWindowWallpaperPositionLocked(WindowState window, float x, float y,
+ float xStep, float yStep) {
+ if (window.mWallpaperX != x || window.mWallpaperY != y) {
+ window.mWallpaperX = x;
+ window.mWallpaperY = y;
+ window.mWallpaperXStep = xStep;
+ window.mWallpaperYStep = yStep;
+ if (updateWallpaperOffsetLocked(window, true)) {
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+ }
+
+ void wallpaperCommandComplete(IBinder window, Bundle result) {
+ synchronized (mWindowMap) {
+ if (mWaitingOnWallpaper != null &&
+ mWaitingOnWallpaper.mClient.asBinder() == window) {
+ mWaitingOnWallpaper = null;
+ mWindowMap.notifyAll();
+ }
+ }
+ }
+
+ public Bundle sendWindowWallpaperCommandLocked(WindowState window,
+ String action, int x, int y, int z, Bundle extras, boolean sync) {
+ if (window == mWallpaperTarget || window == mLowerWallpaperTarget
+ || window == mUpperWallpaperTarget) {
+ boolean doWait = sync;
+ 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);
+ try {
+ wallpaper.mClient.dispatchWallpaperCommand(action,
+ x, y, z, extras, sync);
+ // We only want to be synchronous with one wallpaper.
+ sync = false;
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ if (doWait) {
+ // XXX Need to wait for result.
+ }
+ }
+
+ return null;
+ }
+
public int relayoutWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
@@ -1610,6 +2315,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
|| ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
+ boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
+ && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+
win.mRelayoutCalled = true;
final int oldVisibility = win.mViewVisibility;
win.mViewVisibility = viewVisibility;
@@ -1631,6 +2339,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
&& !win.mCommitDrawPending && !mDisplayFrozen) {
applyEnterAnimationLocked(win);
}
+ if (displayed && (win.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
+ win.mTurnOnScreen = true;
+ }
if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
// To change the format, we need to re-build the surface.
win.destroySurfaceLocked();
@@ -1640,13 +2352,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Surface surface = win.createSurfaceLocked();
if (surface != null) {
outSurface.copyFrom(surface);
+ win.mReportDestroySurface = false;
+ win.mSurfacePendingDestroy = false;
+ if (SHOW_TRANSACTIONS) Log.i(TAG,
+ " OUT SURFACE " + outSurface + ": copied");
} else {
- outSurface.clear();
+ // For some reason there isn't a surface. Clear the
+ // caller's object so they see the same state.
+ outSurface.release();
}
} catch (Exception e) {
Log.w(TAG, "Exception thrown when creating surface for client "
- + client + " (" + win.mAttrs.getTitle() + ")",
- e);
+ + client + " (" + win.mAttrs.getTitle() + ")",
+ e);
Binder.restoreCallingIdentity(origId);
return 0;
}
@@ -1661,17 +2379,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
} else {
win.mEnterAnimationPending = false;
if (win.mSurface != null) {
+ if (DEBUG_VISIBILITY) Log.i(TAG, "Relayout invis " + win
+ + ": mExiting=" + win.mExiting
+ + " mSurfacePendingDestroy=" + win.mSurfacePendingDestroy);
// If we are not currently running the exit animation, we
// need to see about starting one.
- if (!win.mExiting) {
+ if (!win.mExiting || win.mSurfacePendingDestroy) {
// Try starting an animation; if there isn't one, we
// can destroy the surface right away.
int transit = WindowManagerPolicy.TRANSIT_EXIT;
if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
- if (win.isWinVisibleLw() &&
+ if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() &&
applyAnimationLocked(win, transit, false)) {
+ focusMayChange = true;
win.mExiting = true;
mKeyWaiter.finishedKey(session, client, true,
KeyWaiter.RETURN_NOTHING);
@@ -1679,6 +2401,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// Currently in a hide animation... turn this into
// an exit.
win.mExiting = true;
+ } else if (win == mWallpaperTarget) {
+ // If the wallpaper is currently behind this
+ // window, we need to change both of them inside
+ // of a transaction to avoid artifacts.
+ win.mExiting = true;
+ win.mAnimating = true;
} else {
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
@@ -1687,7 +2415,23 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
}
- outSurface.clear();
+
+ if (win.mSurface == null || (win.getAttrs().flags
+ & WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0
+ || win.mSurfacePendingDestroy) {
+ // We are being called from a local process, which
+ // means outSurface holds its current surface. Ensure the
+ // surface object is cleared, but we don't want it actually
+ // destroyed at this point.
+ win.mSurfacePendingDestroy = false;
+ outSurface.release();
+ if (DEBUG_VISIBILITY) Log.i(TAG, "Releasing surface in: " + win);
+ } else if (win.mSurface != null) {
+ if (DEBUG_VISIBILITY) Log.i(TAG,
+ "Keeping surface, will report destroy: " + win);
+ win.mReportDestroySurface = true;
+ outSurface.copyFrom(win.mSurface);
+ }
}
if (focusMayChange) {
@@ -1707,6 +2451,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
assignLayers = true;
}
}
+ if (wallpaperMayMove) {
+ if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+ assignLayers = true;
+ }
+ }
mLayoutNeeded = true;
win.mGivenInsetsPending = insetsPending;
@@ -1715,6 +2464,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
newConfig = updateOrientationFromAppTokensLocked(null, null);
performLayoutAndPlaceSurfacesLocked();
+ if (displayed && win.mIsWallpaper) {
+ updateWallpaperOffsetLocked(win, mDisplay.getWidth(),
+ mDisplay.getHeight(), false);
+ }
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
@@ -1750,6 +2503,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client);
if (win != null && win.finishDrawingLocked()) {
+ if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+ adjustWallpaperWindowsLocked();
+ }
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
@@ -1778,6 +2534,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return null;
}
+ private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
+ if (DEBUG_ANIM) Log.v(TAG, "Loading animations: params package="
+ + packageName + " resId=0x" + Integer.toHexString(resId));
+ if (packageName != null) {
+ if ((resId&0xFF000000) == 0x01000000) {
+ packageName = "android";
+ }
+ if (DEBUG_ANIM) Log.v(TAG, "Loading animations: picked package="
+ + packageName);
+ return AttributeCache.instance().get(packageName, resId,
+ com.android.internal.R.styleable.WindowAnimation);
+ }
+ return null;
+ }
+
private void applyEnterAnimationLocked(WindowState win) {
int transit = WindowManagerPolicy.TRANSIT_SHOW;
if (win.mEnterAnimationPending) {
@@ -1832,7 +2603,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
+ if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
Log.v(TAG, "Loaded animation " + a + " for " + win, e);
}
win.setAnimation(a);
@@ -1861,6 +2632,22 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return null;
}
+ private Animation loadAnimation(String packageName, int resId) {
+ int anim = 0;
+ Context context = mContext;
+ if (resId >= 0) {
+ AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
+ if (ent != null) {
+ context = ent.context;
+ anim = resId;
+ }
+ }
+ if (anim != 0) {
+ return AnimationUtils.loadAnimation(context, anim);
+ }
+ return null;
+ }
+
private boolean applyAnimationLocked(AppWindowToken wtoken,
WindowManager.LayoutParams lp, int transit, boolean enter) {
// Only apply an animation if the display isn't frozen. If it is
@@ -1873,6 +2660,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
a = new FadeInOutAnimation(enter);
if (DEBUG_ANIM) Log.v(TAG,
"applying FadeInOutAnimation for a window in compatibility mode");
+ } else if (mNextAppTransitionPackage != null) {
+ a = loadAnimation(mNextAppTransitionPackage, enter ?
+ mNextAppTransitionEnter : mNextAppTransitionExit);
} else {
int animAttr = 0;
switch (transit) {
@@ -1906,8 +2696,28 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
: com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
break;
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
+ break;
}
- a = loadAnimation(lp, animAttr);
+ a = animAttr != 0 ? loadAnimation(lp, animAttr) : null;
if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken
+ " anim=" + a
+ " animAttr=0x" + Integer.toHexString(animAttr)
@@ -1916,7 +2726,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
+ if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
Log.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
}
wtoken.setAnimation(a);
@@ -2002,6 +2812,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
wtoken = new WindowToken(token, type, true);
mTokenMap.put(token, wtoken);
mTokenList.add(wtoken);
+ if (type == TYPE_WALLPAPER) {
+ mWallpaperTokens.add(wtoken);
+ }
}
}
@@ -2047,6 +2860,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (delayed) {
mExitingTokens.add(wtoken);
+ } else if (wtoken.windowType == TYPE_WALLPAPER) {
+ mWallpaperTokens.remove(wtoken);
}
}
@@ -2206,10 +3021,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Configuration config;
synchronized(mWindowMap) {
config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded);
- }
- if (config != null) {
- mLayoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
+ if (config != null) {
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
}
return config;
}
@@ -2259,7 +3074,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mTempConfiguration.setToDefaults();
if (computeNewConfigurationLocked(mTempConfiguration)) {
if (appConfig.diff(mTempConfiguration) != 0) {
- Log.i(TAG, "Config changed: " + mTempConfiguration);
return new Configuration(mTempConfiguration);
}
}
@@ -2351,7 +3165,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
TAG, "Prepare app transition: transit=" + transit
+ " mNextAppTransition=" + mNextAppTransition);
if (!mDisplayFrozen) {
- if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
+ if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
+ || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
mNextAppTransition = transit;
} else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
&& mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) {
@@ -2377,6 +3192,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return mNextAppTransition;
}
+ public void overridePendingAppTransition(String packageName,
+ int enterAnim, int exitAnim) {
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ mNextAppTransitionPackage = packageName;
+ mNextAppTransitionEnter = enterAnim;
+ mNextAppTransitionExit = exitAnim;
+ }
+ }
+
public void executeAppTransition() {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"executeAppTransition()")) {
@@ -2384,9 +3208,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
synchronized(mWindowMap) {
- if (DEBUG_APP_TRANSITIONS) Log.v(
- TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition);
- if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ if (DEBUG_APP_TRANSITIONS) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "Execute app transition: mNextAppTransition="
+ + mNextAppTransition, e);
+ }
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mAppTransitionReady = true;
final long origId = Binder.clearCallingIdentity();
performLayoutAndPlaceSurfacesLocked();
@@ -2453,11 +3281,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
startingWindow.mToken = wtoken;
startingWindow.mRootToken = wtoken;
startingWindow.mAppToken = wtoken;
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG,
+ "Removing starting window: " + startingWindow);
mWindows.remove(startingWindow);
ttoken.windows.remove(startingWindow);
ttoken.allAppWindows.remove(startingWindow);
addWindowToListInOrderLocked(startingWindow, true);
- wtoken.allAppWindows.add(startingWindow);
// Propagate other interesting state between the
// tokens. If the old token is displayed, we should
@@ -2519,6 +3348,27 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return;
}
+ // If this is a translucent or wallpaper window, then don't
+ // show a starting window -- the current effect (a full-screen
+ // opaque starting window that fades away to the real contents
+ // when it is ready) does not work for this.
+ if (theme != 0) {
+ AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+ com.android.internal.R.styleable.Window);
+ if (ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsTranslucent, false)) {
+ return;
+ }
+ if (ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsFloating, false)) {
+ return;
+ }
+ if (ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
+ return;
+ }
+ }
+
mStartingIconInTransition = true;
wtoken.startingData = new StartingData(
pkg, theme, nonLocalizedLabel,
@@ -2568,7 +3418,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
boolean runningAppAnimation = false;
- if (transit != WindowManagerPolicy.TRANSIT_NONE) {
+ if (transit != WindowManagerPolicy.TRANSIT_UNSET) {
if (wtoken.animation == sDummyAnimation) {
wtoken.animation = null;
}
@@ -2659,7 +3509,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
+ if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
Log.v(TAG, "setAppVisibility(" + token + ", " + visible
+ "): mNextAppTransition=" + mNextAppTransition
+ " hidden=" + wtoken.hidden
@@ -2668,7 +3518,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
- if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
// Already in requested state, don't do anything more.
if (wtoken.hiddenRequested != visible) {
return;
@@ -2680,12 +3530,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
wtoken.setDummyAnimation();
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
+ wtoken.waitingToShow = wtoken.waitingToHide = false;
wtoken.inPendingTransaction = true;
if (visible) {
mOpeningApps.add(wtoken);
wtoken.allDrawn = false;
wtoken.startingDisplayed = false;
wtoken.startingMoved = false;
+ wtoken.waitingToShow = true;
if (wtoken.clientHidden) {
// In the case where we are making an app visible
@@ -2699,12 +3551,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
} else {
mClosingApps.add(wtoken);
+ wtoken.waitingToHide = true;
}
return;
}
final long origId = Binder.clearCallingIdentity();
- setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_NONE, true);
+ setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, true);
wtoken.updateReportedVisibilityLocked();
Binder.restoreCallingIdentity(origId);
}
@@ -2748,7 +3601,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
int configChanges) {
if (DEBUG_ORIENTATION) {
RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
+ if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
Log.i(TAG, "Set freezing of " + wtoken.appToken
+ ": hidden=" + wtoken.hidden + " freezing="
+ wtoken.freezingScreen, e);
@@ -2830,13 +3683,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mTokenList.remove(basewtoken);
if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "Removing app token: " + wtoken);
- delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_NONE, true);
+ delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_UNSET, true);
wtoken.inPendingTransaction = false;
mOpeningApps.remove(wtoken);
+ wtoken.waitingToShow = false;
if (mClosingApps.contains(wtoken)) {
delayed = true;
- } else if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ } else if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mClosingApps.add(wtoken);
+ wtoken.waitingToHide = true;
delayed = true;
}
if (DEBUG_APP_TRANSITIONS) Log.v(
@@ -2846,8 +3701,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (delayed) {
// set the token aside because it has an active animation to be finished
mExitingAppTokens.add(wtoken);
+ } else {
+ // Make sure there is no animation running on this token,
+ // so any windows associated with it will be removed as
+ // soon as their animations are complete
+ wtoken.animation = null;
+ wtoken.animating = false;
}
mAppTokens.remove(wtoken);
+ if (mLastEnterAnimToken == wtoken) {
+ mLastEnterAnimToken = null;
+ mLastEnterAnimParams = null;
+ }
wtoken.removed = true;
if (wtoken.startingData != null) {
startingToken = wtoken;
@@ -2881,11 +3746,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final int NW = token.windows.size();
for (int i=0; i<NW; i++) {
WindowState win = token.windows.get(i);
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Tmp removing app window " + win);
mWindows.remove(win);
int j = win.mChildWindows.size();
while (j > 0) {
j--;
- mWindows.remove(win.mChildWindows.get(j));
+ WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG,
+ "Tmp removing child window " + cwin);
+ mWindows.remove(cwin);
}
}
return NW > 0;
@@ -2923,6 +3792,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);
if (DEBUG_REORDER) Log.v(TAG, "Looking for lower windows @ "
+ tokenPos + " -- " + wtoken.token);
+ if (wtoken.sendingToBottom) {
+ if (DEBUG_REORDER) Log.v(TAG,
+ "Skipping token -- currently sending to bottom");
+ tokenPos--;
+ continue;
+ }
int i = wtoken.windows.size();
while (i > 0) {
i--;
@@ -2931,7 +3806,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
while (j > 0) {
j--;
WindowState cwin = (WindowState)win.mChildWindows.get(j);
- if (cwin.mSubLayer >= 0 ) {
+ if (cwin.mSubLayer >= 0) {
for (int pos=NW-1; pos>=0; pos--) {
if (mWindows.get(pos) == cwin) {
if (DEBUG_REORDER) Log.v(TAG,
@@ -2960,14 +3835,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
for (int j=0; j<NCW; j++) {
WindowState cwin = (WindowState)win.mChildWindows.get(j);
if (!added && cwin.mSubLayer >= 0) {
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding child window at "
+ + index + ": " + cwin);
mWindows.add(index, win);
index++;
added = true;
}
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding window at "
+ + index + ": " + cwin);
mWindows.add(index, cwin);
index++;
}
if (!added) {
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding window at "
+ + index + ": " + win);
mWindows.add(index, win);
index++;
}
@@ -3035,6 +3916,26 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ private void moveAppWindowsLocked(AppWindowToken wtoken, int tokenPos,
+ boolean updateFocusAndLayout) {
+ // First remove all of the windows from the list.
+ tmpRemoveAppWindowsLocked(wtoken);
+
+ // Where to start adding?
+ int pos = findWindowOffsetLocked(tokenPos);
+
+ // And now add them back at the correct place.
+ pos = reAddAppWindowsLocked(pos, wtoken);
+
+ if (updateFocusAndLayout) {
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
+ assignLayersLocked();
+ }
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+
private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) {
// First remove all of the windows from the list.
final int N = tokens.size();
@@ -3057,7 +3958,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
+ assignLayersLocked();
+ }
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
@@ -3078,9 +3981,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
AppWindowToken wt = findAppWindowToken(tokens.get(i));
if (wt != null) {
mAppTokens.add(wt);
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ mToTopApps.remove(wt);
+ mToBottomApps.remove(wt);
+ mToTopApps.add(wt);
+ wt.sendingToBottom = false;
+ wt.sendingToTop = true;
+ }
}
}
- moveAppWindowsLocked(tokens, mAppTokens.size());
+
+ if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) {
+ moveAppWindowsLocked(tokens, mAppTokens.size());
+ }
}
Binder.restoreCallingIdentity(origId);
}
@@ -3100,10 +4013,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
AppWindowToken wt = findAppWindowToken(tokens.get(i));
if (wt != null) {
mAppTokens.add(pos, wt);
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ mToTopApps.remove(wt);
+ mToBottomApps.remove(wt);
+ mToBottomApps.add(i, wt);
+ wt.sendingToTop = false;
+ wt.sendingToBottom = true;
+ }
pos++;
}
}
- moveAppWindowsLocked(tokens, 0);
+
+ if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) {
+ moveAppWindowsLocked(tokens, 0);
+ }
}
Binder.restoreCallingIdentity(origId);
}
@@ -3113,15 +4036,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// -------------------------------------------------------------
public void disableKeyguard(IBinder token, String tag) {
- if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
- mKeyguardDisabled.acquire(token, tag);
+ synchronized (mKeyguardDisabled) {
+ mKeyguardDisabled.acquire(token, tag);
+ }
}
public void reenableKeyguard(IBinder token) {
- if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
@@ -3147,7 +4072,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
* @see android.app.KeyguardManager#exitKeyguardSecurely
*/
public void exitKeyguardSecurely(final IOnKeyguardExitResult callback) {
- if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
@@ -3166,6 +4091,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return mPolicy.inKeyguardRestrictedKeyInputMode();
}
+ public void closeSystemDialogs(String reason) {
+ synchronized(mWindowMap) {
+ for (int i=mWindows.size()-1; i>=0; i--) {
+ WindowState w = (WindowState)mWindows.get(i);
+ if (w.mSurface != null) {
+ try {
+ w.mClient.closeSystemDialogs(reason);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+ }
+
static float fixScale(float scale) {
if (scale < 0) scale = 0;
else if (scale > 20) scale = 20;
@@ -3242,7 +4181,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
"getScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return KeyInputQueue.getScancodeState(sw);
+ return mQueue.getScancodeState(sw);
}
public int getScancodeStateForDevice(int devid, int sw) {
@@ -3250,7 +4189,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
"getScancodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return KeyInputQueue.getScancodeState(devid, sw);
+ return mQueue.getScancodeState(devid, sw);
}
public int getKeycodeState(int sw) {
@@ -3258,7 +4197,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
"getKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return KeyInputQueue.getKeycodeState(sw);
+ return mQueue.getKeycodeState(sw);
}
public int getKeycodeStateForDevice(int devid, int sw) {
@@ -3266,7 +4205,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
"getKeycodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return KeyInputQueue.getKeycodeState(devid, sw);
+ return mQueue.getKeycodeState(devid, sw);
}
public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
@@ -3308,7 +4247,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final int N = mWindows.size();
for (int i=0; i<N; i++) {
WindowState w = (WindowState)mWindows.get(i);
- if (w.isVisibleLw() && !w.isDisplayedLw()) {
+ if (w.isVisibleLw() && !w.isDrawnLw()) {
return;
}
}
@@ -3735,19 +4674,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (!computeNewConfigurationLocked(config)) {
return null;
}
- Log.i(TAG, "Config changed: " + config);
- long now = SystemClock.uptimeMillis();
- //Log.i(TAG, "Config changing, gc pending: " + mFreezeGcPending + ", now " + now);
- if (mFreezeGcPending != 0) {
- if (now > (mFreezeGcPending+1000)) {
- //Log.i(TAG, "Gc! " + now + " > " + (mFreezeGcPending+1000));
- mH.removeMessages(H.FORCE_GC);
- Runtime.getRuntime().gc();
- mFreezeGcPending = now;
- }
- } else {
- mFreezeGcPending = now;
- }
return config;
}
@@ -3833,7 +4759,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
long curTime = SystemClock.uptimeMillis();
- if (eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
+ if (eventType == TOUCH_EVENT || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
if (mLastTouchEventType == eventType &&
(curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) {
return;
@@ -3893,8 +4819,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.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();
@@ -3915,6 +4849,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (action != MotionEvent.ACTION_MOVE) {
Log.w(TAG, "No window to dispatch pointer action " + ev.getAction());
}
+ synchronized (mWindowMap) {
+ if (mSendingPointersToWallpaper) {
+ Log.i(TAG, "Sending skipped pointer to wallpaper!");
+ sendPointerToWallpaperLocked(null, ev, ev.getEventTime());
+ }
+ }
if (qev != null) {
mQueue.recycleEvent(qev);
}
@@ -3922,6 +4862,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return INJECT_FAILED;
}
if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+ synchronized (mWindowMap) {
+ if (mSendingPointersToWallpaper) {
+ Log.i(TAG, "Sending skipped pointer to wallpaper!");
+ sendPointerToWallpaperLocked(null, ev, ev.getEventTime());
+ }
+ }
if (qev != null) {
mQueue.recycleEvent(qev);
}
@@ -3932,7 +4878,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
WindowState target = (WindowState)targetObj;
final long eventTime = ev.getEventTime();
-
+ final long eventTimeNano = ev.getEventTimeNano();
+
//Log.i(TAG, "Sending " + ev + " to " + target);
if (uid != 0 && uid != target.mSession.mUid) {
@@ -3949,6 +4896,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return INJECT_NO_PERMISSION;
}
}
+
+ if (MEASURE_LATENCY) {
+ lt.sample("4 in dispatchPointer ", System.nanoTime() - eventTimeNano);
+ }
if ((target.mAttrs.flags &
WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
@@ -4032,7 +4983,24 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ if (MEASURE_LATENCY) {
+ lt.sample("5 in dispatchPointer ", System.nanoTime() - eventTimeNano);
+ }
+
synchronized(mWindowMap) {
+ if (!target.isVisibleLw()) {
+ // During this motion dispatch, the target window has become
+ // invisible.
+ if (mSendingPointersToWallpaper) {
+ sendPointerToWallpaperLocked(null, ev, eventTime);
+ }
+ 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);
@@ -4047,7 +5015,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final Rect frame = out.mFrame;
oev.offsetLocation(-(float)frame.left, -(float)frame.top);
try {
- out.mClient.dispatchPointer(oev, eventTime);
+ out.mClient.dispatchPointer(oev, eventTime, false);
} catch (android.os.RemoteException e) {
Log.i(TAG, "WINDOW DIED during outside motion dispatch: " + out);
}
@@ -4057,6 +5025,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mKeyWaiter.mOutsideTouchTargets = null;
}
}
+
+ // If we are on top of the wallpaper, then the wallpaper also
+ // gets to see this movement.
+ if (mWallpaperTarget == target || mSendingPointersToWallpaper) {
+ sendPointerToWallpaperLocked(null, ev, eventTime);
+ }
+
final Rect frame = target.mFrame;
ev.offsetLocation(-(float)frame.left, -(float)frame.top);
mKeyWaiter.bindTargetWindowLocked(target);
@@ -4069,7 +5044,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) {
Log.v(TAG, "Delivering pointer " + qev + " to " + target);
}
- target.mClient.dispatchPointer(ev, eventTime);
+
+ if (MEASURE_LATENCY) {
+ lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano);
+ }
+
+ 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) {
Log.i(TAG, "WINDOW DIED during motion dispatch: " + target);
@@ -4141,7 +5125,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
try {
- focus.mClient.dispatchTrackball(ev, eventTime);
+ focus.mClient.dispatchTrackball(ev, eventTime, true);
return INJECT_SUCCEEDED;
} catch (android.os.RemoteException e) {
Log.i(TAG, "WINDOW DIED during key dispatch: " + focus);
@@ -4172,6 +5156,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
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) Log.v(
@@ -4664,7 +5658,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED) {
mPolicy.interceptKeyTi(null, keycode,
- nextKey.getMetaState(), down, repeatCount);
+ nextKey.getMetaState(), down, repeatCount,
+ nextKey.getFlags());
}
Log.w(TAG, "Event timeout during app switch: dropping "
+ nextKey);
@@ -4687,7 +5682,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED) {
if (mPolicy.interceptKeyTi(focus,
- keycode, nextKey.getMetaState(), down, repeatCount)) {
+ keycode, nextKey.getMetaState(), down, repeatCount,
+ nextKey.getFlags())) {
return CONSUMED_EVENT_TOKEN;
}
}
@@ -4914,14 +5910,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return null;
}
+ MotionEvent res = null;
+ QueuedEvent qev = null;
+ WindowState win = null;
+
synchronized (this) {
if (DEBUG_INPUT) Log.v(
TAG, "finishedKey: client=" + client.asBinder()
+ ", force=" + force + ", last=" + mLastBinder
+ " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")");
- QueuedEvent qev = null;
- WindowState win = null;
if (returnWhat == RETURN_PENDING_POINTER) {
qev = session.mPendingPointerMove;
win = session.mPendingPointerWindow;
@@ -4951,17 +5949,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
if (qev != null) {
- MotionEvent res = (MotionEvent)qev.event;
+ res = (MotionEvent)qev.event;
if (DEBUG_INPUT) Log.v(TAG,
"Returning pending motion: " + res);
mQueue.recycleEvent(qev);
if (win != null && returnWhat == RETURN_PENDING_POINTER) {
res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
}
- return res;
}
- return null;
+
+ if (res != null && returnWhat == RETURN_PENDING_POINTER) {
+ synchronized (mWindowMap) {
+ if (mWallpaperTarget == win || mSendingPointersToWallpaper) {
+ sendPointerToWallpaperLocked(win, res, res.getEventTime());
+ }
+ }
+ }
}
+
+ return res;
}
void tickle() {
@@ -5107,7 +6113,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
PowerManager.WakeLock mHoldingScreen;
KeyQ() {
- super(mContext);
+ super(mContext, WindowManagerService.this);
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"KEEP_SCREEN_ON_FLAG");
@@ -5140,8 +6146,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
// XXX end hack
- boolean screenIsOff = !mPowerManager.screenIsOn();
- boolean screenIsDim = !mPowerManager.screenIsBright();
+ boolean screenIsOff = !mPowerManager.isScreenOn();
+ boolean screenIsDim = !mPowerManager.isScreenBright();
int actions = mPolicy.interceptKeyTq(event, !screenIsOff);
if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {
@@ -5171,8 +6177,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
case RawInputEvent.EV_REL: {
- boolean screenIsOff = !mPowerManager.screenIsOn();
- boolean screenIsDim = !mPowerManager.screenIsBright();
+ boolean screenIsOff = !mPowerManager.isScreenOn();
+ boolean screenIsDim = !mPowerManager.isScreenBright();
if (screenIsOff) {
if (!mPolicy.isWakeRelMovementTq(event.deviceId,
device.classes, event)) {
@@ -5188,8 +6194,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
case RawInputEvent.EV_ABS: {
- boolean screenIsOff = !mPowerManager.screenIsOn();
- boolean screenIsDim = !mPowerManager.screenIsBright();
+ boolean screenIsOff = !mPowerManager.isScreenOn();
+ boolean screenIsDim = !mPowerManager.isScreenBright();
if (screenIsOff) {
if (!mPolicy.isWakeAbsMovementTq(event.deviceId,
device.classes, event)) {
@@ -5238,7 +6244,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
}
- };
+ }
public boolean detectSafeMode() {
mSafeMode = mPolicy.detectSafeMode();
@@ -5278,6 +6284,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// 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;
@@ -5303,9 +6310,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_INPUT && ev != null) Log.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 = ev.when;
+ curTime = SystemClock.uptimeMillis();
int eventType;
if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
eventType = eventType((MotionEvent)ev.event);
@@ -5316,31 +6334,45 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
eventType = LocalPowerManager.OTHER_EVENT;
}
try {
- long now = SystemClock.uptimeMillis();
-
- if ((now - mLastBatteryStatsCallTime)
+ if ((curTime - mLastBatteryStatsCallTime)
>= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastBatteryStatsCallTime = now;
+ mLastBatteryStatsCallTime = curTime;
mBatteryStats.noteInputEvent();
}
} catch (RemoteException e) {
// Ignore
}
- mPowerManager.userActivity(curTime, false, eventType, false);
+
+ 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()) {
lastKey = ke;
+ downTime = curTime;
keyRepeatCount = 0;
lastKeyTime = curTime;
nextKeyTime = lastKeyTime
- + KEY_REPEAT_FIRST_DELAY;
+ + ViewConfiguration.getLongPressTimeout();
if (DEBUG_INPUT) Log.v(
TAG, "Received key down: first repeat @ "
+ nextKeyTime);
} else {
lastKey = null;
+ downTime = 0;
// Arbitrary long timeout.
lastKeyTime = curTime;
nextKeyTime = curTime + LONG_WAIT;
@@ -5388,7 +6420,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_INPUT) Log.v(
TAG, "Key repeat: count=" + keyRepeatCount
+ ", next @ " + nextKeyTime);
- dispatchKey(KeyEvent.changeTimeRepeat(lastKey, curTime, keyRepeatCount), 0, 0);
+ 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();
@@ -5592,11 +6636,47 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
+ synchronized(mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ setWindowWallpaperPositionLocked(windowForClientLocked(this, window),
+ x, y, xStep, yStep);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void wallpaperOffsetsComplete(IBinder window) {
+ WindowManagerService.this.wallpaperOffsetsComplete(window);
+ }
+
+ public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
+ int z, Bundle extras, boolean sync) {
+ synchronized(mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return sendWindowWallpaperCommandLocked(
+ windowForClientLocked(this, window),
+ action, x, y, z, extras, sync);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void wallpaperCommandComplete(IBinder window, Bundle result) {
+ WindowManagerService.this.wallpaperCommandComplete(window, result);
+ }
+
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (localLOGV) Log.v(
TAG, "First window added to " + this + ", creating SurfaceSession");
mSurfaceSession = new SurfaceSession();
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " NEW SURFACE SESSION " + mSurfaceSession);
mSessions.add(this);
}
mNumWindow++;
@@ -5614,6 +6694,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (localLOGV) Log.v(
TAG, "Last window removed from " + this
+ ", destroying " + mSurfaceSession);
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " KILL SURFACE SESSION " + mSurfaceSession);
try {
mSurfaceSession.kill();
} catch (Exception e) {
@@ -5667,23 +6749,28 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final int mSubLayer;
final boolean mLayoutAttached;
final boolean mIsImWindow;
+ final boolean mIsWallpaper;
+ final boolean mIsFloatingLayer;
int mViewVisibility;
boolean mPolicyVisibility = true;
boolean mPolicyVisibilityAfterAnim = true;
boolean mAppFreezing;
Surface mSurface;
+ boolean mReportDestroySurface;
+ boolean mSurfacePendingDestroy;
boolean mAttachedHidden; // is our parent window hidden?
boolean mLastHidden; // was this window last hidden?
+ boolean mWallpaperVisible; // for wallpaper, what was last vis report?
int mRequestedWidth;
int mRequestedHeight;
int mLastRequestedWidth;
int mLastRequestedHeight;
- int mReqXPos;
- int mReqYPos;
int mLayer;
int mAnimLayer;
int mLastLayer;
boolean mHaveFrame;
+ boolean mObscured;
+ boolean mTurnOnScreen;
WindowState mNextOutsideTouch;
@@ -5764,6 +6851,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
boolean mHasLocalTransformation;
final Transformation mTransformation = new Transformation();
+ // If a window showing a wallpaper: the requested offset for the
+ // wallpaper; if a wallpaper window: the currently applied offset.
+ float mWallpaperX = -1;
+ float mWallpaperY = -1;
+
+ // If a window showing a wallpaper: what fraction of the offset
+ // range corresponds to a full virtual screen.
+ float mWallpaperXStep = -1;
+ float mWallpaperYStep = -1;
+
+ // Wallpaper windows: pixels offset based on above variables.
+ int mXOffset;
+ int mYOffset;
+
// This is set after IWindowSession.relayout() has been called at
// least once for the window. It allows us to detect the situation
// where we don't yet have a surface, but should have one soon, so
@@ -5824,6 +6925,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mAttachedWindow = null;
mLayoutAttached = false;
mIsImWindow = false;
+ mIsWallpaper = false;
+ mIsFloatingLayer = false;
mBaseLayer = 0;
mSubLayer = 0;
return;
@@ -5844,6 +6947,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD
|| attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+ mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER;
+ mIsFloatingLayer = mIsImWindow || mIsWallpaper;
} else {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
@@ -5855,6 +6960,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+ mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
+ mIsFloatingLayer = mIsImWindow || mIsWallpaper;
}
WindowState appWin = this;
@@ -5877,8 +6984,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
- mReqXPos = 0;
- mReqYPos = 0;
+ mXOffset = 0;
+ mYOffset = 0;
mLayer = 0;
mAnimLayer = 0;
mLastLayer = 0;
@@ -5926,6 +7033,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
visible.set(vf);
final Rect frame = mFrame;
+ final int fw = frame.width();
+ final int fh = frame.height();
//System.out.println("In: w=" + w + " h=" + h + " container=" +
// container + " x=" + mAttrs.x + " y=" + mAttrs.y);
@@ -5962,6 +7071,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
visibleInsets.right = frame.right-visible.right;
visibleInsets.bottom = frame.bottom-visible.bottom;
+ if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
+ updateWallpaperOffsetLocked(this, mDisplay.getWidth(),
+ mDisplay.getHeight(), false);
+ }
+
if (localLOGV) {
//if ("com.google.android.youtube".equals(mAttrs.packageName)
// && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
@@ -6051,6 +7165,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Surface createSurfaceLocked() {
if (mSurface == null) {
+ mReportDestroySurface = false;
+ mSurfacePendingDestroy = false;
mDrawPending = true;
mCommitDrawPending = false;
mReadyToShow = false;
@@ -6059,11 +7175,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
int flags = 0;
- if (mAttrs.memoryType == MEMORY_TYPE_HARDWARE) {
- flags |= Surface.HARDWARE;
- } else if (mAttrs.memoryType == MEMORY_TYPE_GPU) {
- flags |= Surface.GPU;
- } else if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
+ if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
flags |= Surface.PUSH_BUFFERS;
}
@@ -6086,10 +7198,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
h = mRequestedHeight;
}
+ // Something is wrong and SurfaceFlinger will not like this,
+ // try to revert to sane values
+ if (w <= 0) w = 1;
+ if (h <= 0) h = 1;
+
try {
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
0, w, h, mAttrs.format, flags);
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " CREATE SURFACE "
+ + mSurface + " IN SESSION "
+ + mSession.mSurfaceSession
+ + ": pid=" + mSession.mPid + " format="
+ + mAttrs.format + " flags=0x"
+ + Integer.toHexString(flags));
} catch (Surface.OutOfResourcesException e) {
Log.w(TAG, "OutOfResourcesException creating surface");
reclaimSomeSurfaceMemoryLocked(this, "create");
@@ -6114,10 +7237,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Surface.openTransaction();
try {
try {
- mSurface.setPosition(mFrame.left, mFrame.top);
+ mSurface.setPosition(mFrame.left + mXOffset,
+ mFrame.top + mYOffset);
mSurface.setLayer(mAnimLayer);
mSurface.hide();
if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " SURFACE "
+ + mSurface + ": DITHER");
mSurface.setFlags(Surface.SURFACE_DITHER,
Surface.SURFACE_DITHER);
}
@@ -6149,34 +7275,50 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mAppToken.startingDisplayed = false;
}
- if (localLOGV) Log.v(
- TAG, "Window " + this
- + " destroying surface " + mSurface + ", session " + mSession);
if (mSurface != null) {
+ mDrawPending = false;
+ mCommitDrawPending = false;
+ mReadyToShow = false;
+
+ int i = mChildWindows.size();
+ while (i > 0) {
+ i--;
+ WindowState c = (WindowState)mChildWindows.get(i);
+ c.mAttachedHidden = true;
+ }
+
+ if (mReportDestroySurface) {
+ mReportDestroySurface = false;
+ mSurfacePendingDestroy = true;
+ try {
+ mClient.dispatchGetNewSurface();
+ // We'll really destroy on the next time around.
+ return;
+ } catch (RemoteException e) {
+ }
+ }
+
try {
+ if (DEBUG_VISIBILITY) {
+ RuntimeException e = new RuntimeException();
+ if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
+ Log.w(TAG, "Window " + this + " destroying surface "
+ + mSurface + ", session " + mSession, e);
+ }
if (SHOW_TRANSACTIONS) {
RuntimeException ex = new RuntimeException();
- ex.fillInStackTrace();
+ if (!HIDE_STACK_CRAWLS) ex.fillInStackTrace();
Log.i(TAG, " SURFACE " + mSurface + ": DESTROY ("
+ mAttrs.getTitle() + ")", ex);
}
- mSurface.clear();
+ mSurface.destroy();
} catch (RuntimeException e) {
Log.w(TAG, "Exception thrown when destroying Window " + this
+ " surface " + mSurface + " session " + mSession
+ ": " + e.toString());
}
+
mSurface = null;
- mDrawPending = false;
- mCommitDrawPending = false;
- mReadyToShow = false;
-
- int i = mChildWindows.size();
- while (i > 0) {
- i--;
- WindowState c = (WindowState)mChildWindows.get(i);
- c.mAttachedHidden = true;
- }
}
}
@@ -6192,10 +7334,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
// This must be called while inside a transaction.
- void commitFinishDrawingLocked(long currentTime) {
+ boolean commitFinishDrawingLocked(long currentTime) {
//Log.i(TAG, "commitFinishDrawingLocked: " + mSurface);
if (!mCommitDrawPending) {
- return;
+ return false;
}
mCommitDrawPending = false;
mReadyToShow = true;
@@ -6204,13 +7346,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (atoken == null || atoken.allDrawn || starting) {
performShowLocked();
}
+ return true;
}
// This must be called while inside a transaction.
boolean performShowLocked() {
if (DEBUG_VISIBILITY) {
RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
+ if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
Log.v(TAG, "performShow on " + this
+ ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay()
+ " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e);
@@ -6223,7 +7366,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
+ " attHidden=" + mAttachedHidden
+ " tok.hiddenRequested="
+ (mAppToken != null ? mAppToken.hiddenRequested : false)
- + " tok.idden="
+ + " tok.hidden="
+ (mAppToken != null ? mAppToken.hidden : false)
+ " animating=" + mAnimating
+ " tok animating="
@@ -6252,10 +7395,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (mAttrs.type != TYPE_APPLICATION_STARTING
&& mAppToken != null) {
mAppToken.firstWindowDrawn = true;
- if (mAnimation == null && mAppToken.startingData != null) {
- if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Finish starting "
- + mToken
+
+ if (mAppToken.startingData != null) {
+ if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Log.v(TAG,
+ "Finish starting " + mToken
+ ": first real window is shown, no animation");
+ // If this initial window is animating, stop it -- we
+ // will do an animation to reveal it from behind the
+ // starting window, so there is no need for it to also
+ // be doing its own stuff.
+ if (mAnimation != null) {
+ mAnimation = null;
+ // Make sure we clean up the animation.
+ mAnimating = true;
+ }
mFinishedStarting.add(mAppToken);
mH.sendEmptyMessage(H.FINISHED_STARTING);
}
@@ -6302,7 +7455,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
mHasLocalTransformation = false;
if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null
- && mAppToken.hasTransformation) {
+ && mAppToken.animation != null) {
// When our app token is animating, we kind-of pretend like
// we are as well. Note the mLocalAnimating mAnimationIsEntrance
// part of this check means that we will only do this if
@@ -6344,6 +7497,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mAnimLayer = mLayer;
if (mIsImWindow) {
mAnimLayer += mInputMethodAnimLayerAdjustment;
+ } else if (mIsWallpaper) {
+ mAnimLayer += mWallpaperAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this
+ " anim layer: " + mAnimLayer);
@@ -6430,6 +7585,30 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Transformation appTransformation =
(mAppToken != null && mAppToken.hasTransformation)
? mAppToken.transformation : null;
+
+ // Wallpapers are animated based on the "real" window they
+ // are currently targeting.
+ if (mAttrs.type == TYPE_WALLPAPER && mLowerWallpaperTarget == null
+ && mWallpaperTarget != null) {
+ if (mWallpaperTarget.mHasLocalTransformation &&
+ mWallpaperTarget.mAnimation != null &&
+ !mWallpaperTarget.mAnimation.getDetachWallpaper()) {
+ attachedTransformation = mWallpaperTarget.mTransformation;
+ if (DEBUG_WALLPAPER && attachedTransformation != null) {
+ Log.v(TAG, "WP target attached xform: " + attachedTransformation);
+ }
+ }
+ if (mWallpaperTarget.mAppToken != null &&
+ mWallpaperTarget.mAppToken.hasTransformation &&
+ mWallpaperTarget.mAppToken.animation != null &&
+ !mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
+ appTransformation = mWallpaperTarget.mAppToken.transformation;
+ if (DEBUG_WALLPAPER && appTransformation != null) {
+ Log.v(TAG, "WP target app xform: " + appTransformation);
+ }
+ }
+ }
+
if (selfTransformation || attachedTransformation != null
|| appTransformation != null) {
// cache often used attributes locally
@@ -6438,15 +7617,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final Matrix tmpMatrix = mTmpMatrix;
// Compute the desired transformation.
- tmpMatrix.setTranslate(frame.left, frame.top);
+ tmpMatrix.setTranslate(0, 0);
if (selfTransformation) {
- tmpMatrix.preConcat(mTransformation.getMatrix());
+ tmpMatrix.postConcat(mTransformation.getMatrix());
}
+ tmpMatrix.postTranslate(frame.left, frame.top);
if (attachedTransformation != null) {
- tmpMatrix.preConcat(attachedTransformation.getMatrix());
+ tmpMatrix.postConcat(attachedTransformation.getMatrix());
}
if (appTransformation != null) {
- tmpMatrix.preConcat(appTransformation.getMatrix());
+ tmpMatrix.postConcat(appTransformation.getMatrix());
}
// "convert" it into SurfaceFlinger's format
@@ -6460,8 +7640,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mDtDx = tmpFloats[Matrix.MSKEW_X];
mDsDy = tmpFloats[Matrix.MSKEW_Y];
mDtDy = tmpFloats[Matrix.MSCALE_Y];
- int x = (int)tmpFloats[Matrix.MTRANS_X];
- int y = (int)tmpFloats[Matrix.MTRANS_Y];
+ int x = (int)tmpFloats[Matrix.MTRANS_X] + mXOffset;
+ int y = (int)tmpFloats[Matrix.MTRANS_Y] + mYOffset;
int w = frame.width();
int h = frame.height();
mShownFrame.set(x, y, x+w, y+h);
@@ -6498,6 +7678,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
mShownFrame.set(mFrame);
+ if (mXOffset != 0 || mYOffset != 0) {
+ mShownFrame.offset(mXOffset, mYOffset);
+ }
mShownAlpha = mAlpha;
mDsDx = 1;
mDtDx = 0;
@@ -6518,6 +7701,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
/**
+ * Like {@link #isVisibleLw}, but also counts a window that is currently
+ * "hidden" behind the keyguard as visible. This allows us to apply
+ * things like window flags that impact the keyguard.
+ * XXX I am starting to think we need to have ANOTHER visibility flag
+ * for this "hidden behind keyguard" state rather than overloading
+ * mPolicyVisibility. Ungh.
+ */
+ public boolean isVisibleOrBehindKeyguardLw() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && !mAttachedHidden
+ && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
+ && !mExiting && !mDestroying;
+ }
+
+ /**
* Is this window visible, ignoring its app token? It is not visible
* if there is no surface, or we are in the process of running an exit animation
* that will remove the surface.
@@ -6544,7 +7742,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
*/
boolean isVisibleOrAdding() {
final AppWindowToken atoken = mAppToken;
- return (mSurface != null
+ return ((mSurface != null && !mReportDestroySurface)
|| (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& mPolicyVisibility && !mAttachedHidden
&& (atoken == null || !atoken.hiddenRequested)
@@ -6561,10 +7759,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (atoken != null) {
return mSurface != null && mPolicyVisibility && !mDestroying
&& ((!mAttachedHidden && !atoken.hiddenRequested)
- || mAnimating || atoken.animating);
+ || mAnimation != null || atoken.animation != null);
} else {
return mSurface != null && mPolicyVisibility && !mDestroying
- && (!mAttachedHidden || mAnimating);
+ && (!mAttachedHidden || mAnimation != null);
}
}
@@ -6573,11 +7771,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
* of a transition that has not yet been started.
*/
boolean isReadyForDisplay() {
+ if (mRootToken.waitingToShow &&
+ mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ return false;
+ }
final AppWindowToken atoken = mAppToken;
- final boolean animating = atoken != null ? atoken.animating : false;
+ final boolean animating = atoken != null
+ ? (atoken.animation != null) : false;
return mSurface != null && mPolicyVisibility && !mDestroying
- && ((!mAttachedHidden && !mRootToken.hidden)
- || mAnimating || animating);
+ && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
+ && !mRootToken.hidden)
+ || mAnimation != null || animating);
}
/** Is the window or its container currently animating? */
@@ -6609,6 +7813,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
|| mAnimating);
}
+ /**
+ * Returns true if the window has a surface that it has drawn a
+ * complete UI in to.
+ */
+ public boolean isDrawnLw() {
+ final AppWindowToken atoken = mAppToken;
+ return mSurface != null && !mDestroying
+ && !mDrawPending && !mCommitDrawPending;
+ }
+
public boolean fillsScreenLw(int screenWidth, int screenHeight,
boolean shownFrame, boolean onlyOpaque) {
if (mSurface == null) {
@@ -6635,11 +7849,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
/**
- * Return true if the window is opaque and fully drawn.
+ * Return true if the window is opaque and fully drawn. This indicates
+ * it may obscure windows behind it.
*/
boolean isOpaqueDrawn() {
- return mAttrs.format == PixelFormat.OPAQUE && mSurface != null
- && mAnimation == null && !mDrawPending && !mCommitDrawPending;
+ return (mAttrs.format == PixelFormat.OPAQUE
+ || mAttrs.type == TYPE_WALLPAPER)
+ && mSurface != null && mAnimation == null
+ && (mAppToken == null || mAppToken.animation == null)
+ && !mDrawPending && !mCommitDrawPending;
}
boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
@@ -6659,7 +7877,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
boolean isFullscreen(int screenWidth, int screenHeight) {
return mFrame.left <= 0 && mFrame.top <= 0 &&
- mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
+ mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
}
void removeLocked() {
@@ -6705,38 +7923,50 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
public boolean showLw(boolean doAnimation) {
- if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim) {
- mPolicyVisibility = true;
- mPolicyVisibilityAfterAnim = true;
- if (doAnimation) {
- applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
- }
+ return showLw(doAnimation, true);
+ }
+
+ boolean showLw(boolean doAnimation, boolean requestAnim) {
+ if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+ return false;
+ }
+ mPolicyVisibility = true;
+ mPolicyVisibilityAfterAnim = true;
+ if (doAnimation) {
+ applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
+ }
+ if (requestAnim) {
requestAnimationLocked(0);
- return true;
}
- return false;
+ return true;
}
public boolean hideLw(boolean doAnimation) {
+ return hideLw(doAnimation, true);
+ }
+
+ boolean hideLw(boolean doAnimation, boolean requestAnim) {
boolean current = doAnimation ? mPolicyVisibilityAfterAnim
: mPolicyVisibility;
- if (current) {
- if (doAnimation) {
- applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
- if (mAnimation == null) {
- doAnimation = false;
- }
- }
- if (doAnimation) {
- mPolicyVisibilityAfterAnim = false;
- } else {
- mPolicyVisibilityAfterAnim = false;
- mPolicyVisibility = false;
+ if (!current) {
+ return false;
+ }
+ if (doAnimation) {
+ applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
+ if (mAnimation == null) {
+ doAnimation = false;
}
+ }
+ if (doAnimation) {
+ mPolicyVisibilityAfterAnim = false;
+ } else {
+ mPolicyVisibilityAfterAnim = false;
+ mPolicyVisibility = false;
+ }
+ if (requestAnim) {
requestAnimationLocked(0);
- return true;
}
- return false;
+ return true;
}
void dump(PrintWriter pw, String prefix) {
@@ -6749,8 +7979,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow);
pw.print(" mLayoutAttached="); pw.println(mLayoutAttached);
}
- if (mIsImWindow) {
- pw.print(prefix); pw.print("mIsImWindow="); pw.println(mIsImWindow);
+ if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
+ pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow);
+ pw.print(" mIsWallpaper="); pw.print(mIsWallpaper);
+ pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer);
+ pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible);
}
pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
pw.print(" mSubLayer="); pw.print(mSubLayer);
@@ -6773,7 +8006,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("mViewVisibility=0x");
pw.print(Integer.toHexString(mViewVisibility));
pw.print(" mLastHidden="); pw.print(mLastHidden);
- pw.print(" mHaveFrame="); pw.println(mHaveFrame);
+ pw.print(" mHaveFrame="); pw.print(mHaveFrame);
+ pw.print(" mObscured="); pw.println(mObscured);
if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) {
pw.print(prefix); pw.print("mPolicyVisibility=");
pw.print(mPolicyVisibility);
@@ -6782,9 +8016,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(" mAttachedHidden="); pw.println(mAttachedHidden);
}
pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
- pw.print(" h="); pw.print(mRequestedHeight);
- pw.print(" x="); pw.print(mReqXPos);
- pw.print(" y="); pw.println(mReqYPos);
+ pw.print(" h="); pw.println(mRequestedHeight);
+ if (mXOffset != 0 || mYOffset != 0) {
+ pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
+ pw.print(" y="); pw.println(mYOffset);
+ }
pw.print(prefix); pw.print("mGivenContentInsets=");
mGivenContentInsets.printShortString(pw);
pw.print(" mGivenVisibleInsets=");
@@ -6843,15 +8079,24 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(" mDestroying="); pw.print(mDestroying);
pw.print(" mRemoved="); pw.println(mRemoved);
}
- if (mOrientationChanging || mAppFreezing) {
+ if (mOrientationChanging || mAppFreezing || mTurnOnScreen) {
pw.print(prefix); pw.print("mOrientationChanging=");
pw.print(mOrientationChanging);
- pw.print(" mAppFreezing="); pw.println(mAppFreezing);
+ pw.print(" mAppFreezing="); pw.print(mAppFreezing);
+ pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen);
}
if (mHScale != 1 || mVScale != 1) {
pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
pw.print(" mVScale="); pw.println(mVScale);
}
+ if (mWallpaperX != -1 || mWallpaperY != -1) {
+ pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX);
+ pw.print(" mWallpaperY="); pw.println(mWallpaperY);
+ }
+ if (mWallpaperXStep != -1 || mWallpaperYStep != -1) {
+ pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep);
+ pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
+ }
}
@Override
@@ -6895,6 +8140,22 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// Temporary for finding which tokens no longer have visible windows.
boolean hasVisible;
+ // Set to true when this token is in a pending transaction where it
+ // will be shown.
+ boolean waitingToShow;
+
+ // Set to true when this token is in a pending transaction where it
+ // will be hidden.
+ boolean waitingToHide;
+
+ // Set to true when this token is in a pending transaction where its
+ // windows will be put to the bottom of the list.
+ boolean sendingToBottom;
+
+ // Set to true when this token is in a pending transaction where its
+ // windows will be put to the top of the list.
+ boolean sendingToTop;
+
WindowToken(IBinder _token, int type, boolean _explicit) {
token = _token;
windowType = type;
@@ -6907,6 +8168,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
pw.print(" hidden="); pw.print(hidden);
pw.print(" hasVisible="); pw.println(hasVisible);
+ if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) {
+ pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
+ pw.print(" waitingToHide="); pw.print(waitingToHide);
+ pw.print(" sendingToBottom="); pw.print(sendingToBottom);
+ pw.print(" sendingToTop="); pw.println(sendingToTop);
+ }
}
@Override
@@ -7036,6 +8303,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (w == mInputMethodTarget) {
setInputMethodAnimLayerAdjustment(adj);
}
+ if (w == mWallpaperTarget && mLowerWallpaperTarget == null) {
+ setWallpaperAnimLayerAdjustmentLocked(adj);
+ }
}
}
@@ -7073,7 +8343,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (animation == sDummyAnimation) {
// This guy is going to animate, but not yet. For now count
- // it is not animating for purposes of scheduling transactions;
+ // it as not animating for purposes of scheduling transactions;
// when it is really time to animate, this will be set to
// a real animation and the next call will execute normally.
return false;
@@ -7161,10 +8431,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
continue;
}
if (DEBUG_VISIBILITY) {
- Log.v(TAG, "Win " + win + ": isDisplayed="
- + win.isDisplayedLw()
+ Log.v(TAG, "Win " + win + ": isDrawn="
+ + win.isDrawnLw()
+ ", isAnimating=" + win.isAnimating());
- if (!win.isDisplayedLw()) {
+ if (!win.isDrawnLw()) {
Log.v(TAG, "Not displayed: s=" + win.mSurface
+ " pv=" + win.mPolicyVisibility
+ " dp=" + win.mDrawPending
@@ -7177,7 +8447,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
numInteresting++;
- if (win.isDisplayedLw()) {
+ if (win.isDrawnLw()) {
if (!win.isAnimating()) {
numVisible++;
}
@@ -7204,6 +8474,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ WindowState findMainWindow() {
+ int j = windows.size();
+ while (j > 0) {
+ j--;
+ WindowState win = windows.get(j);
+ if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
+ || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
+ return win;
+ }
+ }
+ return null;
+ }
+
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
if (appToken != null) {
@@ -7213,6 +8496,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
}
pw.print(prefix); pw.print("groupId="); pw.print(groupId);
+ pw.print(" appFullscreen="); pw.print(appFullscreen);
pw.print(" requestedOrientation="); pw.println(requestedOrientation);
pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
pw.print(" clientHidden="); pw.print(clientHidden);
@@ -7269,71 +8553,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- public static WindowManager.LayoutParams findAnimations(
- ArrayList<AppWindowToken> order,
- ArrayList<AppWindowToken> openingTokenList1,
- ArrayList<AppWindowToken> closingTokenList2) {
- // We need to figure out which animation to use...
-
- // First, check if there is a compatible window in opening/closing
- // apps, and use it if exists.
- WindowManager.LayoutParams animParams = null;
- int animSrc = 0;
- animParams = findCompatibleWindowParams(openingTokenList1);
- if (animParams == null) {
- animParams = findCompatibleWindowParams(closingTokenList2);
- }
- if (animParams != null) {
- return animParams;
- }
-
- //Log.i(TAG, "Looking for animations...");
- for (int i=order.size()-1; i>=0; i--) {
- AppWindowToken wtoken = order.get(i);
- //Log.i(TAG, "Token " + wtoken + " with " + wtoken.windows.size() + " windows");
- if (openingTokenList1.contains(wtoken) || closingTokenList2.contains(wtoken)) {
- int j = wtoken.windows.size();
- while (j > 0) {
- j--;
- WindowState win = wtoken.windows.get(j);
- //Log.i(TAG, "Window " + win + ": type=" + win.mAttrs.type);
- if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
- || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
- //Log.i(TAG, "Found base or application window, done!");
- if (wtoken.appFullscreen) {
- return win.mAttrs;
- }
- if (animSrc < 2) {
- animParams = win.mAttrs;
- animSrc = 2;
- }
- } else if (animSrc < 1 && win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION) {
- //Log.i(TAG, "Found normal window, we may use this...");
- animParams = win.mAttrs;
- animSrc = 1;
- }
- }
- }
- }
-
- return animParams;
- }
-
- private static LayoutParams findCompatibleWindowParams(ArrayList<AppWindowToken> tokenList) {
- for (int appCount = tokenList.size() - 1; appCount >= 0; appCount--) {
- AppWindowToken wtoken = tokenList.get(appCount);
- // Just checking one window is sufficient as all windows have the compatible flag
- // if the application is in compatibility mode.
- if (wtoken.windows.size() > 0) {
- WindowManager.LayoutParams params = wtoken.windows.get(0).mAttrs;
- if ((params.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
- return params;
- }
- }
- }
- return null;
- }
-
// -------------------------------------------------------------
// DummyAnimation
// -------------------------------------------------------------
@@ -7650,7 +8869,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
case APP_TRANSITION_TIMEOUT: {
synchronized (mWindowMap) {
- if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
"*** APP TRANSITION TIMEOUT");
mAppTransitionReady = true;
@@ -7778,6 +8997,57 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return win;
}
+ final void rebuildAppWindowListLocked() {
+ int NW = mWindows.size();
+ int i;
+ int lastWallpaper = -1;
+ int numRemoved = 0;
+
+ // First remove all existing app windows.
+ i=0;
+ while (i < NW) {
+ WindowState w = (WindowState)mWindows.get(i);
+ if (w.mAppToken != null) {
+ WindowState win = (WindowState)mWindows.remove(i);
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG,
+ "Rebuild removing window: " + win);
+ NW--;
+ numRemoved++;
+ continue;
+ } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER
+ && lastWallpaper == i-1) {
+ lastWallpaper = i;
+ }
+ i++;
+ }
+
+ // The wallpaper window(s) typically live at the bottom of the stack,
+ // so skip them before adding app tokens.
+ lastWallpaper++;
+ i = lastWallpaper;
+
+ // First add all of the exiting app tokens... these are no longer
+ // in the main app list, but still have windows shown. We put them
+ // in the back because now that the animation is over we no longer
+ // will care about them.
+ int NT = mExitingAppTokens.size();
+ for (int j=0; j<NT; j++) {
+ i = reAddAppWindowsLocked(i, mExitingAppTokens.get(j));
+ }
+
+ // And add in the still active app tokens in Z order.
+ NT = mAppTokens.size();
+ for (int j=0; j<NT; j++) {
+ i = reAddAppWindowsLocked(i, mAppTokens.get(j));
+ }
+
+ i -= lastWallpaper;
+ if (i != numRemoved) {
+ Log.w(TAG, "Rebuild removed " + numRemoved
+ + " windows but added " + i);
+ }
+ }
+
private final void assignLayersLocked() {
int N = mWindows.size();
int curBaseLayer = 0;
@@ -7786,7 +9056,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
for (i=0; i<N; i++) {
WindowState w = (WindowState)mWindows.get(i);
- if (w.mBaseLayer == curBaseLayer || w.mIsImWindow) {
+ if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
+ || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
w.mLayer = curLayer;
} else {
@@ -7802,6 +9073,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
if (w.mIsImWindow) {
w.mAnimLayer += mInputMethodAnimLayerAdjustment;
+ } else if (w.mIsWallpaper) {
+ w.mAnimLayer += mWallpaperAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": "
+ w.mAnimLayer);
@@ -7897,7 +9170,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
|| !win.mRelayoutCalled
|| win.mRootToken.hidden
|| (atoken != null && atoken.hiddenRequested)
- || !win.mPolicyVisibility
|| win.mAttachedHidden
|| win.mExiting || win.mDestroying;
@@ -7935,13 +9207,34 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- if (!mPolicy.finishLayoutLw()) {
+ int changes = mPolicy.finishLayoutLw();
+ if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+ assignLayersLocked();
+ }
+ }
+ if (changes == 0) {
mLayoutNeeded = false;
} else if (repeats > 2) {
Log.w(TAG, "Layout repeat aborted after too many iterations");
mLayoutNeeded = false;
+ if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ Configuration newConfig = updateOrientationFromAppTokensLocked(
+ null, null);
+ if (newConfig != null) {
+ mLayoutNeeded = true;
+ mH.sendEmptyMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION);
+ }
+ }
} else {
repeats++;
+ if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ Configuration newConfig = updateOrientationFromAppTokensLocked(
+ null, null);
+ if (newConfig != null) {
+ mH.sendEmptyMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION);
+ }
+ }
}
}
}
@@ -7952,7 +9245,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final int dw = mDisplay.getWidth();
final int dh = mDisplay.getHeight();
- final int N = mWindows.size();
int i;
// FIRST LOOP: Perform a layout, if needed.
@@ -7984,6 +9276,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Surface.openTransaction();
try {
boolean restart;
+ boolean forceHiding = false;
do {
final int transactionSequence = ++mTransactionSequence;
@@ -8008,9 +9301,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
restart = false;
boolean tokenMayBeDrawn = false;
+ boolean wallpaperMayChange = false;
+ boolean focusMayChange = false;
+ boolean wallpaperForceHidingChanged = false;
mPolicy.beginAnimationLw(dw, dh);
+ final int N = mWindows.size();
+
for (i=N-1; i>=0; i--) {
WindowState w = (WindowState)mWindows.get(i);
@@ -8018,12 +9316,64 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (w.mSurface != null) {
// Execute animation.
- w.commitFinishDrawingLocked(currentTime);
+ if (w.commitFinishDrawingLocked(currentTime)) {
+ if ((w.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "First draw done in potential wallpaper target " + w);
+ wallpaperMayChange = true;
+ }
+ }
+
+ boolean wasAnimating = w.mAnimating;
if (w.stepAnimationLocked(currentTime, dw, dh)) {
animating = true;
//w.dump(" ");
}
-
+ if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) {
+ wallpaperMayChange = true;
+ }
+
+ if (mPolicy.doesForceHide(w, attrs)) {
+ if (!wasAnimating && animating) {
+ wallpaperForceHidingChanged = true;
+ focusMayChange = true;
+ } else if (w.isReadyForDisplay() && w.mAnimation == null) {
+ forceHiding = true;
+ }
+ } else if (mPolicy.canBeForceHidden(w, attrs)) {
+ boolean changed;
+ if (forceHiding) {
+ changed = w.hideLw(false, false);
+ } else {
+ changed = w.showLw(false, false);
+ if (changed && wallpaperForceHidingChanged
+ && w.isReadyForDisplay()) {
+ // Assume we will need to animate. If
+ // we don't (because the wallpaper will
+ // stay with the lock screen), then we will
+ // clean up later.
+ Animation a = mPolicy.createForceHideEnterAnimation();
+ if (a != null) {
+ w.setAnimation(a);
+ }
+ }
+ }
+ if (changed && (attrs.flags
+ & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ wallpaperMayChange = true;
+ }
+ if (changed && !forceHiding
+ && (mCurrentFocus == null)
+ && (mFocusedApp != null)) {
+ // It's possible that the last focus recalculation left no
+ // current focused window even though the app has come to the
+ // foreground already. In this case, we make sure to recalculate
+ // focus when we show a window.
+ focusMayChange = true;
+ }
+ }
+
mPolicy.animatingWindowLw(w, attrs);
}
@@ -8038,10 +9388,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
== WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
&& !w.mExiting && !w.mDestroying) {
if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
- Log.v(TAG, "Eval win " + w + ": isDisplayed="
- + w.isDisplayedLw()
+ Log.v(TAG, "Eval win " + w + ": isDrawn="
+ + w.isDrawnLw()
+ ", isAnimating=" + w.isAnimating());
- if (!w.isDisplayedLw()) {
+ if (!w.isDrawnLw()) {
Log.v(TAG, "Not displayed: s=" + w.mSurface
+ " pv=" + w.mPolicyVisibility
+ " dp=" + w.mDrawPending
@@ -8054,7 +9404,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (w != atoken.startingWindow) {
if (!atoken.freezingScreen || !w.mAppFreezing) {
atoken.numInterestingWindows++;
- if (w.isDisplayedLw()) {
+ if (w.isDrawnLw()) {
atoken.numDrawnWindows++;
if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Log.v(TAG,
"tokenMayBeDrawn: " + atoken
@@ -8063,7 +9413,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
tokenMayBeDrawn = true;
}
}
- } else if (w.isDisplayedLw()) {
+ } else if (w.isDrawnLw()) {
atoken.startingDisplayed = true;
}
}
@@ -8145,20 +9495,136 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "**** GOOD TO GO");
int transit = mNextAppTransition;
if (mSkipAppTransitionAnimation) {
- transit = WindowManagerPolicy.TRANSIT_NONE;
+ transit = WindowManagerPolicy.TRANSIT_UNSET;
}
- mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+ mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
mAppTransitionReady = false;
+ mAppTransitionRunning = true;
mAppTransitionTimeout = false;
mStartingIconInTransition = false;
mSkipAppTransitionAnimation = false;
mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
- // We need to figure out which animation to use...
- WindowManager.LayoutParams lp = findAnimations(mAppTokens,
- mOpeningApps, mClosingApps);
-
+ // If there are applications waiting to come to the
+ // top of the stack, now is the time to move their windows.
+ // (Note that we don't do apps going to the bottom
+ // here -- we want to keep their windows in the old
+ // Z-order until the animation completes.)
+ if (mToTopApps.size() > 0) {
+ NN = mAppTokens.size();
+ for (i=0; i<NN; i++) {
+ AppWindowToken wtoken = mAppTokens.get(i);
+ if (wtoken.sendingToTop) {
+ wtoken.sendingToTop = false;
+ moveAppWindowsLocked(wtoken, NN, false);
+ }
+ }
+ mToTopApps.clear();
+ }
+
+ WindowState oldWallpaper = mWallpaperTarget;
+
+ adjustWallpaperWindowsLocked();
+ wallpaperMayChange = false;
+
+ // The top-most window will supply the layout params,
+ // and we will determine it below.
+ LayoutParams animLp = null;
+ AppWindowToken animToken = null;
+ int bestAnimLayer = -1;
+
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "New wallpaper target=" + mWallpaperTarget
+ + ", lower target=" + mLowerWallpaperTarget
+ + ", upper target=" + mUpperWallpaperTarget);
+ int foundWallpapers = 0;
+ // Do a first pass through the tokens for two
+ // things:
+ // (1) Determine if both the closing and opening
+ // app token sets are wallpaper targets, in which
+ // case special animations are needed
+ // (since the wallpaper needs to stay static
+ // behind them).
+ // (2) Find the layout params of the top-most
+ // application window in the tokens, which is
+ // what will control the animation theme.
+ final int NC = mClosingApps.size();
+ NN = NC + mOpeningApps.size();
+ for (i=0; i<NN; i++) {
+ AppWindowToken wtoken;
+ int mode;
+ if (i < NC) {
+ wtoken = mClosingApps.get(i);
+ mode = 1;
+ } else {
+ wtoken = mOpeningApps.get(i-NC);
+ mode = 2;
+ }
+ if (mLowerWallpaperTarget != null) {
+ if (mLowerWallpaperTarget.mAppToken == wtoken
+ || mUpperWallpaperTarget.mAppToken == wtoken) {
+ foundWallpapers |= mode;
+ }
+ }
+ if (wtoken.appFullscreen) {
+ WindowState ws = wtoken.findMainWindow();
+ if (ws != null) {
+ // If this is a compatibility mode
+ // window, we will always use its anim.
+ if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) {
+ animLp = ws.mAttrs;
+ animToken = ws.mAppToken;
+ bestAnimLayer = Integer.MAX_VALUE;
+ } else if (ws.mLayer > bestAnimLayer) {
+ animLp = ws.mAttrs;
+ animToken = ws.mAppToken;
+ bestAnimLayer = ws.mLayer;
+ }
+ }
+ }
+ }
+
+ if (foundWallpapers == 3) {
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "Wallpaper animation!");
+ switch (transit) {
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+ case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+ case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+ transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN;
+ break;
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+ case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
+ case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
+ transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE;
+ break;
+ }
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "New transit: " + transit);
+ } else if (oldWallpaper != null) {
+ // We are transitioning from an activity with
+ // a wallpaper to one without.
+ transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE;
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "New transit away from wallpaper: " + transit);
+ } else if (mWallpaperTarget != null) {
+ // We are transitioning from an activity without
+ // a wallpaper to now showing the wallpaper
+ transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN;
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "New transit into wallpaper: " + transit);
+ }
+
+ if ((transit&WindowManagerPolicy.TRANSIT_ENTER_MASK) != 0) {
+ mLastEnterAnimToken = animToken;
+ mLastEnterAnimParams = animLp;
+ } else if (mLastEnterAnimParams != null) {
+ animLp = mLastEnterAnimParams;
+ mLastEnterAnimToken = null;
+ mLastEnterAnimParams = null;
+ }
+
NN = mOpeningApps.size();
for (i=0; i<NN; i++) {
AppWindowToken wtoken = mOpeningApps.get(i);
@@ -8166,8 +9632,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
"Now opening app" + wtoken);
wtoken.reportedVisible = false;
wtoken.inPendingTransaction = false;
- setTokenVisibilityLocked(wtoken, lp, true, transit, false);
+ wtoken.animation = null;
+ setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
wtoken.updateReportedVisibilityLocked();
+ wtoken.waitingToShow = false;
wtoken.showAllWindowsLocked();
}
NN = mClosingApps.size();
@@ -8176,14 +9644,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
"Now closing app" + wtoken);
wtoken.inPendingTransaction = false;
- setTokenVisibilityLocked(wtoken, lp, false, transit, false);
+ wtoken.animation = null;
+ setTokenVisibilityLocked(wtoken, animLp, false, transit, false);
wtoken.updateReportedVisibilityLocked();
+ wtoken.waitingToHide = false;
// Force the allDrawn flag, because we want to start
// this guy's animations regardless of whether it's
// gotten drawn.
wtoken.allDrawn = true;
}
+ mNextAppTransitionPackage = null;
+
mOpeningApps.clear();
mClosingApps.clear();
@@ -8195,10 +9667,102 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
performLayoutLockedInner();
updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES);
+ focusMayChange = false;
restart = true;
}
}
+
+ if (!animating && mAppTransitionRunning) {
+ // We have finished the animation of an app transition. To do
+ // this, we have delayed a lot of operations like showing and
+ // hiding apps, moving apps in Z-order, etc. The app token list
+ // reflects the correct Z-order, but the window list may now
+ // be out of sync with it. So here we will just rebuild the
+ // entire app window list. Fun!
+ mAppTransitionRunning = false;
+ // Clear information about apps that were moving.
+ mToBottomApps.clear();
+
+ rebuildAppWindowListLocked();
+ restart = true;
+ moveInputMethodWindowsIfNeededLocked(false);
+ wallpaperMayChange = true;
+ mLayoutNeeded = true;
+ // Since the window list has been rebuilt, focus might
+ // have to be recomputed since the actual order of windows
+ // might have changed again.
+ focusMayChange = true;
+ }
+
+ int adjResult = 0;
+
+ if (wallpaperForceHidingChanged) {
+ // At this point, there was a window with a wallpaper that
+ // was force hiding other windows behind it, but now it
+ // is going away. This may be simple -- just animate
+ // away the wallpaper and its window -- or it may be
+ // hard -- the wallpaper now needs to be shown behind
+ // something that was hidden.
+ WindowState oldWallpaper = mWallpaperTarget;
+ adjResult = adjustWallpaperWindowsLocked();
+ wallpaperMayChange = false;
+ if (false) Log.v(TAG, "****** OLD: " + oldWallpaper
+ + " NEW: " + mWallpaperTarget);
+ if (mLowerWallpaperTarget == null) {
+ // Whoops, we don't need a special wallpaper animation.
+ // Clear them out.
+ forceHiding = false;
+ for (i=N-1; i>=0; i--) {
+ WindowState w = (WindowState)mWindows.get(i);
+ if (w.mSurface != null) {
+ final WindowManager.LayoutParams attrs = w.mAttrs;
+ if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) {
+ if (DEBUG_FOCUS) Log.i(TAG, "win=" + w + " force hides other windows");
+ forceHiding = true;
+ } else if (mPolicy.canBeForceHidden(w, attrs)) {
+ if (!w.mAnimating) {
+ // We set the animation above so it
+ // is not yet running.
+ w.clearAnimation();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (wallpaperMayChange) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper may change! Adjusting");
+ adjResult = adjustWallpaperWindowsLocked();
+ }
+
+ if ((adjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper layer changed: assigning layers + relayout");
+ restart = true;
+ mLayoutNeeded = true;
+ assignLayersLocked();
+ } else if ((adjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper visibility changed: relayout");
+ restart = true;
+ mLayoutNeeded = true;
+ }
+
+ if (focusMayChange) {
+ if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES)) {
+ restart = true;
+ adjResult = 0;
+ }
+ }
+
+ if (mLayoutNeeded) {
+ restart = true;
+ performLayoutLockedInner();
+ }
+
} while (restart);
// THIRD LOOP: Update the surfaces of all windows.
@@ -8212,6 +9776,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
boolean syswin = false;
boolean backgroundFillerShown = false;
+ final int N = mWindows.size();
+
for (i=N-1; i>=0; i--) {
WindowState w = (WindowState)mWindows.get(i);
@@ -8239,6 +9805,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
w.mLastRequestedHeight = height;
w.mLastShownFrame.set(w.mShownFrame);
try {
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface
+ + ": POS " + w.mShownFrame.left
+ + ", " + w.mShownFrame.top);
w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
} catch (RuntimeException e) {
Log.w(TAG, "Error positioning surface in " + w, e);
@@ -8251,14 +9821,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
width = w.mShownFrame.width();
height = w.mShownFrame.height();
w.mLastShownFrame.set(w.mShownFrame);
- if (resize) {
- if (SHOW_TRANSACTIONS) Log.i(
- TAG, " SURFACE " + w.mSurface + ": ("
- + w.mShownFrame.left + ","
- + w.mShownFrame.top + ") ("
- + w.mShownFrame.width() + "x"
- + w.mShownFrame.height() + ")");
- }
}
if (resize) {
@@ -8266,6 +9828,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (height < 1) height = 1;
if (w.mSurface != null) {
try {
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface + ": POS "
+ + w.mShownFrame.left + ","
+ + w.mShownFrame.top + " SIZE "
+ + w.mShownFrame.width() + "x"
+ + w.mShownFrame.height());
w.mSurface.setSize(width, height);
w.mSurface.setPosition(w.mShownFrame.left,
w.mShownFrame.top);
@@ -8294,6 +9862,22 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
w.mLastFrame.set(w.mFrame);
w.mLastContentInsets.set(w.mContentInsets);
w.mLastVisibleInsets.set(w.mVisibleInsets);
+ // If the screen is currently frozen, then keep
+ // it frozen until this window draws at its new
+ // orientation.
+ if (mDisplayFrozen) {
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Resizing while display frozen: " + w);
+ w.mOrientationChanging = true;
+ if (mWindowsFreezingScreen) {
+ mWindowsFreezingScreen = true;
+ // XXX should probably keep timeout from
+ // when we first froze the display.
+ mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(
+ H.WINDOW_FREEZE_TIMEOUT), 2000);
+ }
+ }
// If the orientation is changing, then we need to
// hold off on unfreezing the display until this
// window has been redrawn; to do that, we need
@@ -8323,12 +9907,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- if (w.mAttachedHidden) {
+ if (w.mAttachedHidden || !w.isReadyForDisplay()) {
if (!w.mLastHidden) {
//dump();
w.mLastHidden = true;
if (SHOW_TRANSACTIONS) Log.i(
- TAG, " SURFACE " + w.mSurface + ": HIDE (performLayout-attached)");
+ TAG, " SURFACE " + w.mSurface + ": HIDE (performLayout)");
if (w.mSurface != null) {
try {
w.mSurface.hide();
@@ -8349,32 +9933,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_ORIENTATION) Log.v(TAG,
"Orientation change skips hidden " + w);
}
- } else if (!w.isReadyForDisplay()) {
- if (!w.mLastHidden) {
- //dump();
- w.mLastHidden = true;
- if (SHOW_TRANSACTIONS) Log.i(
- TAG, " SURFACE " + w.mSurface + ": HIDE (performLayout-ready)");
- if (w.mSurface != null) {
- try {
- w.mSurface.hide();
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception 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
- // doesn't really matter. Note that this does
- // introduce a potential glitch if the window
- // becomes unhidden before it has drawn for the
- // new orientation.
- if (w.mOrientationChanging) {
- w.mOrientationChanging = false;
- if (DEBUG_ORIENTATION) Log.v(TAG,
- "Orientation change skips hidden " + w);
- }
} else if (w.mLastLayer != w.mAnimLayer
|| w.mLastAlpha != w.mShownAlpha
|| w.mLastDsDx != w.mDsDx
@@ -8395,7 +9953,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
w.mLastVScale = w.mVScale;
if (SHOW_TRANSACTIONS) Log.i(
TAG, " SURFACE " + w.mSurface + ": alpha="
- + w.mShownAlpha + " layer=" + w.mAnimLayer);
+ + w.mShownAlpha + " layer=" + w.mAnimLayer
+ + " matrix=[" + (w.mDsDx*w.mHScale)
+ + "," + (w.mDtDx*w.mVScale)
+ + "][" + (w.mDsDy*w.mHScale)
+ + "," + (w.mDtDy*w.mVScale) + "]");
if (w.mSurface != null) {
try {
w.mSurface.setAlpha(w.mShownAlpha);
@@ -8464,8 +10026,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
focusDisplayed = true;
}
+ final boolean obscuredChanged = w.mObscured != obscured;
+
// Update effect.
- if (!obscured) {
+ if (!(w.mObscured=obscured)) {
if (w.mSurface != null) {
if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
holdScreen = w.mSession;
@@ -8481,7 +10045,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- boolean opaqueDrawn = w.isOpaqueDrawn();
+ boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
if (opaqueDrawn && w.isFullscreen(dw, dh)) {
// This window completely covers everything behind it,
// so we want to leave all of them as unblurred (for
@@ -8564,6 +10128,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
}
+
+ if (obscuredChanged && mWallpaperTarget == w) {
+ // This is the wallpaper target and its obscured state
+ // changed... make sure the current wallaper's visibility
+ // has been updated accordingly.
+ updateWallpaperVisibilityLocked();
+ }
}
if (backgroundFillerShown == false && mBackgroundFillerShown) {
@@ -8617,6 +10188,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
i--;
WindowState win = mResizingWindows.get(i);
try {
+ if (DEBUG_ORIENTATION) Log.v(TAG, "Reporting new frame to "
+ + win + ": " + win.mFrame);
win.mClient.resized(win.mFrame.width(),
win.mFrame.height(), win.mLastContentInsets,
win.mLastVisibleInsets, win.mDrawPending);
@@ -8630,6 +10203,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
// Destroy the surface of any windows that are no longer visible.
+ boolean wallpaperDestroyed = false;
i = mDestroySurface.size();
if (i > 0) {
do {
@@ -8639,6 +10213,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
}
+ if (win == mWallpaperTarget) {
+ wallpaperDestroyed = true;
+ }
win.destroySurfaceLocked();
} while (i > 0);
mDestroySurface.clear();
@@ -8649,6 +10226,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
WindowToken token = mExitingTokens.get(i);
if (!token.hasVisible) {
mExitingTokens.remove(i);
+ if (token.windowType == TYPE_WALLPAPER) {
+ mWallpaperTokens.remove(token);
+ }
}
}
@@ -8656,15 +10236,45 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
for (i=mExitingAppTokens.size()-1; i>=0; i--) {
AppWindowToken token = mExitingAppTokens.get(i);
if (!token.hasVisible && !mClosingApps.contains(token)) {
+ // Make sure there is no animation running on this token,
+ // so any windows associated with it will be removed as
+ // soon as their animations are complete
+ token.animation = null;
+ token.animating = false;
mAppTokens.remove(token);
mExitingAppTokens.remove(i);
+ if (mLastEnterAnimToken == token) {
+ mLastEnterAnimToken = null;
+ mLastEnterAnimParams = null;
+ }
}
}
+ boolean needRelayout = false;
+
+ if (!animating && mAppTransitionRunning) {
+ // We have finished the animation of an app transition. To do
+ // this, we have delayed a lot of operations like showing and
+ // hiding apps, moving apps in Z-order, etc. The app token list
+ // reflects the correct Z-order, but the window list may now
+ // be out of sync with it. So here we will just rebuild the
+ // entire app window list. Fun!
+ mAppTransitionRunning = false;
+ needRelayout = true;
+ rebuildAppWindowListLocked();
+ // Clear information about apps that were moving.
+ mToBottomApps.clear();
+ }
+
if (focusDisplayed) {
mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
}
- if (animating) {
+ if (wallpaperDestroyed) {
+ needRelayout = adjustWallpaperWindowsLocked() != 0;
+ }
+ if (needRelayout) {
+ requestAnimationLocked(0);
+ } else if (animating) {
requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
}
mQueue.setHoldScreenLocked(holdScreen != null);
@@ -8679,6 +10289,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
mH.sendMessage(m);
}
+
+ if (mTurnOnScreen) {
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
+ LocalPowerManager.BUTTON_EVENT, true);
+ mTurnOnScreen = false;
+ }
}
void requestAnimationLocked(long delay) {
@@ -8700,6 +10316,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
try {
if (win.mSurface != null) {
win.mSurface.show();
+ if (win.mTurnOnScreen) {
+ win.mTurnOnScreen = false;
+ mTurnOnScreen = true;
+ }
}
return true;
} catch (RuntimeException e) {
@@ -8738,7 +10358,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
+ " token=" + win.mToken
+ " pid=" + ws.mSession.mPid
+ " uid=" + ws.mSession.mUid);
- ws.mSurface.clear();
+ ws.mSurface.destroy();
ws.mSurface = null;
mForceRemoves.add(ws);
i--;
@@ -8748,7 +10368,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Log.w(TAG, "LEAKED SURFACE (app token hidden): "
+ ws + " surface=" + ws.mSurface
+ " token=" + win.mAppToken);
- ws.mSurface.clear();
+ ws.mSurface.destroy();
ws.mSurface = null;
leakedSurface = true;
}
@@ -8784,7 +10404,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// surface and ask the app to request another one.
Log.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry.");
if (surface != null) {
- surface.clear();
+ surface.destroy();
win.mSurface = null;
}
@@ -8934,8 +10554,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
mDisplayFrozen = true;
- if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
- mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
+ mNextAppTransitionPackage = null;
mAppTransitionReady = true;
}
@@ -9043,6 +10664,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
w.dump(pw, " ");
}
}
+ if (mResizingWindows.size() > 0) {
+ pw.println(" ");
+ pw.println(" Windows waiting to resize:");
+ for (int i=mResizingWindows.size()-1; i>=0; i--) {
+ WindowState w = mResizingWindows.get(i);
+ pw.print(" Resizing #"); pw.print(i); pw.print(' ');
+ pw.print(w); pw.println(":");
+ w.dump(pw, " ");
+ }
+ }
if (mSessions.size() > 0) {
pw.println(" ");
pw.println(" All active sessions:");
@@ -9071,6 +10702,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.println(mTokenList.get(i));
}
}
+ if (mWallpaperTokens.size() > 0) {
+ pw.println(" ");
+ pw.println(" Wallpaper tokens:");
+ for (int i=mWallpaperTokens.size()-1; i>=0; i--) {
+ WindowToken token = mWallpaperTokens.get(i);
+ pw.print(" Wallpaper #"); pw.print(i);
+ pw.print(' '); pw.print(token); pw.println(':');
+ token.dump(pw, " ");
+ }
+ }
if (mAppTokens.size() > 0) {
pw.println(" ");
pw.println(" Application tokens in Z order:");
@@ -9115,6 +10756,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(" mFocusedApp="); pw.println(mFocusedApp);
pw.print(" mInputMethodTarget="); pw.println(mInputMethodTarget);
pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow);
+ pw.print(" mWallpaperTarget="); pw.println(mWallpaperTarget);
+ if (mLowerWallpaperTarget != null && mUpperWallpaperTarget != null) {
+ pw.print(" mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
+ pw.print(" mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
+ }
pw.print(" mInTouchMode="); pw.println(mInTouchMode);
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
@@ -9126,7 +10772,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print( " no DimAnimator ");
}
pw.print(" mInputMethodAnimLayerAdjustment=");
- pw.println(mInputMethodAnimLayerAdjustment);
+ pw.print(mInputMethodAnimLayerAdjustment);
+ pw.print(" mWallpaperAnimLayerAdjustment=");
+ pw.println(mWallpaperAnimLayerAdjustment);
+ pw.print(" mLastWallpaperX="); pw.print(mLastWallpaperX);
+ pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
pw.print(" mAppsFreezingScreen="); pw.println(mAppsFreezingScreen);
@@ -9139,15 +10789,34 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(" mNextAppTransition=0x");
pw.print(Integer.toHexString(mNextAppTransition));
pw.print(", mAppTransitionReady="); pw.print(mAppTransitionReady);
+ pw.print(", mAppTransitionRunning="); pw.print(mAppTransitionRunning);
pw.print(", mAppTransitionTimeout="); pw.println( mAppTransitionTimeout);
+ if (mNextAppTransitionPackage != null) {
+ pw.print(" mNextAppTransitionPackage=");
+ pw.print(mNextAppTransitionPackage);
+ pw.print(", mNextAppTransitionEnter=0x");
+ pw.print(Integer.toHexString(mNextAppTransitionEnter));
+ pw.print(", mNextAppTransitionExit=0x");
+ pw.print(Integer.toHexString(mNextAppTransitionExit));
+ }
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);
pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
+ if (mLastEnterAnimToken != null || mLastEnterAnimToken != null) {
+ pw.print(" mLastEnterAnimToken="); pw.print(mLastEnterAnimToken);
+ pw.print(", mLastEnterAnimParams="); pw.println(mLastEnterAnimParams);
+ }
if (mOpeningApps.size() > 0) {
pw.print(" mOpeningApps="); pw.println(mOpeningApps);
}
if (mClosingApps.size() > 0) {
pw.print(" mClosingApps="); pw.println(mClosingApps);
}
+ if (mToTopApps.size() > 0) {
+ pw.print(" mToTopApps="); pw.println(mToTopApps);
+ }
+ if (mToBottomApps.size() > 0) {
+ pw.print(" mToBottomApps="); pw.println(mToBottomApps);
+ }
pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
pw.println(" KeyWaiter state:");
@@ -9166,6 +10835,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
synchronized (mKeyWaiter) { }
}
+ public void virtualKeyFeedback(KeyEvent event) {
+ mPolicy.keyFeedbackFromInput(event);
+ }
+
/**
* DimAnimator class that controls the dim animation. This holds the surface and
* all state used for dim animation.
@@ -9215,7 +10888,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mDimSurface.setLayer(w.mAnimLayer-1);
final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
- if (SHOW_TRANSACTIONS) Log.i(TAG, "layer=" + (w.mAnimLayer-1) + ", target=" + target);
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface
+ + ": layer=" + (w.mAnimLayer-1) + " target=" + target);
if (mDimTargetAlpha != target) {
// If the desired dim level has changed, then
// start an animation to it.
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5d34d00..c3aeca4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -39,8 +39,10 @@ import android.app.IInstrumentationWatcher;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
+import android.app.Notification;
import android.app.PendingIntent;
import android.app.ResultInfo;
+import android.app.Service;
import android.backup.IBackupManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -50,6 +52,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
+import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
@@ -66,6 +69,7 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -133,9 +137,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final boolean DEBUG_SERVICE = localLOGV || false;
static final boolean DEBUG_VISBILITY = localLOGV || false;
static final boolean DEBUG_PROCESSES = localLOGV || false;
+ static final boolean DEBUG_PROVIDER = localLOGV || false;
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
static final boolean DEBUG_RESULTS = localLOGV || false;
- static final boolean DEBUG_BACKUP = localLOGV || true;
+ static final boolean DEBUG_BACKUP = localLOGV || false;
+ static final boolean DEBUG_CONFIGURATION = localLOGV || false;
static final boolean VALIDATE_TOKENS = false;
static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -334,6 +340,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Memory pages are 4K.
static final int PAGE_SIZE = 4*1024;
+ // System property defining error report receiver for system apps
+ static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps";
+
+ // System property defining default error report receiver
+ static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
+
// Corresponding memory levels for above adjustments.
final int EMPTY_APP_MEM;
final int HIDDEN_APP_MEM;
@@ -447,6 +459,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= 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>();
+
+ /**
* List of intents that were used to start the most recent tasks.
*/
final ArrayList<TaskRecord> mRecentTasks
@@ -763,6 +782,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
String mTopData;
boolean mSystemReady = false;
boolean mBooting = false;
+ boolean mWaitingUpdate = false;
+ boolean mDidUpdate = false;
Context mContext;
@@ -980,6 +1001,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
res.set(0);
}
}
+
+ ensureBootCompleted();
} break;
case SHOW_NOT_RESPONDING_MSG: {
synchronized (ActivityManagerService.this) {
@@ -1000,13 +1023,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
proc.anrDialog = d;
}
- ensureScreenEnabled();
+ ensureBootCompleted();
} break;
case SHOW_FACTORY_ERROR_MSG: {
Dialog d = new FactoryErrorDialog(
mContext, msg.getData().getCharSequence("msg"));
d.show();
- enableScreenAfterBoot();
+ ensureBootCompleted();
} break;
case UPDATE_CONFIGURATION_MSG: {
final ContentResolver resolver = mContext.getContentResolver();
@@ -1070,7 +1093,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// so we need to be conservative and assume it isn't.
IBinder token = (IBinder)msg.obj;
Log.w(TAG, "Activity idle timeout for " + token);
- activityIdleInternal(token, true);
+ activityIdleInternal(token, true, null);
} break;
case DESTROY_TIMEOUT_MSG: {
IBinder token = (IBinder)msg.obj;
@@ -1081,7 +1104,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} break;
case IDLE_NOW_MSG: {
IBinder token = (IBinder)msg.obj;
- activityIdle(token);
+ activityIdle(token, null);
} break;
case SERVICE_TIMEOUT_MSG: {
if (mDidDexOpt) {
@@ -1199,6 +1222,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ApplicationInfo info =
mSelf.mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS);
+ mSystemThread.installSystemApplicationInfo(info);
+
synchronized (mSelf) {
ProcessRecord app = mSelf.newProcessRecordLocked(
mSystemThread.getApplicationThread(), info,
@@ -1411,6 +1436,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mSimpleProcessManagement = true;
}
+ Log.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
+
MY_PID = Process.myPid();
File dataDir = Environment.getDataDirectory();
@@ -1557,6 +1584,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ long[] cpuSpeedTimes = mProcessStats.getLastCpuSpeedTimes();
final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
synchronized(bstats) {
synchronized(mPidsSelfLocked) {
@@ -1570,11 +1598,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (pr != null) {
BatteryStatsImpl.Uid.Proc ps = pr.batteryStats;
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+ ps.addSpeedStepTimes(cpuSpeedTimes);
} else {
BatteryStatsImpl.Uid.Proc ps =
bstats.getProcessStatsLocked(st.name, st.pid);
if (ps != null) {
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+ ps.addSpeedStepTimes(cpuSpeedTimes);
}
}
}
@@ -1850,12 +1880,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent());
+ "activity", r.intent.getComponent(), false);
}
private final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
- String hostingType, ComponentName hostingName) {
+ String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
ProcessRecord app = getProcessRecordLocked(processName, info.uid);
// We don't have to do anything more if:
// (1) There is an existing application record; and
@@ -1908,7 +1938,8 @@ 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
- && (info.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ && !isAllowedWhileBooting(info)
+ && !allowWhileBooting) {
if (!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
@@ -1919,6 +1950,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return (app.pid != 0) ? app : null;
}
+ boolean isAllowedWhileBooting(ApplicationInfo ai) {
+ return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
+ }
+
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
if (app.pid > 0 && app.pid != MY_PID) {
@@ -2219,13 +2254,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mHandler.sendMessage(msg);
}
- reportResumedActivity(next);
+ 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,
@@ -2491,7 +2527,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private void reportResumedActivity(HistoryRecord r) {
+ private void reportResumedActivityLocked(HistoryRecord r) {
//Log.i(TAG, "**** REPORT RESUME: " + r);
final int identHash = System.identityHashCode(r);
@@ -2542,6 +2578,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// 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;
}
@@ -2552,6 +2589,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// 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;
}
@@ -2614,17 +2652,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (prev.finishing) {
if (DEBUG_TRANSITION) Log.v(TAG,
"Prepare close transition: prev=" + prev);
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
- : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
+ 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) Log.v(TAG,
"Prepare open transition: prev=" + prev);
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
- : WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ 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);
@@ -2633,7 +2679,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else if (mHistory.size() > 1) {
if (DEBUG_TRANSITION) Log.v(TAG,
"Prepare open transition: no previous");
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ if (mNoAnimActivities.contains(next)) {
+ mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ }
}
if (next.app != null && next.app.thread != null) {
@@ -2655,13 +2705,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order.
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next : null);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- if (!updateConfigurationLocked(config, next)) {
+ boolean updated;
+ synchronized (this) {
+ Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ mConfiguration,
+ next.mayFreezeScreenLocked(next.app) ? next : null);
+ if (config != null) {
+ /*
+ * Explicitly restore the locale to the one from the
+ * old configuration, since the one that comes back from
+ * the window manager has the default (boot) locale.
+ *
+ * It looks like previously the locale picker only worked
+ * by coincidence: usually it would do its setting of
+ * the locale after the activity transition, so it didn't
+ * matter that this lost it. With the synchronized
+ * block now keeping them from happening at the same time,
+ * this one always would happen second and undo what the
+ * locale picker had just done.
+ */
+ config.locale = mConfiguration.locale;
+ 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
@@ -2675,7 +2743,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Do over!
mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
}
+ setFocusedActivityLocked(next);
+ ensureActivitiesVisibleLocked(null, 0);
mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
return true;
}
@@ -2836,9 +2907,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (DEBUG_TRANSITION) Log.v(TAG,
"Prepare open transition: starting " + r);
- mWindowManager.prepareAppTransition(newTask
- ? WindowManagerPolicy.TRANSIT_TASK_OPEN
- : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ 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;
@@ -2898,7 +2978,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* or null if none was found.
*/
private final HistoryRecord performClearTaskLocked(int taskId,
- HistoryRecord newR, boolean doClear) {
+ HistoryRecord newR, int launchFlags, boolean doClear) {
int i = mHistory.size();
// First find the requested task.
@@ -2941,7 +3021,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// 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) {
+ if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+ && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
if (!ret.finishing) {
int index = indexOfTokenLocked(ret);
if (index >= 0) {
@@ -3312,7 +3393,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (callerAtFront) {
// We really do want to push this one into the
// user's face, right now.
- moveTaskToFrontLocked(taskTop.task);
+ moveTaskToFrontLocked(taskTop.task, r);
}
}
// If the caller has requested that the target task be
@@ -3338,7 +3419,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// cases this means we are resetting the task to its
// initial state.
HistoryRecord top = performClearTaskLocked(
- taskTop.task.taskId, r, true);
+ taskTop.task.taskId, r, launchFlags, true);
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different
@@ -3481,7 +3562,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// task, but the caller has asked to clear that task if the
// activity is already running.
HistoryRecord top = performClearTaskLocked(
- sourceRecord.task.taskId, r, true);
+ sourceRecord.task.taskId, r, launchFlags, true);
if (top != null) {
logStartActivity(LOG_AM_NEW_INTENT, r, top.task);
deliverNewIntentLocked(top, r.intent);
@@ -3592,6 +3673,36 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ public int startActivityIntentSender(IApplicationThread caller,
+ IntentSender intent, Intent fillInIntent, String resolvedType,
+ IBinder resultTo, String resultWho, int requestCode,
+ int flagsMask, int flagsValues) {
+ // Refuse possible leaked file descriptors
+ if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ IIntentSender sender = intent.getTarget();
+ if (!(sender instanceof PendingIntentRecord)) {
+ throw new IllegalArgumentException("Bad PendingIntent object");
+ }
+
+ PendingIntentRecord pir = (PendingIntentRecord)sender;
+
+ 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 ==
+ Binder.getCallingUid()) {
+ mAppSwitchesAllowedTime = 0;
+ }
+ }
+
+ return pir.sendInner(0, fillInIntent, resolvedType,
+ null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
+ }
+
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent) {
// Refuse possible leaked file descriptors
@@ -4108,6 +4219,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ public void overridePendingTransition(IBinder token, String packageName,
+ int enterAnim, int exitAnim) {
+ synchronized(this) {
+ int index = indexOfTokenLocked(token);
+ if (index < 0) {
+ return;
+ }
+ HistoryRecord self = (HistoryRecord)mHistory.get(index);
+
+ final long origId = Binder.clearCallingIdentity();
+
+ if (self.state == ActivityState.RESUMED
+ || self.state == ActivityState.PAUSING) {
+ mWindowManager.overridePendingAppTransition(packageName,
+ enterAnim, exitAnim);
+ }
+
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
/**
* Perform clean-up of service connections in an activity record.
*/
@@ -4444,7 +4576,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
long now = SystemClock.uptimeMillis();
for (i=0; i<count; i++) {
ProcessRecord rec = mLRUProcesses.get(i);
- if (rec.thread != null &&
+ if (rec != app && rec.thread != null &&
(rec.lastLowMemory+GC_MIN_INTERVAL) <= now) {
// The low memory report is overriding any current
// state for a GC request. Make sure to do
@@ -4871,12 +5003,59 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
mWatchers.finishBroadcast();
+ mWindowManager.closeSystemDialogs(reason);
+
+ for (i=mHistory.size()-1; i>=0; i--) {
+ HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
+ finishActivityLocked(r, i,
+ Activity.RESULT_CANCELED, null, "close-sys");
+ }
+ }
+
broadcastIntentLocked(null, null, intent, null,
null, 0, null, null, null, false, false, -1, uid);
}
Binder.restoreCallingIdentity(origId);
}
+ public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
+ throws RemoteException {
+ Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
+ for (int i=pids.length-1; i>=0; i--) {
+ infos[i] = new Debug.MemoryInfo();
+ Debug.getMemoryInfo(pids[i], infos[i]);
+ }
+ return infos;
+ }
+
+ public void killApplicationProcess(String processName, int uid) {
+ if (processName == null) {
+ return;
+ }
+
+ int callerUid = Binder.getCallingUid();
+ // Only the system server can kill an application
+ if (callerUid == Process.SYSTEM_UID) {
+ synchronized (this) {
+ ProcessRecord app = getProcessRecordLocked(processName, uid);
+ if (app != null) {
+ try {
+ app.thread.scheduleSuicide();
+ } catch (RemoteException e) {
+ // If the other end already died, then our work here is done.
+ }
+ } else {
+ Log.w(TAG, "Process/uid not found attempting kill of "
+ + processName + " / " + uid);
+ }
+ }
+ } else {
+ throw new SecurityException(callerUid + " cannot kill app process: " +
+ processName);
+ }
+ }
+
private void restartPackageLocked(final String packageName, int uid) {
uninstallPackageLocked(packageName, uid, false);
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
@@ -5101,8 +5280,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- List providers = generateApplicationProvidersLocked(app);
+ boolean normalMode = mSystemReady || isAllowedWhileBooting(app.info);
+ List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
+ if (!normalMode) {
+ Log.i(TAG, "Launching preboot mode app: " + app);
+ }
+
if (localLOGV) Log.v(
TAG, "New app record " + app
+ " thread=" + thread.asBinder() + " pid=" + pid);
@@ -5118,23 +5302,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mWaitForDebugger = mOrigWaitForDebugger;
}
}
+
// If the app is being launched for restore or full backup, set it up specially
boolean isRestrictedBackupMode = false;
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
}
+
ensurePackageDexOpt(app.instrumentationInfo != null
? app.instrumentationInfo.packageName
: app.info.packageName);
if (app.instrumentationClass != null) {
ensurePackageDexOpt(app.instrumentationClass.getPackageName());
}
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Binding proc "
+ + processName + " with config " + mConfiguration);
thread.bindApplication(processName, app.instrumentationInfo != null
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
- isRestrictedBackupMode, mConfiguration, getCommonServicesLocked());
+ isRestrictedBackupMode || !normalMode,
+ mConfiguration, getCommonServicesLocked());
updateLRUListLocked(app, false);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
@@ -5250,9 +5439,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- public final void activityIdle(IBinder token) {
+ public final void activityIdle(IBinder token, Configuration config) {
final long origId = Binder.clearCallingIdentity();
- activityIdleInternal(token, false);
+ activityIdleInternal(token, false, config);
Binder.restoreCallingIdentity(origId);
}
@@ -5300,10 +5489,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
void enableScreenAfterBoot() {
+ EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN,
+ SystemClock.uptimeMillis());
mWindowManager.enableScreenAfterBoot();
}
- final void activityIdleInternal(IBinder token, boolean fromTimeout) {
+ final void activityIdleInternal(IBinder token, boolean fromTimeout,
+ Configuration config) {
if (localLOGV) Log.v(TAG, "Activity idle: " + token);
ArrayList<HistoryRecord> stops = null;
@@ -5326,6 +5518,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (index >= 0) {
HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ // 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);
@@ -5410,26 +5611,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (booting) {
- // Ensure that any processes we had put on hold are now started
- // up.
- final int NP = mProcessesOnHold.size();
- if (NP > 0) {
- ArrayList<ProcessRecord> procs =
- new ArrayList<ProcessRecord>(mProcessesOnHold);
- for (int ip=0; ip<NP; ip++) {
- this.startProcessLocked(procs.get(ip), "on-hold", null);
- }
- }
- if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- // Tell anyone interested that we are done booting!
- synchronized (this) {
- broadcastIntentLocked(null, null,
- new Intent(Intent.ACTION_BOOT_COMPLETED, null),
- null, null, 0, null, null,
- android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID);
- }
- }
+ finishBooting();
}
trimApplications();
@@ -5437,22 +5619,48 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
//mWindowManager.dump();
if (enableScreen) {
- EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN,
- SystemClock.uptimeMillis());
enableScreenAfterBoot();
}
}
- final void ensureScreenEnabled() {
+ final void finishBooting() {
+ // Ensure that any processes we had put on hold are now started
+ // up.
+ final int NP = mProcessesOnHold.size();
+ if (NP > 0) {
+ ArrayList<ProcessRecord> procs =
+ new ArrayList<ProcessRecord>(mProcessesOnHold);
+ for (int ip=0; ip<NP; ip++) {
+ this.startProcessLocked(procs.get(ip), "on-hold", null);
+ }
+ }
+ if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+ // Tell anyone interested that we are done booting!
+ synchronized (this) {
+ broadcastIntentLocked(null, null,
+ new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID);
+ }
+ }
+ }
+
+ final void ensureBootCompleted() {
+ boolean booting;
boolean enableScreen;
synchronized (this) {
+ booting = mBooting;
+ mBooting = false;
enableScreen = !mBooted;
mBooted = true;
}
+
+ if (booting) {
+ finishBooting();
+ }
if (enableScreen) {
- EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN,
- SystemClock.uptimeMillis());
enableScreenAfterBoot();
}
}
@@ -5552,7 +5760,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public String getCallingPackage(IBinder token) {
synchronized (this) {
HistoryRecord r = getCallingRecordLocked(token);
- return r != null && r.app != null ? r.app.processName : null;
+ return r != null && r.app != null ? r.info.packageName : null;
}
}
@@ -5604,6 +5812,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ if (type == INTENT_SENDER_BROADCAST) {
+ if ((intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ throw new IllegalArgumentException(
+ "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ }
+ }
+
synchronized(this) {
int callingUid = Binder.getCallingUid();
try {
@@ -6807,14 +7022,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);
+ moveTaskToFrontLocked(tr, null);
return;
}
}
for (int i=mHistory.size()-1; i>=0; i--) {
HistoryRecord hr = (HistoryRecord)mHistory.get(i);
if (hr.task.taskId == task) {
- moveTaskToFrontLocked(hr.task);
+ moveTaskToFrontLocked(hr.task, null);
return;
}
}
@@ -6824,7 +7039,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void moveTaskToFrontLocked(TaskRecord tr) {
+ private final void moveTaskToFrontLocked(TaskRecord tr, HistoryRecord reason) {
if (DEBUG_SWITCH) Log.v(TAG, "moveTaskToFront: " + tr);
final int task = tr.taskId;
@@ -6835,10 +7050,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return;
}
- if (DEBUG_TRANSITION) Log.v(TAG,
- "Prepare to front transition: task=" + tr);
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
-
ArrayList moved = new ArrayList();
// Applying the affinities may have removed entries from the history,
@@ -6867,6 +7078,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pos--;
}
+ if (DEBUG_TRANSITION) Log.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);
@@ -6892,7 +7116,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
final long origId = Binder.clearCallingIdentity();
- moveTaskToBackLocked(task);
+ moveTaskToBackLocked(task, null);
Binder.restoreCallingIdentity(origId);
}
}
@@ -6911,7 +7135,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
int taskId = getTaskForActivityLocked(token, !nonRoot);
if (taskId >= 0) {
- return moveTaskToBackLocked(taskId);
+ return moveTaskToBackLocked(taskId, null);
}
Binder.restoreCallingIdentity(origId);
}
@@ -6929,7 +7153,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* @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) {
+ private final boolean moveTaskToBackLocked(int task, HistoryRecord reason) {
Log.i(TAG, "moveTaskToBack: " + task);
// If we have a watcher, preflight the move before committing to it. First check
@@ -6958,7 +7182,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_TRANSITION) Log.v(TAG,
"Prepare to back transition: task=" + task);
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
final int N = mHistory.size();
int bottom = 0;
@@ -6980,6 +7203,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
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);
@@ -7322,10 +7555,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
- // In this case the provider is a single instance, so we can
+ // In this case the provider instance already exists, so we can
// return it right away.
if (r != null) {
- r.conProviders.add(cpr);
+ if (DEBUG_PROVIDER) Log.v(TAG,
+ "Adding provider requested by "
+ + r.processName + " from process "
+ + cpr.info.processName);
+ Integer cnt = r.conProviders.get(cpr);
+ if (cnt == null) {
+ r.conProviders.put(cpr, new Integer(1));
+ } else {
+ r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
+ }
cpr.clients.add(r);
} else {
cpr.externals++;
@@ -7382,10 +7624,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return cpr;
}
- if (false) {
- RuntimeException e = new RuntimeException("foo");
- //Log.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid
- // + " pruid " + ai.uid + "): " + cpi.className, e);
+ if (DEBUG_PROVIDER) {
+ RuntimeException e = new RuntimeException("here");
+ Log.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid
+ + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
}
// This is single process, and our app is now connecting to it.
@@ -7397,14 +7639,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mLaunchingProviders.get(i) == cpr) {
break;
}
- if (false) {
- final ContentProviderRecord rec =
- (ContentProviderRecord)mLaunchingProviders.get(i);
- if (rec.info.name.equals(cpr.info.name)) {
- cpr = rec;
- break;
- }
- }
}
// If the provider is not already being launched, then get it
@@ -7414,7 +7648,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ProcessRecord proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
- cpi.name));
+ cpi.name), false);
if (proc == null) {
Log.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
@@ -7435,7 +7669,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mProvidersByName.put(name, cpr);
if (r != null) {
- r.conProviders.add(cpr);
+ if (DEBUG_PROVIDER) Log.v(TAG,
+ "Adding provider requested by "
+ + r.processName + " from process "
+ + cpr.info.processName);
+ Integer cnt = r.conProviders.get(cpr);
+ if (cnt == null) {
+ r.conProviders.put(cpr, new Integer(1));
+ } else {
+ r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
+ }
cpr.clients.add(r);
} else {
cpr.externals++;
@@ -7489,8 +7732,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
if(cpr == null) {
- //remove from mProvidersByClass
- if(localLOGV) Log.v(TAG, name+" content provider not found in providers list");
+ // remove from mProvidersByClass
+ if (DEBUG_PROVIDER) Log.v(TAG, name +
+ " provider not found in providers list");
return;
}
final ProcessRecord r = getRecordForAppLocked(caller);
@@ -7500,16 +7744,24 @@ 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);
- if(localLOGV) Log.v(TAG, "Removing content provider requested by "+
- r.info.processName+" from process "+localCpr.appInfo.processName);
- if(localCpr.appInfo.processName == r.info.processName) {
+ ContentProviderRecord localCpr = (ContentProviderRecord)
+ mProvidersByClass.get(cpr.info.name);
+ if (DEBUG_PROVIDER) Log.v(TAG, "Removing provider requested by "
+ + r.info.processName + " from process "
+ + localCpr.appInfo.processName);
+ if (localCpr.app == r) {
//should not happen. taken care of as a local provider
- if(localLOGV) Log.v(TAG, "local provider doing nothing Ignoring other names");
+ Log.w(TAG, "removeContentProvider called on local provider: "
+ + cpr.info.name + " in process " + r.processName);
return;
} else {
- localCpr.clients.remove(r);
- r.conProviders.remove(localCpr);
+ Integer cnt = r.conProviders.get(localCpr);
+ if (cnt == null || cnt.intValue() <= 1) {
+ localCpr.clients.remove(r);
+ r.conProviders.remove(localCpr);
+ } else {
+ r.conProviders.put(localCpr, new Integer(cnt.intValue()-1));
+ }
}
updateOomAdjLocked();
}
@@ -8099,7 +8351,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- systemReady();
+ systemReady(null);
}
private void retrieveSettings() {
@@ -8121,6 +8373,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// This happens before any activities are started, so we can
// change mConfiguration in-place.
mConfiguration.updateFrom(configuration);
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Initial config: " + mConfiguration);
}
}
@@ -8129,7 +8382,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return mSystemReady;
}
- public void systemReady() {
+ 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.
if (!Process.supportsProcesses()) {
@@ -8139,19 +8392,97 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(this) {
if (mSystemReady) {
+ if (goingCallback != null) goingCallback.run();
return;
}
+
+ // Check to see if there are any update receivers to run.
+ if (!mDidUpdate) {
+ if (mWaitingUpdate) {
+ return;
+ }
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ List<ResolveInfo> ris = null;
+ try {
+ ris = ActivityThread.getPackageManager().queryIntentReceivers(
+ intent, null, 0);
+ } catch (RemoteException e) {
+ }
+ if (ris != null) {
+ for (int i=ris.size()-1; i>=0; i--) {
+ if ((ris.get(i).activityInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_SYSTEM) == 0) {
+ ris.remove(i);
+ }
+ }
+ intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+ for (int i=0; i<ris.size(); i++) {
+ ActivityInfo ai = ris.get(i).activityInfo;
+ intent.setComponent(new ComponentName(ai.packageName, ai.name));
+ IIntentReceiver finisher = null;
+ if (i == 0) {
+ 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);
+ }
+ };
+ }
+ Log.i(TAG, "Sending system update to: " + intent.getComponent());
+ broadcastIntentLocked(null, null, intent, null, finisher,
+ 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
+ if (i == 0) {
+ mWaitingUpdate = true;
+ }
+ }
+ }
+ if (mWaitingUpdate) {
+ return;
+ }
+ mDidUpdate = true;
+ }
+
mSystemReady = true;
if (!mStartRunning) {
return;
}
}
- if (Config.LOGD) Log.d(TAG, "Start running!");
+ ArrayList<ProcessRecord> procsToKill = null;
+ synchronized(mPidsSelfLocked) {
+ for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
+ ProcessRecord proc = mPidsSelfLocked.valueAt(i);
+ if (!isAllowedWhileBooting(proc.info)){
+ if (procsToKill == null) {
+ procsToKill = new ArrayList<ProcessRecord>();
+ }
+ procsToKill.add(proc);
+ }
+ }
+ }
+
+ if (procsToKill != null) {
+ synchronized(this) {
+ for (int i=procsToKill.size()-1; i>=0; i--) {
+ ProcessRecord proc = procsToKill.get(i);
+ Log.i(TAG, "Removing system update proc: " + proc);
+ removeProcessLocked(proc, true);
+ }
+ }
+ }
+
+ Log.i(TAG, "System now ready");
EventLog.writeEvent(LOG_BOOT_PROGRESS_AMS_READY,
SystemClock.uptimeMillis());
synchronized(this) {
+ // Make sure we have no pre-ready processes sitting around.
+
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
ResolveInfo ri = mContext.getPackageManager()
.resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
@@ -8187,6 +8518,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
retrieveSettings();
+ if (goingCallback != null) goingCallback.run();
+
synchronized (this) {
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
@@ -8209,6 +8542,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ // Start up initial activity.
+ mBooting = true;
+
try {
if (ActivityThread.getPackageManager().hasSystemUidErrors()) {
Message msg = Message.obtain();
@@ -8218,8 +8554,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} catch (RemoteException e) {
}
- // Start up initial activity.
- mBooting = true;
resumeTopActivityLocked(null);
}
}
@@ -8235,28 +8569,70 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
private ComponentName getErrorReportReceiver(ProcessRecord app) {
+ // check if error reporting is enabled in Gservices
+ int enabled = Settings.Gservices.getInt(mContext.getContentResolver(),
+ Settings.Gservices.SEND_ACTION_APP_ERROR, 0);
+ if (enabled == 0) {
+ return null;
+ }
+
IPackageManager pm = ActivityThread.getPackageManager();
+
try {
- // was an installer package name specified when this app was
- // installed?
- String installerPackageName = pm.getInstallerPackageName(app.info.packageName);
- if (installerPackageName == null) {
- return null;
+ // look for receiver in the installer package
+ String candidate = pm.getInstallerPackageName(app.info.packageName);
+ ComponentName result = getErrorReportReceiver(pm, app.info.packageName, candidate);
+ if (result != null) {
+ return result;
}
- // is there an Activity in this package that handles ACTION_APP_ERROR?
- Intent intent = new Intent(Intent.ACTION_APP_ERROR);
- intent.setPackage(installerPackageName);
- ResolveInfo info = pm.resolveIntent(intent, null, 0);
- if (info == null || info.activityInfo == null) {
- return null;
+ // if the error app is on the system image, look for system apps
+ // error receiver
+ if ((app.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
+ result = getErrorReportReceiver(pm, app.info.packageName, candidate);
+ if (result != null) {
+ return result;
+ }
}
- return new ComponentName(installerPackageName, info.activityInfo.name);
+ // if there is a default receiver, try that
+ candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
+ return getErrorReportReceiver(pm, app.info.packageName, candidate);
} catch (RemoteException e) {
- // will return null and no error report will be delivered
+ // should not happen
+ Log.e(TAG, "error talking to PackageManager", e);
+ return null;
}
- return null;
+ }
+
+ /**
+ * Return activity in receiverPackage that handles ACTION_APP_ERROR.
+ *
+ * @param pm PackageManager isntance
+ * @param errorPackage package which caused the error
+ * @param receiverPackage candidate package to receive the error
+ * @return activity component within receiverPackage which handles
+ * ACTION_APP_ERROR, or null if not found
+ */
+ private ComponentName getErrorReportReceiver(IPackageManager pm, String errorPackage,
+ String receiverPackage) throws RemoteException {
+ if (receiverPackage == null || receiverPackage.length() == 0) {
+ return null;
+ }
+
+ // break the loop if it's the error report receiver package that crashed
+ if (receiverPackage.equals(errorPackage)) {
+ return null;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+ intent.setPackage(receiverPackage);
+ ResolveInfo info = pm.resolveIntent(intent, null, 0);
+ if (info == null || info.activityInfo == null) {
+ return null;
+ }
+ return new ComponentName(receiverPackage, info.activityInfo.name);
}
void makeAppNotRespondingLocked(ProcessRecord app,
@@ -8578,6 +8954,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
report.crashInfo.throwFileName = trace.getFileName();
report.crashInfo.throwClassName = trace.getClassName();
report.crashInfo.throwMethodName = trace.getMethodName();
+ report.crashInfo.throwLineNumber = trace.getLineNumber();
} else if (r.notResponding) {
report.type = ApplicationErrorReport.TYPE_ANR;
report.anrInfo = new ApplicationErrorReport.AnrInfo();
@@ -8645,6 +9022,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
app.pid, app.getPackageList());
+ currApp.uid = app.info.uid;
int adj = app.curAdj;
if (adj >= CONTENT_PROVIDER_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY;
@@ -8661,6 +9039,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
}
+ 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;
+ if (r.app != null) currApp.importanceReasonPid = r.app.pid;
+ }
+ if (app.adjTarget instanceof ComponentName) {
+ currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
+ }
//Log.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
// + " lru=" + currApp.lru);
if (runList == null) {
@@ -8830,7 +9218,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pw.print(" lowMem="); pw.print(proc.reportLowMemory);
pw.print(", last gced=");
pw.print(now-proc.lastRequestedGc);
- pw.print(" ms ago, last lowMwm=");
+ pw.print(" ms ago, last lowMem=");
pw.print(now-proc.lastLowMemory);
pw.println(" ms ago");
@@ -9384,7 +9772,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
sr.app = null;
sr.executeNesting = 0;
mStoppingServices.remove(sr);
- if (sr.bindings.size() > 0) {
+
+ boolean hasClients = sr.bindings.size() > 0;
+ if (hasClients) {
Iterator<IntentBindRecord> bindings
= sr.bindings.values().iterator();
while (bindings.hasNext()) {
@@ -9405,7 +9795,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else if (!allowRestart) {
bringDownServiceLocked(sr, true);
} else {
- scheduleServiceRestartLocked(sr);
+ boolean canceled = scheduleServiceRestartLocked(sr, true);
+
+ // Should the service remain running? Note that in the
+ // extreme case of so many attempts to deliver a command
+ // that it failed, that we also will stop it here.
+ if (sr.startRequested && (sr.stopIfKilled || canceled)) {
+ if (sr.pendingStarts.size() == 0) {
+ sr.startRequested = false;
+ if (!hasClients) {
+ // Whoops, no reason to restart!
+ bringDownServiceLocked(sr, true);
+ }
+ }
+ }
}
}
@@ -9468,6 +9871,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mLRUProcesses.remove(index);
}
+ mProcessesToGc.remove(app);
+
// Dismiss any open dialogs.
if (app.crashDialog != null) {
app.crashDialog.dismiss();
@@ -9545,7 +9950,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Unregister from connected content providers.
if (!app.conProviders.isEmpty()) {
- Iterator it = app.conProviders.iterator();
+ Iterator it = app.conProviders.keySet().iterator();
while (it.hasNext()) {
ContentProviderRecord cpr = (ContentProviderRecord)it.next();
cpr.clients.remove(app);
@@ -9648,6 +10053,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.app != null) {
info.pid = r.app.pid;
}
+ info.uid = r.appInfo.uid;
info.process = r.processName;
info.foreground = r.isForeground;
info.activeSince = r.createTime;
@@ -9655,6 +10061,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
info.clientCount = r.connections.size();
info.crashCount = r.crashCount;
info.lastActivityTime = r.lastActivity;
+ if (r.isForeground) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_FOREGROUND;
+ }
+ if (r.startRequested) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED;
+ }
+ if (r.app != null && r.app.pid == Process.myPid()) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
+ }
+ 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;
+ }
+ }
return info;
}
@@ -9683,6 +10108,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ public PendingIntent getRunningServiceControlPanel(ComponentName name) {
+ synchronized (this) {
+ ServiceRecord r = mServices.get(name);
+ if (r != null) {
+ for (ConnectionRecord conn : r.connections.values()) {
+ if (conn.clientIntent != null) {
+ return conn.clientIntent;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private final ServiceRecord findServiceLocked(ComponentName name,
IBinder token) {
ServiceRecord r = mServices.get(name);
@@ -9844,35 +10283,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private final void sendServiceArgsLocked(ServiceRecord r,
boolean oomAdjusted) {
- final int N = r.startArgs.size();
+ final int N = r.pendingStarts.size();
if (N == 0) {
return;
}
- final int BASEID = r.lastStartId - N + 1;
int i = 0;
while (i < N) {
try {
- Intent args = r.startArgs.get(i);
+ ServiceRecord.StartItem si = r.pendingStarts.get(i);
if (DEBUG_SERVICE) Log.v(TAG, "Sending arguments to service: "
- + r.name + " " + r.intent + " args=" + args);
+ + r.name + " " + r.intent + " args=" + si.intent);
+ if (si.intent == null && N > 1) {
+ // If somehow we got a dummy start at the front, then
+ // just drop it here.
+ i++;
+ continue;
+ }
bumpServiceExecutingLocked(r);
if (!oomAdjusted) {
oomAdjusted = true;
updateOomAdjLocked(r.app);
}
- r.app.thread.scheduleServiceArgs(r, BASEID+i, args);
+ int flags = 0;
+ if (si.deliveryCount > 0) {
+ flags |= Service.START_FLAG_RETRY;
+ }
+ if (si.doneExecutingCount > 0) {
+ 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.
+ break;
} catch (Exception e) {
+ Log.w(TAG, "Unexpected exception", e);
break;
}
}
if (i == N) {
- r.startArgs.clear();
+ r.pendingStarts.clear();
} else {
while (i > 0) {
- r.startArgs.remove(0);
i--;
+ r.pendingStarts.remove(i);
}
}
}
@@ -9928,44 +10387,91 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
try {
if (DEBUG_SERVICE) Log.v(TAG, "Scheduling start service: "
+ r.name + " " + r.intent);
+ mStringBuilder.setLength(0);
+ r.intent.getIntent().toShortString(mStringBuilder, false, true);
EventLog.writeEvent(LOG_AM_CREATE_SERVICE,
System.identityHashCode(r), r.shortName,
- r.intent.getIntent().toString(), r.app.pid);
+ mStringBuilder.toString(), r.app.pid);
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
ensurePackageDexOpt(r.serviceInfo.packageName);
app.thread.scheduleCreateService(r, r.serviceInfo);
+ r.postNotification();
created = true;
} finally {
if (!created) {
app.services.remove(r);
- scheduleServiceRestartLocked(r);
+ scheduleServiceRestartLocked(r, false);
}
}
requestServiceBindingsLocked(r);
+
+ // If the service is in the started state, and there are no
+ // pending arguments, then fake up one so its onStartCommand() will
+ // be called.
+ if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
+ r.lastStartId++;
+ if (r.lastStartId < 1) {
+ r.lastStartId = 1;
+ }
+ r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, null));
+ }
+
sendServiceArgsLocked(r, true);
}
- private final void scheduleServiceRestartLocked(ServiceRecord r) {
+ private final boolean scheduleServiceRestartLocked(ServiceRecord r,
+ boolean allowCancel) {
+ boolean canceled = false;
+
final long now = SystemClock.uptimeMillis();
+ long minDuration = SERVICE_RESTART_DURATION;
+ long resetTime = SERVICE_RESET_RUN_DURATION;
+
+ // Any delivered but not yet finished starts should be put back
+ // on the pending list.
+ final int N = r.deliveredStarts.size();
+ if (N > 0) {
+ for (int i=N-1; i>=0; i--) {
+ ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+ if (si.intent == null) {
+ // We'll generate this again if needed.
+ } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
+ && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
+ r.pendingStarts.add(0, si);
+ long dur = SystemClock.uptimeMillis() - si.deliveredTime;
+ dur *= 2;
+ if (minDuration < dur) minDuration = dur;
+ if (resetTime < dur) resetTime = dur;
+ } else {
+ Log.w(TAG, "Canceling start item " + si.intent + " in service "
+ + r.name);
+ canceled = true;
+ }
+ }
+ r.deliveredStarts.clear();
+ }
r.totalRestartCount++;
if (r.restartDelay == 0) {
r.restartCount++;
- r.restartDelay = SERVICE_RESTART_DURATION;
+ r.restartDelay = minDuration;
} else {
// If it has been a "reasonably long time" since the service
// was started, then reset our restart duration back to
// the beginning, so we don't infinitely increase the duration
// on a service that just occasionally gets killed (which is
// a normal case, due to process being killed to reclaim memory).
- if (now > (r.restartTime+SERVICE_RESET_RUN_DURATION)) {
+ if (now > (r.restartTime+resetTime)) {
r.restartCount = 1;
- r.restartDelay = SERVICE_RESTART_DURATION;
+ r.restartDelay = minDuration;
} else {
r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
+ if (r.restartDelay < minDuration) {
+ r.restartDelay = minDuration;
+ }
}
}
@@ -9994,6 +10500,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mRestartingServices.add(r);
}
+ r.cancelNotification();
+
mHandler.removeCallbacks(r.restarter);
mHandler.postAtTime(r.restarter, r.nextRestartTime);
r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
@@ -10006,6 +10514,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
msg.what = SERVICE_ERROR_MSG;
msg.obj = r;
mHandler.sendMessage(msg);
+
+ return canceled;
}
final void performServiceRestartLocked(ServiceRecord r) {
@@ -10030,7 +10540,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
//Log.i(TAG, "Bring up service:");
//r.dump(" ");
- if (r.app != null) {
+ if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, false);
return true;
}
@@ -10061,20 +10571,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// restart the application.
}
+ // Not running -- get it started, and enqueue this service record
+ // to be executed when the app comes up.
+ if (startProcessLocked(appName, r.appInfo, true, intentFlags,
+ "service", r.name, false) == null) {
+ Log.w(TAG, "Unable to launch app "
+ + r.appInfo.packageName + "/"
+ + r.appInfo.uid + " for service "
+ + r.intent.getIntent() + ": process is bad");
+ bringDownServiceLocked(r, true);
+ return false;
+ }
+
if (!mPendingServices.contains(r)) {
- // Not running -- get it started, and enqueue this service record
- // to be executed when the app comes up.
- if (startProcessLocked(appName, r.appInfo, true, intentFlags,
- "service", r.name) == null) {
- Log.w(TAG, "Unable to launch app "
- + r.appInfo.packageName + "/"
- + r.appInfo.uid + " for service "
- + r.intent.getIntent() + ": process is bad");
- bringDownServiceLocked(r, true);
- return false;
- }
mPendingServices.add(r);
}
+
return true;
}
@@ -10162,15 +10674,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ r.cancelNotification();
+ r.isForeground = false;
+ r.foregroundId = 0;
+ r.foregroundNoti = null;
+
+ // Clear start entries.
+ r.deliveredStarts.clear();
+ r.pendingStarts.clear();
+
if (r.app != null) {
synchronized (r.stats.getBatteryStats()) {
r.stats.stopLaunchedLocked();
}
r.app.services.remove(r);
if (r.app.thread != null) {
- updateServiceForegroundLocked(r.app, false);
try {
- Log.i(TAG, "Stopping service: " + r.shortName);
+ if (DEBUG_SERVICE) Log.v(TAG,
+ "Stopping service: " + r.shortName);
bumpServiceExecutingLocked(r);
mStoppingServices.add(r);
updateOomAdjLocked(r.app);
@@ -10180,6 +10701,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ r.shortName, e);
serviceDoneExecutingLocked(r, true);
}
+ updateServiceForegroundLocked(r.app, false);
} else {
if (DEBUG_SERVICE) Log.v(
TAG, "Removed service that has no process: " + r.shortName);
@@ -10223,11 +10745,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ r.shortName);
}
r.startRequested = true;
- r.startArgs.add(service);
+ r.callStart = false;
r.lastStartId++;
if (r.lastStartId < 1) {
r.lastStartId = 1;
}
+ r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, service));
r.lastActivity = SystemClock.uptimeMillis();
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
@@ -10295,6 +10818,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.record.stats.stopRunningLocked();
}
r.record.startRequested = false;
+ r.record.callStart = false;
final long origId = Binder.clearCallingIdentity();
bringDownServiceLocked(r.record, false);
Binder.restoreCallingIdentity(origId);
@@ -10343,10 +10867,35 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_SERVICE) Log.v(TAG, "stopServiceToken: " + className
+ " " + token + " startId=" + startId);
ServiceRecord r = findServiceLocked(className, token);
- if (r != null && (startId < 0 || r.lastStartId == startId)) {
+ if (r != null) {
+ if (startId >= 0) {
+ // Asked to only stop if done with all work. Note that
+ // to avoid leaks, we will take this as dropping all
+ // start items up to and including this one.
+ ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
+ if (si != null) {
+ while (r.deliveredStarts.size() > 0) {
+ if (r.deliveredStarts.remove(0) == si) {
+ break;
+ }
+ }
+ }
+
+ if (r.lastStartId != startId) {
+ return false;
+ }
+
+ if (r.deliveredStarts.size() > 0) {
+ Log.w(TAG, "stopServiceToken startId " + startId
+ + " is last, but have " + r.deliveredStarts.size()
+ + " remaining args");
+ }
+ }
+
synchronized (r.stats.getBatteryStats()) {
r.stats.stopRunningLocked();
r.startRequested = false;
+ r.callStart = false;
}
final long origId = Binder.clearCallingIdentity();
bringDownServiceLocked(r, false);
@@ -10358,20 +10907,45 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
public void setServiceForeground(ComponentName className, IBinder token,
- boolean isForeground) {
+ int id, Notification notification, boolean removeNotification) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
synchronized(this) {
ServiceRecord r = findServiceLocked(className, token);
if (r != null) {
- if (r.isForeground != isForeground) {
- final long origId = Binder.clearCallingIdentity();
- r.isForeground = isForeground;
+ if (id != 0) {
+ if (notification == null) {
+ throw new IllegalArgumentException("null notification");
+ }
+ if (r.foregroundId != id) {
+ r.cancelNotification();
+ r.foregroundId = id;
+ }
+ notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ r.foregroundNoti = notification;
+ r.isForeground = true;
+ r.postNotification();
if (r.app != null) {
updateServiceForegroundLocked(r.app, true);
}
- Binder.restoreCallingIdentity(origId);
+ } else {
+ if (r.isForeground) {
+ r.isForeground = false;
+ if (r.app != null) {
+ updateServiceForegroundLocked(r.app, true);
+ }
+ }
+ if (removeNotification) {
+ r.cancelNotification();
+ r.foregroundId = 0;
+ r.foregroundNoti = null;
+ }
}
}
}
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
public void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
@@ -10420,6 +10994,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
activity = (HistoryRecord)mHistory.get(aindex);
}
+ int clientLabel = 0;
+ PendingIntent clientIntent = null;
+
+ if (callerApp.info.uid == Process.SYSTEM_UID) {
+ // Hacky kind of thing -- allow system stuff to tell us
+ // what they are, so we can report this elsewhere for
+ // others to know why certain services are running.
+ try {
+ clientIntent = (PendingIntent)service.getParcelableExtra(
+ Intent.EXTRA_CLIENT_INTENT);
+ } catch (RuntimeException e) {
+ }
+ if (clientIntent != null) {
+ clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
+ if (clientLabel != 0) {
+ // There are no useful extras in the intent, trash them.
+ // System code calling with this stuff just needs to know
+ // this will happen.
+ service = service.cloneFilter();
+ }
+ }
+ }
+
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType,
Binder.getCallingPid(), Binder.getCallingUid());
@@ -10440,7 +11037,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
- connection, flags);
+ connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
s.connections.put(binder, c);
@@ -10665,7 +11262,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- public void serviceDoneExecuting(IBinder token) {
+ public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
@@ -10683,6 +11280,51 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return;
}
+ if (type == 1) {
+ // This is a call from a service start... take care of
+ // book-keeping.
+ r.callStart = true;
+ switch (res) {
+ case Service.START_STICKY_COMPATIBILITY:
+ case Service.START_STICKY: {
+ // We are done with the associated start arguments.
+ r.findDeliveredStart(startId, true);
+ // Don't stop if killed.
+ r.stopIfKilled = false;
+ break;
+ }
+ case Service.START_NOT_STICKY: {
+ // We are done with the associated start arguments.
+ r.findDeliveredStart(startId, true);
+ if (r.lastStartId == startId) {
+ // There is no more work, and this service
+ // doesn't want to hang around if killed.
+ r.stopIfKilled = true;
+ }
+ break;
+ }
+ case Service.START_REDELIVER_INTENT: {
+ // We'll keep this item until they explicitly
+ // call stop for it, but keep track of the fact
+ // that it was delivered.
+ ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
+ if (si != null) {
+ si.deliveryCount = 0;
+ si.doneExecutingCount++;
+ // Don't stop if killed.
+ r.stopIfKilled = true;
+ }
+ break;
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Unknown service start result: " + res);
+ }
+ if (res == Service.START_STICKY_COMPATIBILITY) {
+ r.callStart = false;
+ }
+ }
+
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
@@ -10761,7 +11403,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName);
// startProcessLocked() returns existing proc's record if it's already running
ProcessRecord proc = startProcessLocked(app.processName, app,
- false, 0, "backup", hostingName);
+ false, 0, "backup", hostingName, false);
if (proc == null) {
Log.e(TAG, "Unable to start backup agent process " + r);
return false;
@@ -10781,8 +11423,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
try {
proc.thread.scheduleCreateBackupAgent(app, backupMode);
} catch (RemoteException e) {
- // !!! TODO: notify the backup manager that we crashed, or rely on
- // death notices, or...?
+ // Will time out on the backup manager side
}
} else {
if (DEBUG_BACKUP) Log.v(TAG, "Agent proc not running, waiting for attach");
@@ -10969,7 +11610,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Intent intent = (Intent)allSticky.get(i);
BroadcastRecord r = new BroadcastRecord(intent, null,
null, -1, -1, null, receivers, null, 0, null, null,
- false);
+ false, true);
if (mParallelBroadcasts.size() == 0) {
scheduleBroadcastsLocked();
}
@@ -11194,7 +11835,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
BroadcastRecord r = new BroadcastRecord(intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
registeredReceivers, resultTo, resultCode, resultData, map,
- ordered);
+ ordered, false);
if (DEBUG_BROADCAST) Log.v(
TAG, "Enqueueing parallel broadcast " + r
+ ": prev had " + mParallelBroadcasts.size());
@@ -11273,7 +11914,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|| resultTo != null) {
BroadcastRecord r = new BroadcastRecord(intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
- receivers, resultTo, resultCode, resultData, map, ordered);
+ receivers, resultTo, resultCode, resultData, map, ordered, false);
if (DEBUG_BROADCAST) Log.v(
TAG, "Enqueueing ordered broadcast " + r
+ ": prev had " + mOrderedBroadcasts.size());
@@ -11298,10 +11939,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
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
- int flags = intent.getFlags();
if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
intent = new Intent(intent);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -11312,6 +11954,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ throw new IllegalArgumentException(
+ "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ }
+
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
@@ -11571,15 +12218,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
static void performReceive(ProcessRecord app, IIntentReceiver receiver,
- Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered) throws RemoteException {
+ Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered, boolean sticky) throws RemoteException {
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.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered);
+ data, extras, ordered, sticky);
} else {
- receiver.performReceive(intent, resultCode, data, extras, ordered);
+ receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
}
}
@@ -11643,7 +12290,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
performReceive(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, r.ordered);
+ r.resultData, r.resultExtras, r.ordered, r.sticky);
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
@@ -11698,8 +12345,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// broadcast, then do nothing at this point. Just in case, we
// check that the process we're waiting for still exists.
if (mPendingBroadcast != null) {
- Log.i(TAG, "processNextBroadcast: waiting for "
- + mPendingBroadcast.curApp);
+ if (DEBUG_BROADCAST_LIGHT) {
+ Log.v(TAG, "processNextBroadcast: waiting for "
+ + mPendingBroadcast.curApp);
+ }
boolean isDead;
synchronized (mPidsSelfLocked) {
@@ -11774,7 +12423,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
performReceive(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false);
+ r.resultData, r.resultExtras, false, false);
} catch (RemoteException e) {
Log.w(TAG, "Failure sending broadcast result of " + r.intent, e);
}
@@ -11906,12 +12555,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// restart the application.
}
- // Not running -- get it started, and enqueue this history record
- // to be executed when the app comes up.
+ // Not running -- get it started, to be executed when the app comes up.
if ((r.curApp=startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- "broadcast", r.curComponent)) == null) {
+ "broadcast", r.curComponent,
+ (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0))
+ == null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Log.w(TAG, "Unable to launch app "
@@ -12119,7 +12769,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Configuration newConfig = new Configuration(mConfiguration);
changes = newConfig.updateFrom(values);
if (changes != 0) {
- if (DEBUG_SWITCH) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
Log.i(TAG, "Updating configuration to: " + values);
}
@@ -12132,6 +12782,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
mConfiguration = newConfig;
+ Log.i(TAG, "Config changed: " + newConfig);
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
msg.obj = new Configuration(mConfiguration);
@@ -12142,6 +12793,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ProcessRecord app = mLRUProcesses.get(i);
try {
if (app.thread != null) {
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Sending to proc "
+ + app.processName + " new config " + mConfiguration);
app.thread.scheduleConfigurationChanged(mConfiguration);
}
} catch (Exception e) {
@@ -12211,6 +12864,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (andResume) {
r.results = null;
r.newIntents = null;
+ reportResumedActivityLocked(r);
}
return true;
@@ -12225,19 +12879,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
*/
private final boolean ensureActivityConfigurationLocked(HistoryRecord r,
int globalChanges) {
- if (DEBUG_SWITCH) Log.i(TAG, "Ensuring correct configuration: " + r);
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.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) Log.i(TAG, "Configuration unchanged in " + r);
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG,
+ "Configuration unchanged in " + r);
return true;
}
// We don't worry about activities that are finishing.
if (r.finishing) {
- if (DEBUG_SWITCH) Log.i(TAG,
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG,
"Configuration doesn't matter in finishing " + r);
r.stopFreezingScreenLocked(false);
return true;
@@ -12251,7 +12907,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// 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) Log.i(TAG,
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG,
"Configuration doesn't matter not running " + r);
r.stopFreezingScreenLocked(false);
return true;
@@ -12263,22 +12919,26 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Figure out what has changed between the two configurations.
int changes = oldConfig.diff(newConfig);
- if (DEBUG_SWITCH) {
- Log.i(TAG, "Checking to restart " + r.info.name + ": changed=0x"
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
+ Log.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
+ Integer.toHexString(changes) + ", handles=0x"
- + Integer.toHexString(r.info.configChanges));
+ + 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) Log.i(TAG, "Switch is destroying non-running " + r);
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.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) Log.v(TAG,
+ "Switch is skipping already pausing " + r);
r.configDestroy = true;
return true;
} else if (r.state == ActivityState.RESUMED) {
@@ -12286,11 +12946,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// and we need to restart the top, resumed activity.
// Instead of doing the normal handshaking, just say
// "restart!".
- if (DEBUG_SWITCH) Log.i(TAG, "Switch is restarting resumed " + r);
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG,
+ "Switch is restarting resumed " + r);
relaunchActivityLocked(r, r.configChangeFlags, true);
r.configChangeFlags = 0;
} else {
- if (DEBUG_SWITCH) Log.i(TAG, "Switch is restarting non-resumed " + r);
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Log.v(TAG,
+ "Switch is restarting non-resumed " + r);
relaunchActivityLocked(r, r.configChangeFlags, false);
r.configChangeFlags = 0;
}
@@ -12308,6 +12970,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// it last got.
if (r.app != null && r.app.thread != null) {
try {
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Sending new config to " + r);
r.app.thread.scheduleActivityConfigurationChanged(r);
} catch (RemoteException e) {
// If process died, whatever.
@@ -12360,6 +13023,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return (app.curAdj=app.maxAdj);
}
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
app.adjSource = null;
app.adjTarget = null;
@@ -12440,15 +13104,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (app.services.size() != 0 && adj > FOREGROUND_APP_ADJ) {
- // 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
- // its services we may bump it up from there.
- if (adj > hiddenAdj) {
- adj = hiddenAdj;
- app.adjType = "bg-services";
- }
final long now = SystemClock.uptimeMillis();
// This process is more important if the top activity is
// bound to the service.
@@ -12493,6 +13148,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
adj = clientAdj > VISIBLE_APP_ADJ
? clientAdj : VISIBLE_APP_ADJ;
app.adjType = "service";
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE;
app.adjSource = cr.binding.client;
app.adjTarget = s.serviceInfo.name;
}
@@ -12506,23 +13163,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|| a.state == ActivityState.PAUSING)) {
adj = FOREGROUND_APP_ADJ;
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
+ // 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
+ // its services we may bump it up from there.
+ if (adj > hiddenAdj) {
+ adj = hiddenAdj;
+ app.adjType = "bg-services";
+ }
}
if (app.pubProviders.size() != 0 && adj > FOREGROUND_APP_ADJ) {
- // If this process has published any content providers, then
- // its adjustment makes it at least as important as any of the
- // processes using those providers, and no less important than
- // CONTENT_PROVIDER_ADJ, which is just shy of EMPTY.
- if (adj > CONTENT_PROVIDER_ADJ) {
- adj = CONTENT_PROVIDER_ADJ;
- app.adjType = "pub-providers";
- }
Iterator jt = app.pubProviders.values().iterator();
while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) {
ContentProviderRecord cpr = (ContentProviderRecord)jt.next();
@@ -12548,6 +13209,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
adj = clientAdj > FOREGROUND_APP_ADJ
? clientAdj : FOREGROUND_APP_ADJ;
app.adjType = "provider";
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_PROVIDER_IN_USE;
app.adjSource = client;
app.adjTarget = cpr.info.name;
}
@@ -12564,6 +13227,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
}
+
+ // Finally, if this process has published any content providers,
+ // then its adjustment makes it at least as important as any of the
+ // processes using those providers, and no less important than
+ // CONTENT_PROVIDER_ADJ, which is just shy of EMPTY.
+ if (adj > CONTENT_PROVIDER_ADJ) {
+ adj = CONTENT_PROVIDER_ADJ;
+ app.adjType = "pub-providers";
+ }
}
app.curRawAdj = adj;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index c834b34..d59aead 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.bluetooth.BluetoothHeadset;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -23,11 +24,11 @@ import android.os.Parcel;
import android.os.Process;
import android.os.ServiceManager;
import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.PowerProfile;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -49,6 +50,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
public void publish(Context context) {
mContext = context;
ServiceManager.addService("batteryinfo", asBinder());
+ mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps());
+ mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_radioScanningTimeout)
+ * 1000L);
}
public void shutdown() {
@@ -193,10 +198,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
}
}
- public void noteAirplaneMode(boolean airplaneMode) {
+ public void notePhoneState(int state) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteAirplaneModeLocked(airplaneMode);
+ mStats.notePhoneStateLocked(state);
}
}
@@ -258,8 +263,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
public void noteBluetoothOn() {
enforceCallingPermission();
+ BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
synchronized (mStats) {
mStats.noteBluetoothOnLocked();
+ mStats.setBtHeadset(headset);
}
}
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index da55049..db0a6cb 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -39,7 +39,9 @@ class BroadcastRecord extends Binder {
final String callerPackage; // who sent this
final int callingPid; // the pid of who sent this
final int callingUid; // the uid of who sent this
- String requiredPermission; // a permission the caller has required
+ final boolean ordered; // serialize the send to receivers?
+ final boolean sticky; // originated from existing sticky data?
+ final String requiredPermission; // a permission the caller has required
final List receivers; // contains BroadcastFilter and ResolveInfo
final IIntentReceiver resultTo; // who receives final result if non-null
long dispatchTime; // when dispatch started on this set of receivers
@@ -48,7 +50,6 @@ class BroadcastRecord extends Binder {
String resultData; // current result data value.
Bundle resultExtras; // current result extra data values.
boolean resultAbort; // current result abortBroadcast value.
- boolean ordered; // serialize the send to receivers?
int nextReceiver; // next receiver to be executed.
IBinder receiver; // who is currently running, null if none.
int state;
@@ -86,7 +87,7 @@ class BroadcastRecord extends Binder {
+ " resultCode=" + resultCode + " resultData=" + resultData);
pw.println(prefix + "resultExtras=" + resultExtras);
pw.println(prefix + "resultAbort=" + resultAbort
- + " ordered=" + ordered);
+ + " ordered=" + ordered + " sticky=" + sticky);
pw.println(prefix + "nextReceiver=" + nextReceiver
+ " receiver=" + receiver);
pw.println(prefix + "curFilter=" + curFilter);
@@ -122,7 +123,8 @@ class BroadcastRecord extends Binder {
BroadcastRecord(Intent _intent, ProcessRecord _callerApp, String _callerPackage,
int _callingPid, int _callingUid, String _requiredPermission,
List _receivers, IIntentReceiver _resultTo, int _resultCode,
- String _resultData, Bundle _resultExtras, boolean _serialized) {
+ String _resultData, Bundle _resultExtras, boolean _serialized,
+ boolean _sticky) {
intent = _intent;
callerApp = _callerApp;
callerPackage = _callerPackage;
@@ -135,6 +137,7 @@ class BroadcastRecord extends Binder {
resultData = _resultData;
resultExtras = _resultExtras;
ordered = _serialized;
+ sticky = _sticky;
nextReceiver = 0;
state = IDLE;
}
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index b3343dd..f613b00 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.app.IServiceConnection;
+import android.app.PendingIntent;
import java.io.PrintWriter;
@@ -28,6 +29,8 @@ class ConnectionRecord {
final HistoryRecord 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.
+ final PendingIntent clientIntent; // How to launch the client.
String stringName; // Caching of toString.
void dump(PrintWriter pw, String prefix) {
@@ -40,11 +43,14 @@ class ConnectionRecord {
}
ConnectionRecord(AppBindRecord _binding, HistoryRecord _activity,
- IServiceConnection _conn, int _flags) {
+ IServiceConnection _conn, int _flags,
+ int _clientLabel, PendingIntent _clientIntent) {
binding = _binding;
activity = _activity;
conn = _conn;
flags = _flags;
+ clientLabel = _clientLabel;
+ clientIntent = _clientIntent;
}
public String toString() {
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index fa2a100..b3086d5 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -22,6 +22,7 @@ import android.content.IIntentReceiver;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -172,6 +173,14 @@ class PendingIntentRecord extends IIntentSender.Stub {
public int send(int code, Intent intent, String resolvedType,
IIntentReceiver finishedReceiver) {
+ return sendInner(code, intent, resolvedType, finishedReceiver,
+ null, null, 0, 0, 0);
+ }
+
+ int sendInner(int code, Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver,
+ IBinder resultTo, String resultWho, int requestCode,
+ int flagsMask, int flagsValues) {
synchronized(owner) {
if (!canceled) {
sent = true;
@@ -189,6 +198,9 @@ class PendingIntentRecord extends IIntentSender.Stub {
} else {
resolvedType = key.requestResolvedType;
}
+ flagsMask &= ~Intent.IMMUTABLE_FLAGS;
+ flagsValues &= flagsMask;
+ finalIntent.setFlags((finalIntent.getFlags()&~flagsMask) | flagsValues);
final long origId = Binder.clearCallingIdentity();
@@ -198,7 +210,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
try {
owner.startActivityInPackage(uid,
finalIntent, resolvedType,
- null, null, 0, false);
+ resultTo, resultWho, requestCode, false);
} catch (RuntimeException e) {
Log.w(ActivityManagerService.TAG,
"Unable to send startActivity intent", e);
@@ -236,7 +248,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
if (sendFinish) {
try {
finishedReceiver.performReceive(new Intent(finalIntent), 0,
- null, null, false);
+ null, null, false, false);
} catch (RemoteException e) {
}
}
@@ -246,7 +258,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
return 0;
}
}
- return -1;
+ return IActivityManager.START_CANCELED;
}
protected void finalize() throws Throwable {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 76fdf09..6202257 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -75,6 +75,7 @@ class ProcessRecord implements Watchdog.PssRequestor {
boolean reportLowMemory; // Set to true when waiting to report low mem
int lastPss; // Last pss size reported by app.
String adjType; // Debugging: primary thing impacting oom_adj.
+ int adjTypeCode; // Debugging: adj code to report to app.
Object adjSource; // Debugging: option dependent object.
Object adjTarget; // Debugging: target component impacting oom_adj.
@@ -93,7 +94,8 @@ class ProcessRecord implements Watchdog.PssRequestor {
// class (String) -> ContentProviderRecord
final HashMap pubProviders = new HashMap();
// All ContentProviderRecord process is using
- final HashSet conProviders = new HashSet();
+ final HashMap<ContentProviderRecord, Integer> conProviders
+ = new HashMap<ContentProviderRecord, Integer>();
boolean persistent; // always keep this application running?
boolean crashing; // are we in the process of crashing?
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 98df4b3..2534410 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -18,12 +18,16 @@ package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.SystemClock;
import java.io.PrintWriter;
@@ -60,13 +64,38 @@ class ServiceRecord extends Binder {
final HashMap<IBinder, ConnectionRecord> connections
= new HashMap<IBinder, ConnectionRecord>();
// IBinder -> ConnectionRecord of all bound clients
- final List<Intent> startArgs = new ArrayList<Intent>();
+
+ // 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; // asked to run as a foreground service?
+ ProcessRecord app; // where this service is running or null.
+ boolean isForeground; // is service currently in foreground mode?
+ int foregroundId; // Notification ID of last foreground req.
+ Notification foregroundNoti; // Notification record of foreground state.
long lastActivity; // last time there was some activity on the service.
boolean startRequested; // someone explicitly called start?
+ boolean stopIfKilled; // last onStart() said to stop if service killed?
+ boolean callStart; // last onStart() has asked to alway be called on restart.
int lastStartId; // identifier of most recent start request.
int executeNesting; // number of outstanding operations keeping foreground.
long executingStart; // start time of last execute request.
@@ -79,6 +108,25 @@ class ServiceRecord extends Binder {
String stringName; // caching of toString
+ 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 (si.deliveryCount != 0) {
+ pw.print(" dc="); pw.print(si.deliveryCount);
+ }
+ if (si.doneExecutingCount != 0) {
+ pw.print(" dxc="); pw.print(si.doneExecutingCount);
+ }
+ pw.print(" ");
+ if (si.intent != null) pw.println(si.intent.toString());
+ else pw.println("null");
+ }
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("intent={");
pw.print(intent.getIntent().toShortString(true, false));
@@ -93,20 +141,39 @@ class ServiceRecord extends Binder {
if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
pw.print(" dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
- pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
- pw.print(" lastActivity="); pw.println(lastActivity-now);
- pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
- pw.print(" startId="); pw.print(lastStartId);
- pw.print(" executeNesting="); pw.print(executeNesting);
+ 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(" crashCount="); pw.println(crashCount);
- pw.print(prefix); pw.print("totalRestartCount="); pw.print(totalRestartCount);
- pw.print(" restartCount="); pw.print(restartCount);
- pw.print(" restartDelay="); pw.print(restartDelay);
- pw.print(" restartTime="); pw.print(restartTime-now);
- pw.print(" nextRestartTime="); pw.println(nextRestartTime-now);
+ pw.print(" restartTime="); pw.println(restartTime);
+ if (startRequested || lastStartId != 0) {
+ pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
+ pw.print(" stopIfKilled="); pw.print(stopIfKilled);
+ pw.print(" callStart="); pw.print(callStart);
+ pw.print(" lastStartId="); pw.println(lastStartId);
+ }
+ if (executeNesting != 0 || crashCount != 0 || restartCount != 0
+ || 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(" crashCount="); pw.println(crashCount);
+ }
+ if (deliveredStarts.size() > 0) {
+ pw.print(prefix); pw.println("Delivered Starts:");
+ dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
+ }
+ if (pendingStarts.size() > 0) {
+ pw.print(prefix); pw.println("Pending Starts:");
+ dumpStartList(pw, prefix, pendingStarts, 0);
+ }
if (bindings.size() > 0) {
Iterator<IntentBindRecord> it = bindings.values().iterator();
+ pw.print(prefix); pw.println("Bindings:");
while (it.hasNext()) {
IntentBindRecord b = it.next();
pw.print(prefix); pw.print("* IntentBindRecord{");
@@ -167,6 +234,45 @@ class ServiceRecord extends Binder {
restartTime = 0;
}
+ public StartItem findDeliveredStart(int id, boolean remove) {
+ final int N = deliveredStarts.size();
+ for (int i=0; i<N; i++) {
+ StartItem si = deliveredStarts.get(i);
+ if (si.id == id) {
+ if (remove) deliveredStarts.remove(i);
+ return si;
+ }
+ }
+
+ return null;
+ }
+
+ public void postNotification() {
+ if (foregroundId != 0 && foregroundNoti != null) {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm != null) {
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotification(packageName, foregroundId,
+ foregroundNoti, outId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public void cancelNotification() {
+ if (foregroundId != 0) {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm != null) {
+ try {
+ inm.cancelNotification(packageName, foregroundId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
public String toString() {
if (stringName != null) {
return stringName;
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index d458911..373b44e 100755..100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -381,7 +381,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
// Copy current file to back up
File backupFile = new File(mFile.getPath() + ".bak");
- mFile.renameTo(backupFile);
+ if (!mFile.renameTo(backupFile)) {
+ Log.w(TAG, "Failed to persist new stats");
+ return;
+ }
try {
// Write mStats to file
writeStatsFLOCK();
@@ -695,7 +698,14 @@ public final class UsageStatsService extends IUsageStats.Stub {
if (NC > 0) {
for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) {
sb.append("A:");
- sb.append(ent.getKey());
+ String activity = ent.getKey();
+ if (activity.startsWith(pkgName)) {
+ sb.append('*');
+ sb.append(activity.substring(
+ pkgName.length(), activity.length()));
+ } else {
+ sb.append(activity);
+ }
TimeStats times = ent.getValue();
sb.append(',');
sb.append(times.count);
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
index 63a7d70..0a3411a 100644
--- a/services/java/com/android/server/status/NotificationData.java
+++ b/services/java/com/android/server/status/NotificationData.java
@@ -5,6 +5,7 @@ import android.widget.RemoteViews;
public class NotificationData {
public String pkg;
+ public String tag;
public int id;
public CharSequence tickerText;
@@ -17,9 +18,6 @@ public class NotificationData {
public PendingIntent deleteIntent;
- public NotificationData() {
- }
-
public String toString() {
return "NotificationData(package=" + pkg + " tickerText=" + tickerText
+ " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java
index 6d09919..857784b 100644
--- a/services/java/com/android/server/status/StatusBarIcon.java
+++ b/services/java/com/android/server/status/StatusBarIcon.java
@@ -149,6 +149,11 @@ class StatusBarIcon {
r = context.getResources();
}
+ if (data.iconId == 0) {
+ Log.w(StatusBarService.TAG, "No icon ID for slot " + data.slot);
+ return null;
+ }
+
try {
return r.getDrawable(data.iconId);
} catch (RuntimeException e) {
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index a4b47b5..2b9d18f 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -18,10 +18,9 @@ package com.android.server.status;
import android.app.AlertDialog;
import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothError;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothIntent;
+import android.bluetooth.BluetoothPbap;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -76,15 +75,8 @@ public class StatusBarPolicy {
private static StatusBarPolicy sInstance;
// message codes for the handler
- private static final int EVENT_DATA_CONN_STATE_CHANGED = 2;
- private static final int EVENT_DATA_ACTIVITY = 3;
private static final int EVENT_BATTERY_CLOSE = 4;
- // indices into mBatteryThresholds
- private static final int BATTERY_THRESHOLD_CLOSE_WARNING = 0;
- private static final int BATTERY_THRESHOLD_WARNING = 1;
- private static final int BATTERY_THRESHOLD_EMPTY = 2;
-
private final Context mContext;
private final StatusBarService mService;
private final Handler mHandler = new StatusBarHandler();
@@ -99,26 +91,21 @@ public class StatusBarPolicy {
private IBinder mBatteryIcon;
private IconData mBatteryData;
private boolean mBatteryFirst = true;
- private int mBatteryPlugged;
+ private boolean mBatteryPlugged;
private int mBatteryLevel;
- private int mBatteryThreshold = 0; // index into mBatteryThresholds
- private int[] mBatteryThresholds = new int[] { 20, 15, -1 };
private AlertDialog mLowBatteryDialog;
private TextView mBatteryLevelTextView;
private View mBatteryView;
private int mBatteryViewSequence;
private boolean mBatteryShowLowOnEndCall = false;
- private boolean mSentLowBatteryBroadcast = false;
private static final boolean SHOW_LOW_BATTERY_WARNING = true;
// phone
private TelephonyManager mPhone;
private IBinder mPhoneIcon;
- private IBinder mPhoneEvdoIcon;
//***** Signal strength icons
private IconData mPhoneData;
- private IconData mPhoneEvdoData;
//GSM/UMTS
private static final int[] sSignalImages = new int[] {
com.android.internal.R.drawable.stat_sys_signal_0,
@@ -134,14 +121,6 @@ public class StatusBarPolicy {
com.android.internal.R.drawable.stat_sys_r_signal_3,
com.android.internal.R.drawable.stat_sys_r_signal_4
};
- //CDMA
- private static final int[] sSignalImages_cdma = new int[] {
- com.android.internal.R.drawable.stat_sys_signal_cdma_0,
- com.android.internal.R.drawable.stat_sys_signal_cdma_1,
- com.android.internal.R.drawable.stat_sys_signal_cdma_2,
- com.android.internal.R.drawable.stat_sys_signal_cdma_3,
- com.android.internal.R.drawable.stat_sys_signal_cdma_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
@@ -241,14 +220,6 @@ public class StatusBarPolicy {
// 128-255 Reserved
};
- // EVDO
- private static final int[] sSignalImages_evdo = new int[] {
- com.android.internal.R.drawable.stat_sys_signal_evdo_0,
- com.android.internal.R.drawable.stat_sys_signal_evdo_1,
- com.android.internal.R.drawable.stat_sys_signal_evdo_2,
- com.android.internal.R.drawable.stat_sys_signal_evdo_3,
- com.android.internal.R.drawable.stat_sys_signal_evdo_4
- };
//***** Data connection icons
private int[] mDataIconList = sDataNetType_g;
@@ -271,20 +242,21 @@ public class StatusBarPolicy {
com.android.internal.R.drawable.stat_sys_data_out_e,
com.android.internal.R.drawable.stat_sys_data_inandout_e,
};
- //CDMA
- private static final int[] sDataNetType_evdo = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_evdo,
- com.android.internal.R.drawable.stat_sys_data_in_evdo,
- com.android.internal.R.drawable.stat_sys_data_out_evdo,
- com.android.internal.R.drawable.stat_sys_data_inandout_evdo,
- com.android.internal.R.drawable.stat_sys_data_dormant_evdo,
+ //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,
};
- private static final int[] sDataNetType_1xrtt = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_1xrtt,
- com.android.internal.R.drawable.stat_sys_data_in_1xrtt,
- com.android.internal.R.drawable.stat_sys_data_out_1xrtt,
- com.android.internal.R.drawable.stat_sys_data_inandout_1xrtt,
- com.android.internal.R.drawable.stat_sys_data_dormant_1xrtt,
+
+ //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
@@ -300,6 +272,7 @@ public class StatusBarPolicy {
private IBinder mDataIcon;
private IconData mDataData;
private boolean mDataIconVisible;
+ private boolean mHspaDataDistinguishable;
// ringer volume
private IBinder mVolumeIcon;
@@ -311,6 +284,7 @@ public class StatusBarPolicy {
private IconData mBluetoothData;
private int mBluetoothHeadsetState;
private int mBluetoothA2dpState;
+ private int mBluetoothPbapState;
private boolean mBluetoothEnabled;
// wifi
@@ -363,6 +337,9 @@ public class StatusBarPolicy {
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();
}
@@ -377,12 +354,17 @@ public class StatusBarPolicy {
else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
updateSyncState(intent);
}
- else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- updateBattery(intent);
+ else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
+ onBatteryLow(intent);
}
- else if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION) ||
- action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION) ||
- action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
+ 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) ||
@@ -430,12 +412,6 @@ public class StatusBarPolicy {
null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
mPhoneIcon = service.addIcon(mPhoneData, null);
- // phone_evdo_signal
- mPhoneEvdoData = IconData.makeIcon("phone_evdo_signal",
- null, com.android.internal.R.drawable.stat_sys_signal_evdo_0, 0, 0);
- mPhoneEvdoIcon = service.addIcon(mPhoneEvdoData, null);
- service.setIconVisibility(mPhoneEvdoIcon, false);
-
// register for phone state notifications.
((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
.listen(mPhoneStateListener,
@@ -473,15 +449,15 @@ public class StatusBarPolicy {
mBluetoothData = IconData.makeIcon("bluetooth",
null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
mBluetoothIcon = service.addIcon(mBluetoothData, null);
- BluetoothDevice bluetooth =
- (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
- if (bluetooth != null) {
- mBluetoothEnabled = bluetooth.isEnabled();
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ mBluetoothEnabled = adapter.isEnabled();
} else {
mBluetoothEnabled = false;
}
mBluetoothA2dpState = BluetoothA2dp.STATE_DISCONNECTED;
mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
+ mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
// Gps status
@@ -490,7 +466,7 @@ public class StatusBarPolicy {
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);
+ service.setIconVisibility(mGpsIcon, false);
// Alarm clock
mAlarmClockIconData = IconData.makeIcon(
@@ -513,7 +489,7 @@ public class StatusBarPolicy {
mVolumeIcon = service.addIcon(mVolumeData, null);
service.setIconVisibility(mVolumeIcon, false);
updateVolume();
-
+
IntentFilter filter = new IntentFilter();
// Register for Intent broadcasts for...
@@ -521,14 +497,18 @@ public class StatusBarPolicy {
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(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
- filter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION);
- filter.addAction(BluetoothA2dp.SINK_STATE_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);
@@ -538,6 +518,14 @@ public class StatusBarPolicy {
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) {
@@ -564,38 +552,22 @@ public class StatusBarPolicy {
//mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
}
- private void pickNextBatteryLevel(int level) {
- final int N = mBatteryThresholds.length;
- for (int i=0; i<N; i++) {
- if (level >= mBatteryThresholds[i]) {
- mBatteryThreshold = i;
- break;
- }
- }
- if (mBatteryThreshold >= N) {
- mBatteryThreshold = N-1;
- }
- }
-
private final void updateBattery(Intent intent) {
mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
mBatteryData.iconLevel = intent.getIntExtra("level", 0);
mService.updateIcon(mBatteryIcon, mBatteryData, null);
- int plugged = intent.getIntExtra("plugged", 0);
+ boolean plugged = intent.getIntExtra("plugged", 0) != 0;
int level = intent.getIntExtra("level", -1);
if (false) {
Log.d(TAG, "updateBattery level=" + level
+ " plugged=" + plugged
+ " mBatteryPlugged=" + mBatteryPlugged
+ " mBatteryLevel=" + mBatteryLevel
- + " mBatteryThreshold=" + mBatteryThreshold
+ " mBatteryFirst=" + mBatteryFirst);
}
- int oldPlugged = mBatteryPlugged;
- int oldThreshold = mBatteryThreshold;
- pickNextBatteryLevel(level);
+ boolean oldPlugged = mBatteryPlugged;
mBatteryPlugged = plugged;
mBatteryLevel = level;
@@ -617,49 +589,35 @@ public class StatusBarPolicy {
}
*/
if (false) {
- Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level
- + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold);
+ Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
}
- if (plugged == 0
- && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
- || (mBatteryThreshold > oldThreshold
- && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) {
- // Broadcast the low battery warning
- mSentLowBatteryBroadcast = true;
- Intent batIntent = new Intent(Intent.ACTION_BATTERY_LOW);
- batIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(batIntent);
-
- if (SHOW_LOW_BATTERY_WARNING) {
- if (false) {
- Log.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
+ }
- if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- showLowBatteryWarning();
- } else {
- mBatteryShowLowOnEndCall = true;
- }
- }
- } else if (mBatteryThreshold < BATTERY_THRESHOLD_WARNING) {
- if (mSentLowBatteryBroadcast == true) {
- mSentLowBatteryBroadcast = false;
- Intent batIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
- batIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(batIntent);
+ private void onBatteryLow(Intent intent) {
+ if (SHOW_LOW_BATTERY_WARNING) {
+ if (false) {
+ Log.d(TAG, "mPhoneState=" + mPhoneState
+ + " mLowBatteryDialog=" + mLowBatteryDialog
+ + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
}
- if (SHOW_LOW_BATTERY_WARNING) {
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = false;
- }
+
+ if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+ showLowBatteryWarning();
+ } else {
+ mBatteryShowLowOnEndCall = true;
}
}
}
- private void showBatteryView() {
+ 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();
@@ -675,15 +633,20 @@ public class StatusBarPolicy {
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,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_BLUR_BEHIND
- | WindowManager.LayoutParams.FLAG_DIM_BEHIND,
- pixelFormat);
+ flags, pixelFormat);
// Get the dim amount from the theme
TypedArray a = mContext.obtainStyledAttributes(
@@ -720,9 +683,9 @@ public class StatusBarPolicy {
private void showLowBatteryWarning() {
closeLastBatteryView();
- int level = mBatteryThresholds[mBatteryThreshold > 1 ? mBatteryThreshold - 1 : 0];
+ // Show exact battery level.
CharSequence levelText = mContext.getString(
- com.android.internal.R.string.battery_low_percent_format, level);
+ com.android.internal.R.string.battery_low_percent_format, mBatteryLevel);
if (mBatteryLevelTextView != null) {
mBatteryLevelTextView.setText(levelText);
@@ -738,7 +701,7 @@ public class StatusBarPolicy {
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
@@ -773,7 +736,7 @@ public class StatusBarPolicy {
}
if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
if (mBatteryShowLowOnEndCall) {
- if (mBatteryPlugged == 0) {
+ if (!mBatteryPlugged) {
showLowBatteryWarning();
}
mBatteryShowLowOnEndCall = false;
@@ -819,19 +782,23 @@ public class StatusBarPolicy {
public void onServiceStateChanged(ServiceState state) {
mServiceState = state;
updateSignalStrength();
- updateCdmaRoamingIcon();
+ 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) {
+ public void onDataConnectionStateChanged(int state, int networkType) {
mDataState = state;
- updateDataNetType();
+ updateDataNetType(networkType);
updateDataIcon();
}
@@ -854,7 +821,7 @@ public class StatusBarPolicy {
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;
}
@@ -868,7 +835,15 @@ public class StatusBarPolicy {
}
private boolean isCdma() {
- return ((mPhone != null) && (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA));
+ 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)));
}
private boolean hasService() {
@@ -887,9 +862,7 @@ public class StatusBarPolicy {
private final void updateSignalStrength() {
int iconLevel = -1;
- int evdoIconLevel = -1;
int[] iconList;
- int[] evdoIconList;
if (!hasService()) {
//Log.d(TAG, "updateSignalStrength: no service");
@@ -900,7 +873,6 @@ public class StatusBarPolicy {
mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
}
mService.updateIcon(mPhoneIcon, mPhoneData, null);
- mService.setIconVisibility(mPhoneEvdoIcon,false);
return;
}
@@ -917,73 +889,75 @@ public class StatusBarPolicy {
else if (asu >= 4) 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_cdma;
-
- int cdmaDbm = mSignalStrength.getCdmaDbm();
- 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;
-
- iconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
- }
+ iconList = this.sSignalImages;
- if ((mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
- || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_A)) {
- // Use Evdo icon
- evdoIconList = this.sSignalImages_evdo;
-
- int evdoEcio = mSignalStrength.getEvdoEcio();
- int evdoSnr = mSignalStrength.getEvdoSnr();
- int levelEvdoEcio = 0;
- int levelEvdoSnr = 0;
-
- // Ec/Io are in dB*10
- if (evdoEcio >= -650) levelEvdoEcio = 4;
- else if (evdoEcio >= -750) levelEvdoEcio = 3;
- else if (evdoEcio >= -900) levelEvdoEcio = 2;
- else if (evdoEcio >= -1050) levelEvdoEcio = 1;
- else levelEvdoEcio = 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;
-
- evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr;
-
- mPhoneEvdoData.iconId = evdoIconList[evdoIconLevel];
- mService.updateIcon(mPhoneEvdoIcon, mPhoneEvdoData, null);
- mService.setIconVisibility(mPhoneEvdoIcon,true);
- } else {
- mService.setIconVisibility(mPhoneEvdoIcon,false);
+ // 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) {
+ Log.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel());
+ }
+ } else {
+ iconLevel = getCdmaLevel();
+ }
}
-
mPhoneData.iconId = iconList[iconLevel];
mService.updateIcon(mPhoneIcon, mPhoneData, null);
}
- private final void updateDataNetType() {
- int net = mPhone.getNetworkType();
+ 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:
@@ -992,16 +966,25 @@ public class StatusBarPolicy {
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_1xrtt;
+ mDataIconList = this.sDataNetType_1x;
break;
case TelephonyManager.NETWORK_TYPE_1xRTT:
- mDataIconList = this.sDataNetType_1xrtt;
+ mDataIconList = this.sDataNetType_1x;
break;
case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
case TelephonyManager.NETWORK_TYPE_EVDO_A:
- mDataIconList = sDataNetType_evdo;
+ mDataIconList = sDataNetType_3g;
break;
default:
mDataIconList = sDataNetType_g;
@@ -1054,8 +1037,6 @@ public class StatusBarPolicy {
iconId = mDataIconList[3];
break;
case TelephonyManager.DATA_ACTIVITY_DORMANT:
- iconId = mDataIconList[4];
- break;
default:
iconId = mDataIconList[0];
break;
@@ -1104,23 +1085,26 @@ public class StatusBarPolicy {
int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
String action = intent.getAction();
- if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION)) {
- int state = intent.getIntExtra(BluetoothIntent.BLUETOOTH_STATE,
- BluetoothError.ERROR);
- mBluetoothEnabled = state == BluetoothDevice.BLUETOOTH_STATE_ON;
- } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
- mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
+ 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.SINK_STATE_CHANGED_ACTION)) {
- mBluetoothA2dpState = intent.getIntExtra(BluetoothA2dp.SINK_STATE,
+ } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
+ mBluetoothA2dpState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE,
BluetoothA2dp.STATE_DISCONNECTED);
+ } 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 ||
mBluetoothA2dpState == BluetoothA2dp.STATE_CONNECTED ||
- mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING) {
+ mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING ||
+ mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected;
}
@@ -1213,21 +1197,21 @@ public class StatusBarPolicy {
final String action = intent.getAction();
final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
- Log.i(TAG, "updateTTY: enabled: " + enabled);
+ if (false) Log.v(TAG, "updateTTY: enabled: " + enabled);
if (enabled) {
// TTY is on
- Log.i(TAG, "updateTTY: set TTY on");
+ if (false) Log.v(TAG, "updateTTY: set TTY on");
mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null);
mService.setIconVisibility(mTTYModeIcon, true);
} else {
// TTY is off
- Log.i(TAG, "updateTTY: set TTY off");
+ if (false) Log.v(TAG, "updateTTY: set TTY off");
mService.setIconVisibility(mTTYModeIcon, false);
}
}
- private final void updateCdmaRoamingIcon() {
+ private final void updateCdmaRoamingIcon(ServiceState state) {
if (!hasService()) {
mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
return;
@@ -1239,8 +1223,8 @@ public class StatusBarPolicy {
}
int[] iconList = sRoamingIndicatorImages_cdma;
- int iconIndex = mPhone.getCdmaEriIconIndex();
- int iconMode = mPhone.getCdmaEriIconMode();
+ int iconIndex = state.getCdmaEriIconIndex();
+ int iconMode = state.getCdmaEriIconMode();
if (iconIndex == -1) {
Log.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update");
@@ -1253,7 +1237,7 @@ public class StatusBarPolicy {
}
if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) {
- Log.d(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
+ if (false) Log.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
return;
}
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index b44168a..8d73904 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -126,7 +126,7 @@ public class StatusBarService extends IStatusBar.Stub
public interface NotificationCallbacks {
void onSetDisabled(int status);
void onClearAll();
- void onNotificationClick(String pkg, int id);
+ void onNotificationClick(String pkg, String tag, int id);
void onPanelRevealed();
}
@@ -140,7 +140,7 @@ public class StatusBarService extends IStatusBar.Stub
boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
- if (down) {
+ if (!down) {
StatusBarService.this.deactivate();
}
return true;
@@ -322,6 +322,7 @@ public class StatusBarService extends IStatusBar.Stub
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);
}
@@ -691,6 +692,7 @@ public class StatusBarService extends IStatusBar.Stub
mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
}
}
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
}
// icon
@@ -832,7 +834,7 @@ public class StatusBarService extends IStatusBar.Stub
content.setOnFocusChangeListener(mFocusChangeListener);
PendingIntent contentIntent = n.contentIntent;
if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.id));
+ content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
}
View child = null;
@@ -895,7 +897,7 @@ public class StatusBarService extends IStatusBar.Stub
com.android.internal.R.id.content);
PendingIntent contentIntent = n.contentIntent;
if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.id));
+ content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
}
}
catch (RuntimeException e) {
@@ -949,7 +951,9 @@ public class StatusBarService extends IStatusBar.Stub
panelSlightlyVisible(true);
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- mExpandedDialog.show();
+ 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);
@@ -972,15 +976,24 @@ public class StatusBarService extends IStatusBar.Stub
}
void animateCollapse() {
- if (SPEW) Log.d(TAG, "Animate collapse: expanded=" + mExpanded
- + " expanded visible=" + mExpandedVisible);
+ if (SPEW) {
+ Log.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible
+ + " mAnimating=" + mAnimating
+ + " mAnimVel=" + mAnimVel);
+ }
if (!mExpandedVisible) {
return;
}
- prepareTracking(mDisplay.getHeight()-1);
- performFling(mDisplay.getHeight()-1, -2000.0f, true);
+ if (mAnimating) {
+ return;
+ }
+
+ int y = mDisplay.getHeight()-1;
+ prepareTracking(y);
+ performFling(y, -2000.0f, true);
}
void performExpand() {
@@ -1017,7 +1030,9 @@ public class StatusBarService extends IStatusBar.Stub
}
mExpandedVisible = false;
panelSlightlyVisible(false);
- mExpandedDialog.hide();
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
mTrackingView.setVisibility(View.GONE);
if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
@@ -1046,6 +1061,7 @@ public class StatusBarService extends IStatusBar.Stub
else if (mAnimY < mStatusBarView.getHeight()) {
if (SPEW) Log.d(TAG, "Animation completed to collapsed state.");
mAnimating = false;
+ updateExpandedViewPos(0);
performCollapse();
}
else {
@@ -1095,7 +1111,7 @@ public class StatusBarService extends IStatusBar.Stub
mTracking = true;
mVelocityTracker = VelocityTracker.obtain();
boolean opening = !mExpanded;
- if (!mExpanded) {
+ if (opening) {
mAnimAccel = 2000.0f;
mAnimVel = 200;
mAnimY = mStatusBarView.getHeight();
@@ -1110,16 +1126,13 @@ public class StatusBarService extends IStatusBar.Stub
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);
}
- }
- if (opening) {
- makeExpandedVisible();
- } else {
updateExpandedViewPos(y + mViewDelta);
}
}
@@ -1247,11 +1260,13 @@ public class StatusBarService extends IStatusBar.Stub
private class Launcher implements View.OnClickListener {
private PendingIntent mIntent;
private String mPkg;
+ private String mTag;
private int mId;
- Launcher(PendingIntent intent, String pkg, int id) {
+ Launcher(PendingIntent intent, String pkg, String tag, int id) {
mIntent = intent;
mPkg = pkg;
+ mTag = tag;
mId = id;
}
@@ -1266,7 +1281,7 @@ public class StatusBarService extends IStatusBar.Stub
}
try {
mIntent.send();
- mNotificationCallbacks.onNotificationClick(mPkg, mId);
+ mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId);
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here. Just log the exception message.
Log.w(TAG, "Sending contentIntent failed: " + e);
@@ -1489,24 +1504,28 @@ public class StatusBarService extends IStatusBar.Stub
/// ---------- Expanded View --------------
pixelFormat = PixelFormat.TRANSLUCENT;
- if (false) {
- bg = mExpandedView.getBackground();
- if (bg != null) {
- pixelFormat = bg.getOpacity();
+ bg = mExpandedView.getBackground();
+ if (bg != null) {
+ pixelFormat = bg.getOpacity();
+ if (pixelFormat != PixelFormat.TRANSLUCENT) {
+ // we want good-looking gradients, so we force a 8-bits per
+ // pixel format.
+ pixelFormat = PixelFormat.RGBX_8888;
}
}
+ final int disph = mDisplay.getHeight();
lp = mExpandedDialog.getWindow().getAttributes();
lp.width = ViewGroup.LayoutParams.FILL_PARENT;
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
lp.x = 0;
- lp.y = 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_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_DITHER;
+ | WindowManager.LayoutParams.FLAG_DITHER
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
lp.format = pixelFormat;
lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
lp.setTitle("StatusBarExpanded");
@@ -1519,7 +1538,6 @@ public class StatusBarService extends IStatusBar.Stub
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
mExpandedDialog.show();
- mExpandedDialog.hide();
FrameLayout hack = (FrameLayout)mExpandedView.getParent();
hack.setForeground(null);
}
@@ -1541,20 +1559,29 @@ public class StatusBarService extends IStatusBar.Stub
void updateExpandedViewPos(int expandedPosition) {
if (SPEW) {
- Log.d(TAG, "updateExpandedViewPos before pos=" + expandedPosition
+ Log.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
+ " mTrackingParams.y=" + mTrackingParams.y
+ " mTrackingPosition=" + mTrackingPosition);
}
- // If the expanded view is not visible, there is no reason to do
- // any work.
+ 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 = mTrackingParams.y = -disph;
+ WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+ }
+ if (mExpandedParams != null) {
+ mExpandedParams.y = -disph;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
return;
}
-
+
// tracking view...
- int h = mStatusBarView.getHeight();
- int disph = mDisplay.getHeight();
int pos;
if (expandedPosition == EXPANDED_FULL_OPEN) {
pos = h;
@@ -1665,14 +1692,15 @@ public class StatusBarService extends IStatusBar.Stub
private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
public void onClick(View v) {
mNotificationCallbacks.onClearAll();
- performCollapse();
+ 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)) {
+ 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)) {
@@ -1726,7 +1754,7 @@ public class StatusBarService extends IStatusBar.Stub
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));
- Log.d(TAG, "updateResources");
+ if (false) Log.v(TAG, "updateResources");
}
//
diff --git a/services/java/com/android/server/status/TrackingPatternView.java b/services/java/com/android/server/status/TrackingPatternView.java
index 0ae9984..4cb8eff 100644
--- a/services/java/com/android/server/status/TrackingPatternView.java
+++ b/services/java/com/android/server/status/TrackingPatternView.java
@@ -55,8 +55,6 @@ public class TrackingPatternView extends View {
final int textureWidth = mTextureWidth;
final int textureHeight = mTextureHeight;
- Log.d("TrackingPatternView", "width=" + width + " textureWidth=" + textureWidth);
-
int x = 0;
int y;