diff options
Diffstat (limited to 'services/java/com/android')
31 files changed, 5935 insertions, 1908 deletions
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 1625853..d66c6e5 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -34,21 +34,26 @@ import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; -import android.util.Config; +import android.text.format.Time; +import android.util.EventLog; import android.util.Log; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.TimeZone; class AlarmManagerService extends IAlarmManager.Stub { + // The threshold for how long an alarm can be late before we print a + // warning message. The time duration is in milliseconds. + private static final long LATE_ALARM_THRESHOLD = 10 * 1000; + private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP; private static final int RTC_MASK = 1 << AlarmManager.RTC; private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP; @@ -72,6 +77,7 @@ class AlarmManagerService extends IAlarmManager.Stub { private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>(); private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>(); private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>(); + private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder(); // slots corresponding with the inexact-repeat interval buckets, // ordered from shortest to longest @@ -250,12 +256,12 @@ class AlarmManagerService extends IAlarmManager.Stub { if (localLOGV) Log.v(TAG, "timezone changed: " + current + ", new=" + zone.getID()); timeZoneWasChanged = true; SystemProperties.set(TIMEZONE_PROPERTY, zone.getID()); - - // Update the kernel timezone information - // Kernel tracks time offsets as 'minutes west of GMT' - int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000; - setKernelTimezone(mDescriptor, -(gmtOffset)); } + + // Update the kernel timezone information + // Kernel tracks time offsets as 'minutes west of GMT' + int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000; + setKernelTimezone(mDescriptor, -(gmtOffset)); } TimeZone.setDefault(null); @@ -338,11 +344,26 @@ class AlarmManagerService extends IAlarmManager.Stub { private int addAlarmLocked(Alarm alarm) { ArrayList<Alarm> alarmList = getAlarmList(alarm.type); - int index = Collections.binarySearch(alarmList, alarm); - index = (index < 0) ? ((index + 1) * -1) : index; - if (localLOGV) Log.v( - TAG, "Adding alarm " + alarm + " at " + index); + int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder); + if (index < 0) { + index = 0 - index - 1; + } + if (localLOGV) Log.v(TAG, "Adding alarm " + alarm + " at " + index); alarmList.add(index, alarm); + + if (localLOGV) { + // Display the list of alarms for this alarm type + Log.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type); + int position = 0; + for (Alarm a : alarmList) { + Time time = new Time(); + time.set(a.when); + String timeStr = time.format("%b %d %I:%M:%S %p"); + Log.v(TAG, position + ": " + timeStr + + " " + a.operation.getTargetPackage()); + position += 1; + } + } return index; } @@ -382,7 +403,7 @@ class AlarmManagerService extends IAlarmManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump AlarmManager from from pid=" + Binder.getCallingPid() @@ -459,14 +480,22 @@ class AlarmManagerService extends IAlarmManager.Stub { if (localLOGV) Log.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm); - if (alarm.when > now) - { + if (alarm.when > now) { // don't fire alarms in the future break; } + + // If the alarm is late, then print a warning message. + // Note that this can happen if the user creates a new event on + // the Calendar app with a reminder that is in the past. In that + // case, the reminder alarm will fire immediately. + if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) { + Log.v(TAG, "alarm is late! alarm time: " + alarm.when + + " now: " + now + " delay (in seconds): " + + (now - alarm.when) / 1000); + } - // add it to the trigger list so we can trigger it without the lock held. - // recurring alarms may have passed several alarm intervals while the + // Recurring alarms may have passed several alarm intervals while the // phone was asleep or off, so pass a trigger count when sending them. if (localLOGV) Log.v(TAG, "Alarm triggering: " + alarm); alarm.count = 1; @@ -481,28 +510,42 @@ class AlarmManagerService extends IAlarmManager.Stub { it.remove(); // if it repeats queue it up to be read-added to the list - if (alarm.repeatInterval > 0) - { + if (alarm.repeatInterval > 0) { repeats.add(alarm); } } // reset any repeating alarms. it = repeats.iterator(); - while (it.hasNext()) - { + while (it.hasNext()) { Alarm alarm = it.next(); alarm.when += alarm.count * alarm.repeatInterval; addAlarmLocked(alarm); } - if (alarmList.size() > 0) - { + if (alarmList.size() > 0) { setLocked(alarmList.get(0)); } } - private class Alarm implements Comparable<Alarm> { + /** + * This Comparator sorts Alarms into increasing time order. + */ + public static class IncreasingTimeOrder implements Comparator<Alarm> { + public int compare(Alarm a1, Alarm a2) { + long when1 = a1.when; + long when2 = a2.when; + if (when1 - when2 > 0) { + return 1; + } + if (when1 - when2 < 0) { + return -1; + } + return 0; + } + } + + private static class Alarm { public int type; public int count; public long when; @@ -515,15 +558,7 @@ class AlarmManagerService extends IAlarmManager.Stub { operation = null; } - public int compareTo(Alarm obj) - { - if (obj.when > this.when) return -1; - if (obj.when < this.when) return 1; - if (obj.operation.equals(this.operation) - && obj.repeatInterval == this.repeatInterval) return 0; - return -1; - } - + @Override public String toString() { return "Alarm{" @@ -701,11 +736,11 @@ class AlarmManagerService extends IAlarmManager.Stub { public void scheduleDateChangedEvent() { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); - calendar.add(Calendar.DAY_OF_MONTH, 1); calendar.set(Calendar.HOUR, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); + calendar.add(Calendar.DAY_OF_MONTH, 1); set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender); } diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java new file mode 100644 index 0000000..de5d0ac --- /dev/null +++ b/services/java/com/android/server/AppWidgetService.java @@ -0,0 +1,1148 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.PackageItemInfo; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; +import android.widget.RemoteViews; + +import java.io.IOException; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.HashSet; + +import com.android.internal.appwidget.IAppWidgetService; +import com.android.internal.appwidget.IAppWidgetHost; +import com.android.internal.util.XmlUtils; +import com.android.internal.util.FastXmlSerializer; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +class AppWidgetService extends IAppWidgetService.Stub +{ + private static final String TAG = "AppWidgetService"; + + private static final String SETTINGS_FILENAME = "appwidgets.xml"; + private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp"; + + /* + * When identifying a Host or Provider based on the calling process, use the uid field. + * When identifying a Host or Provider based on a package manager broadcast, use the + * package given. + */ + + static class Provider { + int uid; + AppWidgetProviderInfo info; + ArrayList<AppWidgetId> instances = new ArrayList(); + PendingIntent broadcast; + boolean zombie; // if we're in safe mode, don't prune this just because nobody references it + + int tag; // for use while saving state (the index) + } + + static class Host { + int uid; + int hostId; + String packageName; + ArrayList<AppWidgetId> instances = new ArrayList(); + IAppWidgetHost callbacks; + boolean zombie; // if we're in safe mode, don't prune this just because nobody references it + + int tag; // for use while saving state (the index) + } + + static class AppWidgetId { + int appWidgetId; + Provider provider; + RemoteViews views; + Host host; + } + + Context mContext; + PackageManager mPackageManager; + AlarmManager mAlarmManager; + ArrayList<Provider> mInstalledProviders = new ArrayList(); + int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1; + ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList(); + ArrayList<Host> mHosts = new ArrayList(); + boolean mSafeMode; + + AppWidgetService(Context context) { + mContext = context; + mPackageManager = context.getPackageManager(); + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + } + + public void systemReady(boolean safeMode) { + mSafeMode = safeMode; + + loadAppWidgetList(); + loadStateLocked(); + + // Register for the boot completed broadcast, so we can send the + // ENABLE broacasts. If we try to send them now, they time out, + // because the system isn't ready to handle them yet. + mContext.registerReceiver(mBroadcastReceiver, + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + + // 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); + } + + @Override + public 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 from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + synchronized (mAppWidgetIds) { + int N = mInstalledProviders.size(); + pw.println("Providers: (size=" + N + ")"); + for (int i=0; i<N; i++) { + Provider p = mInstalledProviders.get(i); + AppWidgetProviderInfo info = p.info; + pw.println(" [" + i + "] provder=" + info.provider + + " min=(" + info.minWidth + "x" + info.minHeight + ")" + + " updatePeriodMillis=" + info.updatePeriodMillis + + " initialLayout=" + info.initialLayout + " zombie=" + p.zombie); + } + + N = mAppWidgetIds.size(); + pw.println("AppWidgetIds: (size=" + N + ")"); + for (int i=0; i<N; i++) { + AppWidgetId id = mAppWidgetIds.get(i); + pw.println(" [" + i + "] appWidgetId=" + id.appWidgetId + + " host=" + id.host.hostId + "/" + id.host.packageName + " provider=" + + (id.provider == null ? "null" : id.provider.info.provider) + + " host.callbacks=" + (id.host != null ? id.host.callbacks : "(no host)") + + " views=" + id.views); + } + + N = mHosts.size(); + pw.println("Hosts: (size=" + N + ")"); + for (int i=0; i<N; i++) { + Host host = mHosts.get(i); + pw.println(" [" + i + "] packageName=" + host.packageName + " uid=" + host.uid + + " hostId=" + host.hostId + " callbacks=" + host.callbacks + + " instances.size=" + host.instances.size() + " zombie=" + host.zombie); + } + } + } + + public int allocateAppWidgetId(String packageName, int hostId) { + int callingUid = enforceCallingUid(packageName); + synchronized (mAppWidgetIds) { + int appWidgetId = mNextAppWidgetId++; + + Host host = lookupOrAddHostLocked(callingUid, packageName, hostId); + + AppWidgetId id = new AppWidgetId(); + id.appWidgetId = appWidgetId; + id.host = host; + + host.instances.add(id); + mAppWidgetIds.add(id); + + saveStateLocked(); + + return appWidgetId; + } + } + + public void deleteAppWidgetId(int appWidgetId) { + synchronized (mAppWidgetIds) { + AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); + if (id != null) { + deleteAppWidgetLocked(id); + saveStateLocked(); + } + } + } + + public void deleteHost(int hostId) { + synchronized (mAppWidgetIds) { + int callingUid = getCallingUid(); + Host host = lookupHostLocked(callingUid, hostId); + if (host != null) { + deleteHostLocked(host); + saveStateLocked(); + } + } + } + + public void deleteAllHosts() { + synchronized (mAppWidgetIds) { + int callingUid = getCallingUid(); + final int N = mHosts.size(); + boolean changed = false; + for (int i=N-1; i>=0; i--) { + Host host = mHosts.get(i); + if (host.uid == callingUid) { + deleteHostLocked(host); + changed = true; + } + } + if (changed) { + saveStateLocked(); + } + } + } + + void deleteHostLocked(Host host) { + final int N = host.instances.size(); + for (int i=N-1; i>=0; i--) { + AppWidgetId id = host.instances.get(i); + deleteAppWidgetLocked(id); + } + host.instances.clear(); + mHosts.remove(host); + // it's gone or going away, abruptly drop the callback connection + host.callbacks = null; + } + + void deleteAppWidgetLocked(AppWidgetId id) { + Host host = id.host; + host.instances.remove(id); + pruneHostLocked(host); + + mAppWidgetIds.remove(id); + + Provider p = id.provider; + if (p != null) { + p.instances.remove(id); + if (!p.zombie) { + // send the broacast saying that this appWidgetId has been deleted + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED); + intent.setComponent(p.info.provider); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId); + mContext.sendBroadcast(intent); + if (p.instances.size() == 0) { + // cancel the future updates + cancelBroadcasts(p); + + // send the broacast saying that the provider is not in use any more + intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED); + intent.setComponent(p.info.provider); + mContext.sendBroadcast(intent); + } + } + } + } + + void cancelBroadcasts(Provider p) { + if (p.broadcast != null) { + mAlarmManager.cancel(p.broadcast); + long token = Binder.clearCallingIdentity(); + try { + p.broadcast.cancel(); + } finally { + Binder.restoreCallingIdentity(token); + } + p.broadcast = null; + } + } + + public void bindAppWidgetId(int appWidgetId, ComponentName provider) { + mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, + "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider); + synchronized (mAppWidgetIds) { + AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); + if (id == null) { + throw new IllegalArgumentException("bad appWidgetId"); + } + if (id.provider != null) { + throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to " + + id.provider.info.provider); + } + Provider p = lookupProviderLocked(provider); + if (p == null) { + throw new IllegalArgumentException("not a appwidget provider: " + provider); + } + if (p.zombie) { + throw new IllegalArgumentException("can't bind to a 3rd party provider in" + + " safe mode: " + provider); + } + + id.provider = p; + p.instances.add(id); + int instancesSize = p.instances.size(); + if (instancesSize == 1) { + // tell the provider that it's ready + sendEnableIntentLocked(p); + } + + // send an update now -- We need this update now, and just for this appWidgetId. + // It's less critical when the next one happens, so when we schdule the next one, + // we add updatePeriodMillis to its start time. That time will have some slop, + // but that's okay. + sendUpdateIntentLocked(p, new int[] { appWidgetId }); + + // schedule the future updates + registerForBroadcastsLocked(p, getAppWidgetIds(p)); + saveStateLocked(); + } + } + + public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { + synchronized (mAppWidgetIds) { + AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); + if (id != null && id.provider != null && !id.provider.zombie) { + return id.provider.info; + } + return null; + } + } + + public RemoteViews getAppWidgetViews(int appWidgetId) { + synchronized (mAppWidgetIds) { + AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); + if (id != null) { + return id.views; + } + return null; + } + } + + public List<AppWidgetProviderInfo> getInstalledProviders() { + synchronized (mAppWidgetIds) { + final int N = mInstalledProviders.size(); + ArrayList<AppWidgetProviderInfo> result = new ArrayList(N); + for (int i=0; i<N; i++) { + Provider p = mInstalledProviders.get(i); + if (!p.zombie) { + result.add(p.info); + } + } + return result; + } + } + + public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) { + if (appWidgetIds == null) { + return; + } + if (appWidgetIds.length == 0) { + return; + } + final int N = appWidgetIds.length; + + synchronized (mAppWidgetIds) { + for (int i=0; i<N; i++) { + AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]); + updateAppWidgetInstanceLocked(id, views); + } + } + } + + public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) { + synchronized (mAppWidgetIds) { + Provider p = lookupProviderLocked(provider); + if (p == null) { + Log.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider); + return; + } + ArrayList<AppWidgetId> instances = p.instances; + final int N = instances.size(); + for (int i=0; i<N; i++) { + AppWidgetId id = instances.get(i); + updateAppWidgetInstanceLocked(id, views); + } + } + } + + void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) { + // allow for stale appWidgetIds and other badness + // lookup also checks that the calling process can access the appWidgetId + // drop unbound appWidgetIds (shouldn't be possible under normal circumstances) + if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) { + id.views = views; + + // is anyone listening? + if (id.host.callbacks != null) { + try { + // the lock is held, but this is a oneway call + id.host.callbacks.updateAppWidget(id.appWidgetId, views); + } catch (RemoteException e) { + // It failed; remove the callback. No need to prune because + // we know that this host is still referenced by this instance. + id.host.callbacks = null; + } + } + } + } + + public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId, + List<RemoteViews> updatedViews) { + int callingUid = enforceCallingUid(packageName); + synchronized (mAppWidgetIds) { + Host host = lookupOrAddHostLocked(callingUid, packageName, hostId); + host.callbacks = callbacks; + + updatedViews.clear(); + + ArrayList<AppWidgetId> instances = host.instances; + int N = instances.size(); + int[] updatedIds = new int[N]; + for (int i=0; i<N; i++) { + AppWidgetId id = instances.get(i); + updatedIds[i] = id.appWidgetId; + updatedViews.add(id.views); + } + return updatedIds; + } + } + + public void stopListening(int hostId) { + synchronized (mAppWidgetIds) { + Host host = lookupHostLocked(getCallingUid(), hostId); + host.callbacks = null; + pruneHostLocked(host); + } + } + + boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) { + if (id.host.uid == callingUid) { + // Apps hosting the AppWidget have access to it. + return true; + } + if (id.provider != null && id.provider.uid == callingUid) { + // Apps providing the AppWidget have access to it (if the appWidgetId has been bound) + return true; + } + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) + == PackageManager.PERMISSION_GRANTED) { + // Apps that can bind have access to all appWidgetIds. + return true; + } + // Nobody else can access it. + return false; + } + + AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) { + int callingUid = getCallingUid(); + final int N = mAppWidgetIds.size(); + for (int i=0; i<N; i++) { + AppWidgetId id = mAppWidgetIds.get(i); + if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) { + return id; + } + } + return null; + } + + Provider lookupProviderLocked(ComponentName provider) { + final int N = mInstalledProviders.size(); + for (int i=0; i<N; i++) { + Provider p = mInstalledProviders.get(i); + if (p.info.provider.equals(provider)) { + return p; + } + } + return null; + } + + Host lookupHostLocked(int uid, int hostId) { + final int N = mHosts.size(); + for (int i=0; i<N; i++) { + Host h = mHosts.get(i); + if (h.uid == uid && h.hostId == hostId) { + return h; + } + } + return null; + } + + Host lookupOrAddHostLocked(int uid, String packageName, int hostId) { + final int N = mHosts.size(); + for (int i=0; i<N; i++) { + Host h = mHosts.get(i); + if (h.hostId == hostId && h.packageName.equals(packageName)) { + return h; + } + } + Host host = new Host(); + host.packageName = packageName; + host.uid = uid; + host.hostId = hostId; + mHosts.add(host); + return host; + } + + void pruneHostLocked(Host host) { + if (host.instances.size() == 0 && host.callbacks == null) { + mHosts.remove(host); + } + } + + void loadAppWidgetList() { + PackageManager pm = mPackageManager; + + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent, + PackageManager.GET_META_DATA); + + final int N = broadcastReceivers.size(); + for (int i=0; i<N; i++) { + ResolveInfo ri = broadcastReceivers.get(i); + addProviderLocked(ri); + } + } + + boolean addProviderLocked(ResolveInfo ri) { + Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName, + ri.activityInfo.name), ri); + if (p != null) { + mInstalledProviders.add(p); + return true; + } else { + return false; + } + } + + void removeProviderLocked(int index, Provider p) { + int N = p.instances.size(); + for (int i=0; i<N; i++) { + AppWidgetId id = p.instances.get(i); + // Call back with empty RemoteViews + updateAppWidgetInstanceLocked(id, null); + // Stop telling the host about updates for this from now on + cancelBroadcasts(p); + // clear out references to this appWidgetId + id.host.instances.remove(id); + mAppWidgetIds.remove(id); + id.provider = null; + pruneHostLocked(id.host); + id.host = null; + } + p.instances.clear(); + mInstalledProviders.remove(index); + // no need to send the DISABLE broadcast, since the receiver is gone anyway + cancelBroadcasts(p); + } + + void sendEnableIntentLocked(Provider p) { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED); + intent.setComponent(p.info.provider); + mContext.sendBroadcast(intent); + } + + void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) { + if (appWidgetIds != null && appWidgetIds.length > 0) { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); + intent.setComponent(p.info.provider); + mContext.sendBroadcast(intent); + } + } + + void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) { + if (p.info.updatePeriodMillis > 0) { + // if this is the first instance, set the alarm. otherwise, + // rely on the fact that we've already set it and that + // PendingIntent.getBroadcast will update the extras. + boolean alreadyRegistered = p.broadcast != null; + int instancesSize = p.instances.size(); + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); + intent.setComponent(p.info.provider); + long token = Binder.clearCallingIdentity(); + try { + p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent, + PendingIntent.FLAG_UPDATE_CURRENT); + } finally { + Binder.restoreCallingIdentity(token); + } + if (!alreadyRegistered) { + mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + p.info.updatePeriodMillis, + p.info.updatePeriodMillis, p.broadcast); + } + } + } + + static int[] getAppWidgetIds(Provider p) { + int instancesSize = p.instances.size(); + int appWidgetIds[] = new int[instancesSize]; + for (int i=0; i<instancesSize; i++) { + appWidgetIds[i] = p.instances.get(i).appWidgetId; + } + return appWidgetIds; + } + + public int[] getAppWidgetIds(ComponentName provider) { + synchronized (mAppWidgetIds) { + Provider p = lookupProviderLocked(provider); + if (p != null && getCallingUid() == p.uid) { + return getAppWidgetIds(p); + } else { + return new int[0]; + } + } + } + + private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) { + Provider p = null; + + ActivityInfo activityInfo = ri.activityInfo; + XmlResourceParser parser = null; + try { + parser = activityInfo.loadXmlMetaData(mPackageManager, + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER); + if (parser == null) { + Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for " + + "AppWidget provider '" + component + '\''); + return null; + } + + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + // drain whitespace, comments, etc. + } + + String nodeName = parser.getName(); + if (!"appwidget-provider".equals(nodeName)) { + Log.w(TAG, "Meta-data does not start with appwidget-provider tag for" + + " AppWidget provider '" + component + '\''); + return null; + } + + p = new Provider(); + AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo(); + + info.provider = component; + p.uid = activityInfo.applicationInfo.uid; + + TypedArray sa = mContext.getResources().obtainAttributes(attrs, + com.android.internal.R.styleable.AppWidgetProviderInfo); + info.minWidth = sa.getDimensionPixelSize( + com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth, 0); + info.minHeight = sa.getDimensionPixelSize( + com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight, 0); + info.updatePeriodMillis = sa.getInt( + com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); + info.initialLayout = sa.getResourceId( + com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0); + String className = sa.getString( + com.android.internal.R.styleable.AppWidgetProviderInfo_configure); + if (className != null) { + info.configure = new ComponentName(component.getPackageName(), className); + } + info.label = activityInfo.loadLabel(mPackageManager).toString(); + info.icon = ri.getIconResource(); + sa.recycle(); + } catch (Exception e) { + // Ok to catch Exception here, because anything going wrong because + // of what a client process passes to us should not be fatal for the + // system process. + Log.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e); + return null; + } finally { + if (parser != null) parser.close(); + } + return p; + } + + int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException { + PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0); + if (pkgInfo == null || pkgInfo.applicationInfo == null) { + throw new PackageManager.NameNotFoundException(); + } + return pkgInfo.applicationInfo.uid; + } + + int enforceCallingUid(String packageName) throws IllegalArgumentException { + int callingUid = getCallingUid(); + int packageUid; + try { + packageUid = getUidForPackage(packageName); + } catch (PackageManager.NameNotFoundException ex) { + throw new IllegalArgumentException("packageName and uid don't match packageName=" + + packageName); + } + if (callingUid != packageUid) { + throw new IllegalArgumentException("packageName and uid don't match packageName=" + + packageName); + } + return callingUid; + } + + void sendInitialBroadcasts() { + synchronized (mAppWidgetIds) { + final int N = mInstalledProviders.size(); + for (int i=0; i<N; i++) { + Provider p = mInstalledProviders.get(i); + if (p.instances.size() > 0) { + sendEnableIntentLocked(p); + int[] appWidgetIds = getAppWidgetIds(p); + sendUpdateIntentLocked(p, appWidgetIds); + registerForBroadcastsLocked(p, appWidgetIds); + } + } + } + } + + // only call from initialization -- it assumes that the data structures are all empty + void loadStateLocked() { + File temp = savedStateTempFile(); + File real = savedStateRealFile(); + + // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the + // real one. if there is both a real file and a temp one, assume that the temp one isn't + // fully written and delete it. + if (real.exists()) { + readStateFromFileLocked(real); + if (temp.exists()) { + temp.delete(); + } + } else if (temp.exists()) { + readStateFromFileLocked(temp); + temp.renameTo(real); + } + } + + void saveStateLocked() { + File temp = savedStateTempFile(); + File real = savedStateRealFile(); + + if (!real.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 loadState code not + // use the temporary one until it's fully written, create an empty file + // for real, which will we'll shortly delete. + try { + real.createNewFile(); + } catch (IOException e) { + } + } + + if (temp.exists()) { + temp.delete(); + } + + writeStateToFileLocked(temp); + + real.delete(); + temp.renameTo(real); + } + + void writeStateToFileLocked(File file) { + FileOutputStream stream = null; + int N; + + try { + stream = new FileOutputStream(file, false); + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, "utf-8"); + out.startDocument(null, true); + + + out.startTag(null, "gs"); + + int providerIndex = 0; + N = mInstalledProviders.size(); + for (int i=0; i<N; i++) { + Provider p = mInstalledProviders.get(i); + if (p.instances.size() > 0) { + out.startTag(null, "p"); + out.attribute(null, "pkg", p.info.provider.getPackageName()); + out.attribute(null, "cl", p.info.provider.getClassName()); + out.endTag(null, "h"); + p.tag = providerIndex; + providerIndex++; + } + } + + N = mHosts.size(); + for (int i=0; i<N; i++) { + Host host = mHosts.get(i); + out.startTag(null, "h"); + out.attribute(null, "pkg", host.packageName); + out.attribute(null, "id", Integer.toHexString(host.hostId)); + out.endTag(null, "h"); + host.tag = i; + } + + N = mAppWidgetIds.size(); + for (int i=0; i<N; i++) { + AppWidgetId id = mAppWidgetIds.get(i); + out.startTag(null, "g"); + out.attribute(null, "id", Integer.toHexString(id.appWidgetId)); + out.attribute(null, "h", Integer.toHexString(id.host.tag)); + if (id.provider != null) { + out.attribute(null, "p", Integer.toHexString(id.provider.tag)); + } + out.endTag(null, "g"); + } + + out.endTag(null, "gs"); + + out.endDocument(); + stream.close(); + } catch (IOException e) { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException ex) { + } + if (file.exists()) { + file.delete(); + } + } + } + + void readStateFromFileLocked(File file) { + FileInputStream stream = null; + + boolean success = false; + + try { + stream = new FileInputStream(file); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, null); + + int type; + int providerIndex = 0; + HashMap<Integer,Provider> loadedProviders = new HashMap(); + do { + type = parser.next(); + if (type == XmlPullParser.START_TAG) { + String tag = parser.getName(); + if ("p".equals(tag)) { + // TODO: do we need to check that this package has the same signature + // as before? + String pkg = parser.getAttributeValue(null, "pkg"); + String cl = parser.getAttributeValue(null, "cl"); + Provider p = lookupProviderLocked(new ComponentName(pkg, cl)); + if (p == null && mSafeMode) { + // if we're in safe mode, make a temporary one + p = new Provider(); + p.info = new AppWidgetProviderInfo(); + p.info.provider = new ComponentName(pkg, cl); + p.zombie = true; + mInstalledProviders.add(p); + } + if (p != null) { + // if it wasn't uninstalled or something + loadedProviders.put(providerIndex, p); + } + providerIndex++; + } + else if ("h".equals(tag)) { + Host host = new Host(); + + // TODO: do we need to check that this package has the same signature + // as before? + host.packageName = parser.getAttributeValue(null, "pkg"); + try { + host.uid = getUidForPackage(host.packageName); + } catch (PackageManager.NameNotFoundException ex) { + host.zombie = true; + } + if (!host.zombie || mSafeMode) { + // In safe mode, we don't discard the hosts we don't recognize + // so that they're not pruned from our list. Otherwise, we do. + host.hostId = Integer.parseInt( + parser.getAttributeValue(null, "id"), 16); + mHosts.add(host); + } + } + else if ("g".equals(tag)) { + AppWidgetId id = new AppWidgetId(); + id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16); + if (id.appWidgetId >= mNextAppWidgetId) { + mNextAppWidgetId = id.appWidgetId + 1; + } + + String providerString = parser.getAttributeValue(null, "p"); + if (providerString != null) { + // there's no provider if it hasn't been bound yet. + // maybe we don't have to save this, but it brings the system + // to the state it was in. + int pIndex = Integer.parseInt(providerString, 16); + id.provider = loadedProviders.get(pIndex); + if (false) { + Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider " + + pIndex + " which is " + id.provider); + } + if (id.provider == null) { + // This provider is gone. We just let the host figure out + // that this happened when it fails to load it. + continue; + } + } + + int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16); + id.host = mHosts.get(hIndex); + if (id.host == null) { + // This host is gone. + continue; + } + + if (id.provider != null) { + id.provider.instances.add(id); + } + id.host.instances.add(id); + mAppWidgetIds.add(id); + } + } + } 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) { + } + + if (success) { + // delete any hosts that didn't manage to get connected (should happen) + // if it matters, they'll be reconnected. + final int N = mHosts.size(); + for (int i=0; i<N; i++) { + pruneHostLocked(mHosts.get(i)); + } + } else { + // failed reading, clean up + mAppWidgetIds.clear(); + mHosts.clear(); + final int N = mInstalledProviders.size(); + for (int i=0; i<N; i++) { + mInstalledProviders.get(i).instances.clear(); + } + } + } + + File savedStateTempFile() { + return new File("/data/system/" + SETTINGS_TMP_FILENAME); + //return new File(mContext.getFilesDir(), SETTINGS_FILENAME); + } + + File savedStateRealFile() { + return new File("/data/system/" + SETTINGS_FILENAME); + //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME); + } + + BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + //Log.d(TAG, "received " + action); + if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { + sendInitialBroadcasts(); + } else { + Uri uri = intent.getData(); + if (uri == null) { + return; + } + String pkgName = uri.getSchemeSpecificPart(); + if (pkgName == null) { + return; + } + + if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + synchronized (mAppWidgetIds) { + Bundle extras = intent.getExtras(); + if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { + // The package was just upgraded + updateProvidersForPackageLocked(pkgName); + } else { + // The package was just added + addProvidersForPackageLocked(pkgName); + } + saveStateLocked(); + } + } + else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + Bundle extras = intent.getExtras(); + if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { + // The package is being updated. We'll receive a PACKAGE_ADDED shortly. + } else { + synchronized (mAppWidgetIds) { + removeProvidersForPackageLocked(pkgName); + saveStateLocked(); + } + } + } + } + } + }; + + // TODO: If there's a better way of matching an intent filter against the + // packages for a given package, use that. + void addProvidersForPackageLocked(String pkgName) { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, + PackageManager.GET_META_DATA); + + final int N = broadcastReceivers.size(); + for (int i=0; i<N; i++) { + ResolveInfo ri = broadcastReceivers.get(i); + ActivityInfo ai = ri.activityInfo; + + if (pkgName.equals(ai.packageName)) { + addProviderLocked(ri); + } + } + } + + // TODO: If there's a better way of matching an intent filter against the + // packages for a given package, use that. + void updateProvidersForPackageLocked(String pkgName) { + HashSet<String> keep = new HashSet(); + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, + PackageManager.GET_META_DATA); + + // add the missing ones and collect which ones to keep + int N = broadcastReceivers.size(); + for (int i=0; i<N; i++) { + ResolveInfo ri = broadcastReceivers.get(i); + ActivityInfo ai = ri.activityInfo; + if (pkgName.equals(ai.packageName)) { + ComponentName component = new ComponentName(ai.packageName, ai.name); + Provider p = lookupProviderLocked(component); + if (p == null) { + if (addProviderLocked(ri)) { + keep.add(ai.name); + } + } else { + Provider parsed = parseProviderInfoXml(component, ri); + if (parsed != null) { + keep.add(ai.name); + // Use the new AppWidgetProviderInfo. + AppWidgetProviderInfo oldInfo = p.info; + p.info = parsed.info; + // If it's enabled + final int M = p.instances.size(); + if (M > 0) { + int[] appWidgetIds = getAppWidgetIds(p); + // Reschedule for the new updatePeriodMillis (don't worry about handling + // it specially if updatePeriodMillis didn't change because we just sent + // an update, and the next one will be updatePeriodMillis from now). + cancelBroadcasts(p); + registerForBroadcastsLocked(p, appWidgetIds); + // If it's currently showing, call back with the new AppWidgetProviderInfo. + for (int j=0; j<M; j++) { + AppWidgetId id = p.instances.get(j); + if (id.host != null && id.host.callbacks != null) { + try { + id.host.callbacks.providerChanged(id.appWidgetId, p.info); + } catch (RemoteException ex) { + // It failed; remove the callback. No need to prune because + // we know that this host is still referenced by this + // instance. + id.host.callbacks = null; + } + } + } + // Now that we've told the host, push out an update. + sendUpdateIntentLocked(p, appWidgetIds); + } + } + } + } + } + + // prune the ones we don't want to keep + N = mInstalledProviders.size(); + for (int i=N-1; i>=0; i--) { + Provider p = mInstalledProviders.get(i); + if (pkgName.equals(p.info.provider.getPackageName()) + && !keep.contains(p.info.provider.getClassName())) { + removeProviderLocked(i, p); + } + } + } + + void removeProvidersForPackageLocked(String pkgName) { + int N = mInstalledProviders.size(); + for (int i=N-1; i>=0; i--) { + Provider p = mInstalledProviders.get(i); + if (pkgName.equals(p.info.provider.getPackageName())) { + removeProviderLocked(i, p); + } + } + + // Delete the hosts for this package too + // + // By now, we have removed any AppWidgets that were in any hosts here, + // so we don't need to worry about sending DISABLE broadcasts to them. + N = mHosts.size(); + for (int i=N-1; i>=0; i--) { + Host host = mHosts.get(i); + if (pkgName.equals(host.packageName)) { + deleteHostLocked(host); + } + } + } +} + diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index ed53272..618b317 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -20,19 +20,31 @@ import com.android.internal.app.IBatteryStats; import com.android.server.am.BatteryStatsService; import android.app.ActivityManagerNative; +import android.content.ContentResolver; import android.content.Context; 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; +import android.os.SystemClock; import android.os.UEventObserver; +import android.provider.Checkin; +import android.provider.Settings; import android.util.EventLog; import android.util.Log; +import java.io.File; import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; -import java.lang.String; + + /** * <p>BatteryService monitors the charging status, and charge level of the device @@ -59,11 +71,27 @@ import java.lang.String; class BatteryService extends Binder { private static final String TAG = BatteryService.class.getSimpleName(); + private static final boolean LOCAL_LOGV = false; + static final int LOG_BATTERY_LEVEL = 2722; static final int LOG_BATTERY_STATUS = 2723; + static final int LOG_BATTERY_DISCHARGE_STATUS = 2730; static final int BATTERY_SCALE = 100; // battery capacity is a percentage + // Used locally for determining when to make a last ditch effort to log + // discharge stats before the device dies. + private static final int CRITICAL_BATTERY_LEVEL = 4; + + private static final int DUMP_MAX_LENGTH = 24 * 1024; + private static final String[] DUMPSYS_ARGS = new String[] { "-c", "-u" }; + private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo"; + + private static final String DUMPSYS_DATA_PATH = "/data/system/"; + + // This should probably be exposed in the API, though it's not critical + private static final int BATTERY_PLUGGED_NONE = 0; + private final Context mContext; private final IBatteryStats mBatteryStats; @@ -76,6 +104,7 @@ class BatteryService extends Binder { private int mBatteryVoltage; private int mBatteryTemperature; private String mBatteryTechnology; + private boolean mBatteryLevelCritical; private int mLastBatteryStatus; private int mLastBatteryHealth; @@ -83,9 +112,14 @@ class BatteryService extends Binder { private int mLastBatteryLevel; private int mLastBatteryVoltage; private int mLastBatteryTemperature; + private boolean mLastBatteryLevelCritical; private int mPlugType; - private int mLastPlugType; + private int mLastPlugType = -1; // Extra state so we can detect first run + + private long mDischargeStartTime; + private int mDischargeStartLevel; + public BatteryService(Context context) { mContext = context; @@ -113,9 +147,10 @@ class BatteryService extends Binder { } int plugTypeBit = 0; if (mAcOnline) { - plugTypeBit = BatteryManager.BATTERY_PLUGGED_AC; - } else if (mUsbOnline) { - plugTypeBit = BatteryManager.BATTERY_PLUGGED_USB; + plugTypeBit |= BatteryManager.BATTERY_PLUGGED_AC; + } + if (mUsbOnline) { + plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB; } return (plugTypeSet & plugTypeBit) != 0; } @@ -140,12 +175,14 @@ class BatteryService extends Binder { private synchronized final void update() { native_update(); + + mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL; if (mAcOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_AC; } else if (mUsbOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_USB; } else { - mPlugType = 0; + mPlugType = BATTERY_PLUGGED_NONE; } if (mBatteryStatus != mLastBatteryStatus || mBatteryHealth != mLastBatteryHealth || @@ -155,6 +192,27 @@ class BatteryService extends Binder { mBatteryVoltage != mLastBatteryVoltage || mBatteryTemperature != mLastBatteryTemperature) { + if (mPlugType != mLastPlugType) { + if (mLastPlugType == BATTERY_PLUGGED_NONE) { + // discharging -> charging + + // There's no value in this data unless we've discharged at least once and the + // battery level has changed; so don't log until it does. + if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryLevel) { + long duration = SystemClock.elapsedRealtime() - mDischargeStartTime; + EventLog.writeEvent(LOG_BATTERY_DISCHARGE_STATUS, duration, + mDischargeStartLevel, mBatteryLevel); + // make sure we see a discharge event before logging again + mDischargeStartTime = 0; + + logOutlier(duration); + } + } else if (mPlugType == BATTERY_PLUGGED_NONE) { + // charging -> discharging or we just powered up + mDischargeStartTime = SystemClock.elapsedRealtime(); + mDischargeStartLevel = mBatteryLevel; + } + } if (mBatteryStatus != mLastBatteryStatus || mBatteryHealth != mLastBatteryHealth || mBatteryPresent != mLastBatteryPresent || @@ -169,6 +227,12 @@ class BatteryService extends Binder { EventLog.writeEvent(LOG_BATTERY_LEVEL, mBatteryLevel, mBatteryVoltage, mBatteryTemperature); } + if (mBatteryLevelCritical && !mLastBatteryLevelCritical && + mPlugType == BATTERY_PLUGGED_NONE) { + // We want to make sure we log discharge cycle outliers + // if the battery is about to die. + logOutlier(SystemClock.elapsedRealtime() - mDischargeStartTime); + } // Separate broadcast is sent for power connected / not connected // since the standard intent will not wake any applications and some @@ -187,6 +251,7 @@ class BatteryService extends Binder { mLastPlugType = mPlugType; mLastBatteryVoltage = mBatteryVoltage; mLastBatteryTemperature = mBatteryTemperature; + mLastBatteryLevelCritical = mBatteryLevelCritical; sendIntent(); } @@ -197,7 +262,7 @@ class BatteryService extends Binder { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); try { - mBatteryStats.setOnBattery(mPlugType == 0); + mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE); } catch (RemoteException e) { // Should never happen. } @@ -229,6 +294,83 @@ class BatteryService extends Binder { ActivityManagerNative.broadcastStickyIntent(intent, null); } + private final void logBatteryStats() { + + IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME); + if (batteryInfoService != null) { + byte[] buffer = new byte[DUMP_MAX_LENGTH]; + File dumpFile = null; + FileOutputStream dumpStream = null; + try { + // dump the service to a file + dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump"); + dumpStream = new FileOutputStream(dumpFile); + batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS); + dumpStream.getFD().sync(); + + // read dumped file above into buffer truncated to DUMP_MAX_LENGTH + // and insert into events table. + int length = (int) Math.min(dumpFile.length(), DUMP_MAX_LENGTH); + FileInputStream fileInputStream = new FileInputStream(dumpFile); + int nread = fileInputStream.read(buffer, 0, length); + if (nread > 0) { + Checkin.logEvent(mContext.getContentResolver(), + Checkin.Events.Tag.BATTERY_DISCHARGE_INFO, + new String(buffer, 0, nread)); + if (LOCAL_LOGV) Log.v(TAG, "dumped " + nread + "b from " + + batteryInfoService + "to log"); + if (LOCAL_LOGV) Log.v(TAG, "actual dump:" + new String(buffer, 0, nread)); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to dump service '" + BATTERY_STATS_SERVICE_NAME + + "':" + e); + } catch (IOException e) { + Log.e(TAG, "failed to write dumpsys file: " + e); + } finally { + // make sure we clean up + if (dumpStream != null) { + try { + dumpStream.close(); + } catch (IOException e) { + Log.e(TAG, "failed to close dumpsys output stream"); + } + } + if (dumpFile != null && !dumpFile.delete()) { + Log.e(TAG, "failed to delete temporary dumpsys file: " + + dumpFile.getAbsolutePath()); + } + } + } + } + + private final void logOutlier(long duration) { + ContentResolver cr = mContext.getContentResolver(); + String dischargeThresholdString = Settings.Gservices.getString(cr, + Settings.Gservices.BATTERY_DISCHARGE_THRESHOLD); + String durationThresholdString = Settings.Gservices.getString(cr, + Settings.Gservices.BATTERY_DISCHARGE_DURATION_THRESHOLD); + + if (dischargeThresholdString != null && durationThresholdString != null) { + try { + long durationThreshold = Long.parseLong(durationThresholdString); + int dischargeThreshold = Integer.parseInt(dischargeThresholdString); + if (duration <= durationThreshold && + mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) { + // If the discharge cycle is bad enough we want to know about it. + logBatteryStats(); + } + if (LOCAL_LOGV) Log.v(TAG, "duration threshold: " + durationThreshold + + " discharge threshold: " + dischargeThreshold); + if (LOCAL_LOGV) Log.v(TAG, "duration: " + duration + " discharge: " + + (mDischargeStartLevel - mBatteryLevel)); + } catch (NumberFormatException e) { + Log.e(TAG, "Invalid DischargeThresholds GService string: " + + durationThresholdString + " or " + dischargeThresholdString); + return; + } + } + } + private final int getIcon(int level) { if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) { return com.android.internal.R.drawable.stat_sys_battery_charge; @@ -243,7 +385,7 @@ class BatteryService extends Binder { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump Battery service from from pid=" diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 65e3650..760988d 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -35,6 +35,7 @@ import android.os.Message; import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.Settings; +import android.provider.Sync; import android.util.EventLog; import android.util.Log; @@ -333,6 +334,32 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + /** + * @see ConnectivityManager#getBackgroundDataSetting() + */ + public boolean getBackgroundDataSetting() { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.BACKGROUND_DATA, 1) == 1; + } + + /** + * @see ConnectivityManager#setBackgroundDataSetting(boolean) + */ + public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) { + 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); + + Intent broadcast = new Intent( + ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); + mContext.sendBroadcast(broadcast); + } + private int getNumConnectedNetworks() { int numConnectedNets = 0; @@ -632,7 +659,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(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() diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java index 44f70f0..85861bb 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -67,6 +67,7 @@ class DeviceStorageMonitorService extends Binder { private static final int EVENT_LOG_LOW_STORAGE_NOTIFICATION = 2745; 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 mLastReportedFreeMem; private long mLastReportedFreeMemTime; @@ -82,6 +83,9 @@ class DeviceStorageMonitorService extends Binder { 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. @@ -100,7 +104,7 @@ class DeviceStorageMonitorService extends Binder { Log.e(TAG, "Will not process invalid message"); return; } - checkMemory(); + checkMemory(msg.arg1 == _TRUE); } }; @@ -109,7 +113,8 @@ class DeviceStorageMonitorService extends Binder { mClearSucceeded = succeeded; mClearingCache = false; if(localLOGV) Log.i(TAG, " Clear succeeded:"+mClearSucceeded - +", mClearingCache:"+mClearingCache); + +", mClearingCache:"+mClearingCache+" Forcing memory check"); + postCheckMemoryMsg(false, 0); } } @@ -145,11 +150,15 @@ class DeviceStorageMonitorService extends Binder { } private final void clearCache() { - CachePackageDataObserver observer = new CachePackageDataObserver(); + if (mClearCacheObserver == null) { + // Lazy instantiation + mClearCacheObserver = new CachePackageDataObserver(); + } mClearingCache = true; try { + if (localLOGV) Log.i(TAG, "Clearing cache"); IPackageManager.Stub.asInterface(ServiceManager.getService("package")). - freeStorageAndNotify(getMemThreshold(), observer); + freeStorageAndNotify(getMemThreshold(), mClearCacheObserver); } catch (RemoteException e) { Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e); mClearingCache = false; @@ -157,7 +166,7 @@ class DeviceStorageMonitorService extends Binder { } } - private final void checkMemory() { + 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 // and should be accessed via a lock but even if it does this test will fail now and @@ -172,16 +181,23 @@ class DeviceStorageMonitorService extends Binder { } else { restatDataDir(); if (localLOGV) Log.v(TAG, "freeMemory="+mFreeMem); + //post intent to NotificationManager to display icon if necessary long memThreshold = getMemThreshold(); if (mFreeMem < memThreshold) { if (!mLowMemFlag) { - //see if clearing cache helps - mThreadStartTime = System.currentTimeMillis(); - clearCache(); - Log.i(TAG, "Running low on memory. Sending notification"); - sendNotification(); - mLowMemFlag = true; + if (checkCache) { + // See if clearing cache helps + // Note that clearing cache is asynchronous and so we do a + // memory check again once the cache has been cleared. + mThreadStartTime = System.currentTimeMillis(); + mClearSucceeded = false; + clearCache(); + } else { + Log.i(TAG, "Running low on memory. Sending notification"); + sendNotification(); + mLowMemFlag = true; + } } else { if (localLOGV) Log.v(TAG, "Running low on memory " + "notification already sent. do nothing"); @@ -196,8 +212,15 @@ class DeviceStorageMonitorService extends Binder { } if(localLOGV) Log.i(TAG, "Posting Message again"); //keep posting messages to itself periodically - mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT), - MONITOR_INTERVAL*60*1000); + postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL); + } + + private void postCheckMemoryMsg(boolean clearCache, long delay) { + // Remove queued messages + mHandler.removeMessages(DEVICE_MEMORY_WHAT); + mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT, + clearCache ?_TRUE : _FALSE, 0), + delay); } /* @@ -231,7 +254,7 @@ class DeviceStorageMonitorService extends Binder { mTotalMemory = (mFileStats.getBlockCount()*mBlkSize)/100; mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW); mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK); - checkMemory(); + checkMemory(true); } @@ -281,14 +304,11 @@ class DeviceStorageMonitorService extends Binder { } public void updateMemory() { - ActivityManagerService ams = (ActivityManagerService)ServiceManager.getService("activity"); int callingUid = getCallingUid(); if(callingUid != Process.SYSTEM_UID) { return; } - //remove queued messages - mHandler.removeMessages(DEVICE_MEMORY_WHAT); - //force an early check - checkMemory(); + // force an early check + postCheckMemoryMsg(true, 0); } } diff --git a/services/java/com/android/server/FallbackCheckinService.java b/services/java/com/android/server/FallbackCheckinService.java new file mode 100644 index 0000000..cf22446 --- /dev/null +++ b/services/java/com/android/server/FallbackCheckinService.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.ICheckinService; +import android.os.IParentalControlCallback; +import android.util.Log; + +import java.io.IOException; + +import com.android.internal.os.RecoverySystem; +import com.google.android.net.ParentalControlState; + +/** + * @hide + */ +public final class FallbackCheckinService extends ICheckinService.Stub { + static final String TAG = "FallbackCheckinService"; + final Context mContext; + + public FallbackCheckinService(Context context) { + mContext = context; + } + + public boolean checkin() { + return false; // failure, because not implemented + } + + public void reportCrashSync(byte[] crashData) { + } + + public void reportCrashAsync(byte[] crashData) { + } + + public void masterClear() { + if (mContext.checkCallingOrSelfPermission("android.permission.MASTER_CLEAR") != + PackageManager.PERMISSION_GRANTED) { + Log.e(TAG, "Permission Denial: can't invoke masterClear from " + + "pid=" + Binder.getCallingPid() + ", " + + "uid=" + Binder.getCallingUid()); + return; + } + + // Save the android ID so the new system can get it erased. + try { + RecoverySystem.rebootAndWipe(); + } catch (IOException e) { + Log.e(TAG, "Reboot for masterClear() failed", e); + } + } + + public void getParentalControlState(IParentalControlCallback p, String requestingApp) + throws android.os.RemoteException { + ParentalControlState state = new ParentalControlState(); + state.isEnabled = false; + p.onResult(state); + } +} diff --git a/services/java/com/android/server/GadgetService.java b/services/java/com/android/server/GadgetService.java deleted file mode 100644 index 4be9ed5..0000000 --- a/services/java/com/android/server/GadgetService.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.PackageItemInfo; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.gadget.GadgetManager; -import android.gadget.GadgetInfo; -import android.os.Binder; -import android.util.AttributeSet; -import android.util.Log; -import android.util.Xml; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; - -import com.android.internal.gadget.IGadgetService; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -class GadgetService extends IGadgetService.Stub -{ - private static final String TAG = "GadgetService"; - - static class GadgetId { - int gadgetId; - String hostPackage; - GadgetInfo info; - } - - Context mContext; - PackageManager mPackageManager; - ArrayList<GadgetInfo> mInstalledProviders; - int mNextGadgetId = 1; - ArrayList<GadgetId> mGadgetIds = new ArrayList(); - - GadgetService(Context context) { - mContext = context; - mPackageManager = context.getPackageManager(); - mInstalledProviders = getGadgetList(); - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - synchronized (mGadgetIds) { - int N = mInstalledProviders.size(); - pw.println("Providers: (size=" + N + ")"); - for (int i=0; i<N; i++) { - GadgetInfo info = mInstalledProviders.get(i); - pw.println(" [" + i + "] provder=" + info.provider - + " min=(" + info.minWidth + "x" + info.minHeight + ")" - + " updatePeriodMillis=" + info.updatePeriodMillis - + " initialLayout=" + info.initialLayout); - } - - N = mGadgetIds.size(); - pw.println("GadgetIds: (size=" + N + ")"); - for (int i=0; i<N; i++) { - GadgetId id = mGadgetIds.get(i); - pw.println(" [" + i + "] gadgetId=" + id.gadgetId + " host=" + id.hostPackage - + " provider=" + (id.info == null ? "null" : id.info.provider)); - } - } - } - - public int allocateGadgetId(String hostPackage) { - synchronized (mGadgetIds) { - // TODO: Check for pick permission - int gadgetId = mNextGadgetId++; - - GadgetId id = new GadgetId(); - id.gadgetId = gadgetId; - id.hostPackage = hostPackage; - - mGadgetIds.add(id); - - return gadgetId; - } - } - - public void deleteGadgetId(int gadgetId) { - synchronized (mGadgetIds) { - String callingPackage = getCallingPackage(); - final int N = mGadgetIds.size(); - for (int i=0; i<N; i++) { - GadgetId id = mGadgetIds.get(i); - if (canAccessGadgetId(id, callingPackage)) { - mGadgetIds.remove(i); - // TODO: Notify someone? - return; - } - } - } - } - - public void bindGadgetId(int gadgetId, ComponentName provider) { - synchronized (mGadgetIds) { - GadgetId id = lookupGadgetIdLocked(gadgetId); - if (id == null) { - throw new IllegalArgumentException("bad gadgetId"); // TODO: use a better exception - } - if (id.info != null) { - throw new IllegalArgumentException("gadgetId " + gadgetId + " already bound to " - + id.info.provider); - } - GadgetInfo info = lookupGadgetInfoLocked(provider); - if (info == null) { - throw new IllegalArgumentException("not a gadget provider: " + provider); - } - - id.info = info; - } - } - - public GadgetInfo getGadgetInfo(int gadgetId) { - synchronized (mGadgetIds) { - GadgetId id = lookupGadgetIdLocked(gadgetId); - if (id != null) { - return id.info; - } - return null; - } - } - - public List<GadgetInfo> getInstalledProviders() { - synchronized (mGadgetIds) { - return new ArrayList<GadgetInfo>(mInstalledProviders); - } - } - - boolean canAccessGadgetId(GadgetId id, String callingPackage) { - if (id.hostPackage.equals(callingPackage)) { - return true; - } - if (id.info != null && id.info.provider.getPackageName().equals(callingPackage)) { - return true; - } - // TODO: Check for the pick permission - //if (has permission) { - // return true; - //} - //return false; - return true; - } - - private GadgetId lookupGadgetIdLocked(int gadgetId) { - String callingPackage = getCallingPackage(); - final int N = mGadgetIds.size(); - for (int i=0; i<N; i++) { - GadgetId id = mGadgetIds.get(i); - if (canAccessGadgetId(id, callingPackage)) { - return id; - } - } - return null; - } - - GadgetInfo lookupGadgetInfoLocked(ComponentName provider) { - final int N = mInstalledProviders.size(); - for (int i=0; i<N; i++) { - GadgetInfo info = mInstalledProviders.get(i); - if (info.provider.equals(provider)) { - return info; - } - } - return null; - } - - ArrayList<GadgetInfo> getGadgetList() { - PackageManager pm = mPackageManager; - - // TODO: We have these as different actions. I wonder if it makes more sense to - // have like a GADGET_ACTION, and then subcommands. It's kind of arbitrary that - // we look for GADGET_UPDATE_ACTION and not any of the other gadget actions. - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); - List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent, - PackageManager.GET_META_DATA); - - ArrayList<GadgetInfo> result = new ArrayList<GadgetInfo>(); - - final int N = broadcastReceivers.size(); - for (int i=0; i<N; i++) { - ResolveInfo ri = broadcastReceivers.get(i); - ActivityInfo ai = ri.activityInfo; - GadgetInfo gi = parseGadgetInfoXml(new ComponentName(ai.packageName, ai.name), - ri.activityInfo); - if (gi != null) { - result.add(gi); - } - } - - return result; - } - - private GadgetInfo parseGadgetInfoXml(ComponentName component, - PackageItemInfo packageItemInfo) { - GadgetInfo gi = null; - - XmlResourceParser parser = null; - try { - parser = packageItemInfo.loadXmlMetaData(mPackageManager, - GadgetManager.GADGET_PROVIDER_META_DATA); - if (parser == null) { - Log.w(TAG, "No " + GadgetManager.GADGET_PROVIDER_META_DATA + " meta-data for " - + "gadget provider '" + component + '\''); - return null; - } - - AttributeSet attrs = Xml.asAttributeSet(parser); - - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && type != XmlPullParser.START_TAG) { - // drain whitespace, comments, etc. - } - - String nodeName = parser.getName(); - if (!"gadget-provider".equals(nodeName)) { - Log.w(TAG, "Meta-data does not start with gadget-provider tag for" - + " gadget provider '" + component + '\''); - return null; - } - - gi = new GadgetInfo(); - - gi.provider = component; - - TypedArray sa = mContext.getResources().obtainAttributes(attrs, - com.android.internal.R.styleable.GadgetProviderInfo); - gi.minWidth = sa.getDimensionPixelSize( - com.android.internal.R.styleable.GadgetProviderInfo_minWidth, 0); - gi.minHeight = sa.getDimensionPixelSize( - com.android.internal.R.styleable.GadgetProviderInfo_minHeight, 0); - gi.updatePeriodMillis = sa.getInt( - com.android.internal.R.styleable.GadgetProviderInfo_updatePeriodMillis, 0); - gi.initialLayout = sa.getResourceId( - com.android.internal.R.styleable.GadgetProviderInfo_initialLayout, 0); - sa.recycle(); - } catch (Exception e) { - // Ok to catch Exception here, because anything going wrong because - // of what a client process passes to us should not be fatal for the - // system process. - Log.w(TAG, "XML parsing failed for gadget provider '" + component + '\'', e); - } finally { - if (parser != null) parser.close(); - } - return gi; - } - - void sendEnabled(ComponentName provider) { - Intent intent = new Intent(GadgetManager.GADGET_ENABLE_ACTION); - intent.setComponent(provider); - mContext.sendBroadcast(intent); - } - - String getCallingPackage() { - return mPackageManager.getNameForUid(getCallingUid()); - } -} - diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java index 22ad7bd..2131ffd 100755 --- a/services/java/com/android/server/HardwareService.java +++ b/services/java/com/android/server/HardwareService.java @@ -25,6 +25,7 @@ import android.os.Hardware; import android.os.IHardwareService; import android.os.Power; import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.os.IBinder; import android.os.Binder; @@ -35,6 +36,10 @@ public class HardwareService extends IHardwareService.Stub { private static final String TAG = "HardwareService"; HardwareService(Context context) { + // Reset the hardware to a default state, in case this is a runtime + // restart instead of a fresh boot. + vibratorOff(); + mContext = context; PowerManager pm = (PowerManager)context.getSystemService( Context.POWER_SERVICE); @@ -201,7 +206,7 @@ public class HardwareService extends IHardwareService.Stub { mThread.notify(); } mThread = null; - off(); + vibratorOff(); } } } @@ -217,49 +222,66 @@ public class HardwareService extends IHardwareService.Stub { mWakeLock.acquire(); } + private void delay(long duration) { + if (duration > 0) { + long bedtime = SystemClock.uptimeMillis(); + do { + try { + this.wait(duration); + } + catch (InterruptedException e) { + } + if (mDone) { + break; + } + duration = duration + - SystemClock.uptimeMillis() - bedtime; + } while (duration > 0); + } + } + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); synchronized (this) { int index = 0; - boolean nextState = false; long[] pattern = mPattern; - if (pattern[0] == 0) { - index++; - nextState = true; - } int len = pattern.length; - long start = SystemClock.uptimeMillis(); + long duration = 0; while (!mDone) { - if (nextState) { - HardwareService.this.on(); - } else { - HardwareService.this.off(); + // add off-time duration to any accumulated on-time duration + if (index < len) { + duration += pattern[index++]; } - nextState = !nextState; - long bedtime = SystemClock.uptimeMillis(); - long duration = pattern[index]; - do { - try { - this.wait(duration); - } - catch (InterruptedException e) { - } - if (mDone) { - break; + + // sleep until it is time to start the vibrator + delay(duration); + if (mDone) { + break; + } + + if (index < len) { + // read on-time duration and start the vibrator + // duration is saved for delay() at top of loop + duration = pattern[index++]; + if (duration > 0) { + HardwareService.this.vibratorOn(duration); } - duration = duration - - SystemClock.uptimeMillis() - bedtime; - } while (duration > 0); - index++; - if (index >= len) { + } else { if (mRepeat < 0) { - HardwareService.this.off(); break; } else { index = mRepeat; + 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) { @@ -301,6 +323,6 @@ public class HardwareService extends IHardwareService.Stub { volatile Death mDeath; volatile IBinder mToken; - native static void on(); - native static void off(); + native static void vibratorOn(long milliseconds); + native static void vibratorOff(); } diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java index 2bea731..855734d 100644 --- a/services/java/com/android/server/HeadsetObserver.java +++ b/services/java/com/android/server/HeadsetObserver.java @@ -19,6 +19,8 @@ package com.android.server; import android.app.ActivityManagerNative; import android.content.Context; import android.content.Intent; +import android.os.Handler; +import android.os.Message; import android.os.UEventObserver; import android.util.Log; import android.media.AudioManager; @@ -40,6 +42,8 @@ class HeadsetObserver extends UEventObserver { private int mHeadsetState; private String mHeadsetName; + private boolean mAudioRouteNeedsUpdate; + private AudioManager mAudioManager; public HeadsetObserver(Context context) { mContext = context; @@ -60,7 +64,7 @@ class HeadsetObserver extends UEventObserver { } } - private final void init() { + private synchronized final void init() { char[] buffer = new char[1024]; String newName = mHeadsetName; @@ -80,21 +84,33 @@ 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); mHeadsetName = newName; mHeadsetState = newState; - AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - - audioManager.setWiredHeadsetOn(mHeadsetState == 1); - sendIntent(); + mAudioRouteNeedsUpdate = true; + + sendIntent(isUnplug); + + if (isUnplug) { + // It often takes >200ms to flush the audio pipeline after apps + // pause audio playback, but audio route changes are immediate, + // so delay the route change by 400ms. + // This could be improved once the audio sub-system provides an + // interface to clear the audio pipeline. + mHandler.sendEmptyMessageDelayed(0, 400); + } else { + updateAudioRoute(); + } } } - private synchronized final void sendIntent() { + private synchronized final void sendIntent(boolean isUnplug) { // Pack up the values and broadcast them to everyone Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -104,5 +120,25 @@ 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(); + } + }; + } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 139aaa3..9948322 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -30,6 +30,7 @@ import com.android.server.status.StatusBarService; import org.xmlpull.v1.XmlPullParserException; +import android.app.ActivityManagerNative; import android.app.AlertDialog; import android.content.ComponentName; import android.content.ContentResolver; @@ -53,15 +54,17 @@ import android.os.IInterface; import android.os.Message; import android.os.Parcel; import android.os.RemoteException; +import android.os.ResultReceiver; import android.os.ServiceManager; +import android.os.SystemClock; import android.provider.Settings; import android.text.TextUtils; +import android.util.EventLog; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; import android.view.IWindowManager; import android.view.WindowManager; -import android.view.inputmethod.DefaultInputMethod; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodInfo; @@ -98,6 +101,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_UNBIND_METHOD = 3000; static final int MSG_BIND_METHOD = 3010; + static final long TIME_TO_RECONNECT = 10*1000; + + static final int LOG_IMF_FORCE_RECONNECT_IME = 32000; + final Context mContext; final Handler mHandler; final SettingsObserver mSettingsObserver; @@ -189,6 +196,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub ClientState mCurClient; /** + * The last window token that gained focus. + */ + IBinder mCurFocusedWindow; + + /** + * The input context last provided by the current client. + */ + IInputContext mCurInputContext; + + /** * The attributes last provided by the current client. */ EditorInfo mCurAttribute; @@ -216,6 +233,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean mShowExplicitlyRequested; /** + * Set if we were forced to be shown. + */ + boolean mShowForced; + + /** * Set if we last told the input method to show itself. */ boolean mInputShown; @@ -238,6 +260,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub IInputMethod mCurMethod; /** + * Time that we last initiated a bind to the input method, to determine + * if we should try to disconnect and reconnect to it. + */ + long mLastBindTime; + + /** * Have we called mCurMethod.bindInput()? */ boolean mBoundToMethod; @@ -281,7 +309,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; } else { - Log.e(TAG, "Unexpected intent " + intent); + Log.w(TAG, "Unexpected intent " + intent); } // Inform the current client of the change in active status @@ -290,7 +318,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurClient.client.setActive(mScreenOn); } } catch (RemoteException e) { - Log.e(TAG, "Got RemoteException sending 'screen on/off' notification", e); + Log.w(TAG, "Got RemoteException sending 'screen on/off' notification to pid " + + mCurClient.pid + " uid " + mCurClient.uid); } } } @@ -475,7 +504,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public void systemReady() { - } public List<InputMethodInfo> getInputMethodList() { @@ -534,7 +562,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - void unbindCurrentInputLocked() { + void unbindCurrentClientLocked() { if (mCurClient != null) { if (DEBUG) Log.v(TAG, "unbindCurrentInputLocked: client = " + mCurClient.client.asBinder()); @@ -553,12 +581,34 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { mCurClient.client.setActive(false); } catch (RemoteException e) { - Log.e(TAG, "Got RemoteException sending setActive(false) notification", e); + Log.w(TAG, "Got RemoteException sending setActive(false) notification to pid " + + mCurClient.pid + " uid " + mCurClient.uid); } mCurClient = null; } } + private int getImeShowFlags() { + int flags = 0; + if (mShowForced) { + flags |= InputMethod.SHOW_FORCED + | InputMethod.SHOW_EXPLICIT; + } else if (mShowExplicitlyRequested) { + flags |= InputMethod.SHOW_EXPLICIT; + } + return flags; + } + + private int getAppShowFlags() { + int flags = 0; + if (mShowForced) { + flags |= InputMethodManager.SHOW_FORCED; + } else if (!mShowExplicitlyRequested) { + flags |= InputMethodManager.SHOW_IMPLICIT; + } + return flags; + } + InputBindResult attachNewInputLocked(boolean initial, boolean needResult) { if (!mBoundToMethod) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( @@ -567,16 +617,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } final SessionState session = mCurClient.curSession; if (initial) { - executeOrSendMessage(session.method, mCaller.obtainMessageOO( - MSG_START_INPUT, session, mCurAttribute)); + executeOrSendMessage(session.method, mCaller.obtainMessageOOO( + MSG_START_INPUT, session, mCurInputContext, mCurAttribute)); } else { - executeOrSendMessage(session.method, mCaller.obtainMessageOO( - MSG_RESTART_INPUT, session, mCurAttribute)); + executeOrSendMessage(session.method, mCaller.obtainMessageOOO( + MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute)); } if (mShowRequested) { if (DEBUG) Log.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(mShowExplicitlyRequested - ? 0 : InputMethodManager.SHOW_IMPLICIT); + showCurrentInputLocked(getAppShowFlags(), null); } return needResult ? new InputBindResult(session.session, mCurId, mCurSeq) @@ -584,7 +633,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } InputBindResult startInputLocked(IInputMethodClient client, - EditorInfo attribute, boolean initial, boolean needResult) { + IInputContext inputContext, EditorInfo attribute, + boolean initial, boolean needResult) { // If no method is currently selected, do nothing. if (mCurMethodId == null) { return mNoBinding; @@ -613,7 +663,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurClient != cs) { // If the client is changing, we need to switch over to the new // one. - unbindCurrentInputLocked(); + unbindCurrentClientLocked(); if (DEBUG) Log.v(TAG, "switching to client: client = " + cs.client.asBinder()); @@ -622,7 +672,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { cs.client.setActive(mScreenOn); } catch (RemoteException e) { - Log.e(TAG, "Got RemoteException sending setActive notification", e); + Log.w(TAG, "Got RemoteException sending setActive notification to pid " + + cs.pid + " uid " + cs.uid); } } } @@ -631,6 +682,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurSeq++; if (mCurSeq <= 0) mCurSeq = 1; mCurClient = cs; + mCurInputContext = inputContext; mCurAttribute = attribute; // Check if the input method is changing. @@ -641,14 +693,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return attachNewInputLocked(initial, needResult); } if (mHaveConnection) { - if (mCurMethod != null && !cs.sessionRequested) { - cs.sessionRequested = true; - if (DEBUG) Log.v(TAG, "Creating new session for client " + cs); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_CREATE_SESSION, mCurMethod, - new MethodCallback(mCurMethod))); + if (mCurMethod != null) { + if (!cs.sessionRequested) { + cs.sessionRequested = true; + if (DEBUG) Log.v(TAG, "Creating new session for client " + cs); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( + MSG_CREATE_SESSION, mCurMethod, + new MethodCallback(mCurMethod))); + } + // Return to client, and we will get back with it when + // we have had a session made for it. + return new InputBindResult(null, mCurId, mCurSeq); + } else if (SystemClock.uptimeMillis() + < (mLastBindTime+TIME_TO_RECONNECT)) { + // In this case we have connected to the service, but + // don't yet have its interface. If it hasn't been too + // long since we did the connection, we'll return to + // the client and wait to get the service interface so + // we can report back. If it has been too long, we want + // to fall through so we can try a disconnect/reconnect + // to see if we can get back in touch with the service. + return new InputBindResult(null, mCurId, mCurSeq); + } else { + EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId, + SystemClock.uptimeMillis()-mLastBindTime, 0); } - return new InputBindResult(null, mCurId, mCurSeq); } } @@ -657,28 +726,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("Unknown id: " + mCurMethodId); } - if (mCurToken != null) { - try { - mIWindowManager.removeWindowToken(mCurToken); - } catch (RemoteException e) { - } - mCurToken = null; - } - - if (mHaveConnection) { - mContext.unbindService(this); - mHaveConnection = false; - } - - clearCurMethod(); + unbindCurrentMethodLocked(false); mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE); mCurIntent.setComponent(info.getComponent()); if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) { + mLastBindTime = SystemClock.uptimeMillis(); mHaveConnection = true; mCurId = info.getId(); mCurToken = new Binder(); try { + if (DEBUG) Log.v(TAG, "Adding window token: " + mCurToken); mIWindowManager.addWindowToken(mCurToken, WindowManager.LayoutParams.TYPE_INPUT_METHOD); } catch (RemoteException e) { @@ -686,18 +744,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return new InputBindResult(null, mCurId, mCurSeq); } else { mCurIntent = null; - Log.e(TAG, "Failure connecting to input method service: " + Log.w(TAG, "Failure connecting to input method service: " + mCurIntent); } return null; } public InputBindResult startInput(IInputMethodClient client, - EditorInfo attribute, boolean initial, boolean needResult) { + IInputContext inputContext, EditorInfo attribute, + boolean initial, boolean needResult) { synchronized (mMethodMap) { final long ident = Binder.clearCallingIdentity(); try { - return startInputLocked(client, attribute, initial, needResult); + return startInputLocked(client, inputContext, attribute, + initial, needResult); } finally { Binder.restoreCallingIdentity(ident); } @@ -712,6 +772,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurIntent != null && name.equals(mCurIntent.getComponent())) { mCurMethod = IInputMethod.Stub.asInterface(service); if (mCurClient != null) { + if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_ATTACH_TOKEN, mCurMethod, mCurToken)); if (mCurClient != null) { @@ -728,7 +789,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub void onSessionCreated(IInputMethod method, IInputMethodSession session) { synchronized (mMethodMap) { - if (mCurMethod == method) { + if (mCurMethod != null && method != null + && mCurMethod.asBinder() == method.asBinder()) { if (mCurClient != null) { mCurClient.curSession = new SessionState(mCurClient, method, session); @@ -743,7 +805,30 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - void clearCurMethod() { + void unbindCurrentMethodLocked(boolean reportToClient) { + if (mHaveConnection) { + mContext.unbindService(this); + mHaveConnection = false; + } + + if (mCurToken != null) { + try { + if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken); + mIWindowManager.removeWindowToken(mCurToken); + } catch (RemoteException e) { + } + mCurToken = null; + } + + clearCurMethodLocked(); + + if (reportToClient && mCurClient != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( + MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); + } + } + + void clearCurMethodLocked() { if (mCurMethod != null) { for (ClientState cs : mClients.values()) { cs.sessionRequested = false; @@ -751,6 +836,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mCurMethod = null; } + mStatusBar.setIconVisibility(mInputMethodIcon, false); } public void onServiceDisconnected(ComponentName name) { @@ -759,7 +845,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " mCurIntent=" + mCurIntent); if (mCurMethod != null && mCurIntent != null && name.equals(mCurIntent.getComponent())) { - clearCurMethod(); + clearCurMethodLocked(); + // We consider this to be a new bind attempt, since the system + // should now try to restart the service for us. + mLastBindTime = SystemClock.uptimeMillis(); mShowRequested = mInputShown; mInputShown = false; if (mCurClient != null) { @@ -770,35 +859,48 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - public void updateStatusIcon(int iconId, String iconPackage) { - if (iconId == 0) { - Log.d(TAG, "hide the small icon for the input method"); - mStatusBar.setIconVisibility(mInputMethodIcon, false); - } else { - Log.d(TAG, "show a small icon for the input method"); - - if (iconPackage != null - && iconPackage - .equals(InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) { - iconPackage = null; + public void updateStatusIcon(IBinder token, String packageName, int iconId) { + long ident = Binder.clearCallingIdentity(); + try { + if (token == null || mCurToken != token) { + Log.w(TAG, "Ignoring setInputMethod of token: " + token); + return; } - - mInputMethodData.iconId = iconId; - mInputMethodData.iconPackage = iconPackage; - mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null); - mStatusBar.setIconVisibility(mInputMethodIcon, true); + + synchronized (mMethodMap) { + if (iconId == 0) { + if (DEBUG) Log.d(TAG, "hide the small icon for the input method"); + mStatusBar.setIconVisibility(mInputMethodIcon, false); + } else if (packageName != null) { + if (DEBUG) Log.d(TAG, "show a small icon for the input method"); + mInputMethodData.iconId = iconId; + mInputMethodData.iconPackage = packageName; + mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null); + mStatusBar.setIconVisibility(mInputMethodIcon, true); + } + } + } finally { + Binder.restoreCallingIdentity(ident); } } void updateFromSettingsLocked() { + // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and + // ENABLED_INPUT_METHODS is taking care of keeping them correctly in + // sync, so we will never have a DEFAULT_INPUT_METHOD that is not + // enabled. String id = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); - if (id != null) { + if (id != null && id.length() > 0) { try { setInputMethodLocked(id); } catch (IllegalArgumentException e) { Log.w(TAG, "Unknown input method from prefs: " + id, e); + unbindCurrentMethodLocked(true); } + } else { + // There is no longer an input method set, so stop any current one. + unbindCurrentMethodLocked(true); } } @@ -817,154 +919,217 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurMethodId = id; Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD, id); - - Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); - intent.putExtra("input_method_id", id); - mContext.sendBroadcast(intent); - unbindCurrentInputLocked(); + + if (ActivityManagerNative.isSystemReady()) { + Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); + intent.putExtra("input_method_id", id); + mContext.sendBroadcast(intent); + } + unbindCurrentClientLocked(); } finally { Binder.restoreCallingIdentity(ident); } } - public void showSoftInput(IInputMethodClient client, int flags) { - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring showSoftInput of: " + client); - return; + public boolean showSoftInput(IInputMethodClient client, int flags, + ResultReceiver resultReceiver) { + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring showSoftInput of: " + client); + return false; + } + } catch (RemoteException e) { + return false; } - } catch (RemoteException e) { } + + if (DEBUG) Log.v(TAG, "Client requesting input be shown"); + return showCurrentInputLocked(flags, resultReceiver); } - - if (DEBUG) Log.v(TAG, "Client requesting input be shown"); - showCurrentInputLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); } } - void showCurrentInputLocked(int flags) { + boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) { mShowRequested = true; if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) { mShowExplicitlyRequested = true; } + if ((flags&InputMethodManager.SHOW_FORCED) != 0) { + mShowExplicitlyRequested = true; + mShowForced = true; + } + boolean res = false; if (mCurMethod != null) { - executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO( - MSG_SHOW_SOFT_INPUT, mShowExplicitlyRequested ? 1 : 0, - mCurMethod)); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO( + MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod, + resultReceiver)); mInputShown = true; + res = true; + } else if (mHaveConnection && SystemClock.uptimeMillis() + < (mLastBindTime+TIME_TO_RECONNECT)) { + // The client has asked to have the input method shown, but + // we have been sitting here too long with a connection to the + // service and no interface received, so let's disconnect/connect + // to try to prod things along. + EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId, + SystemClock.uptimeMillis()-mLastBindTime,1); + mContext.unbindService(this); + mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE); } + + return res; } - public void hideSoftInput(IInputMethodClient client, int flags) { - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring hideSoftInput of: " + client); - return; + public boolean hideSoftInput(IInputMethodClient client, int flags, + ResultReceiver resultReceiver) { + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring hideSoftInput of: " + client); + return false; + } + } catch (RemoteException e) { + return false; } - } catch (RemoteException e) { } + + if (DEBUG) Log.v(TAG, "Client requesting input be hidden"); + return hideCurrentInputLocked(flags, resultReceiver); } - - if (DEBUG) Log.v(TAG, "Client requesting input be hidden"); - hideCurrentInputLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); } } - void hideCurrentInputLocked(int flags) { + boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) { if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 - && mShowExplicitlyRequested) { + && (mShowExplicitlyRequested || mShowForced)) { if (DEBUG) Log.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); - return; + return false; + } + if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) { + if (DEBUG) Log.v(TAG, + "Not hiding: forced show not cancelled by not-always hide"); + return false; } + boolean res; if (mInputShown && mCurMethod != null) { - executeOrSendMessage(mCurMethod, mCaller.obtainMessageO( - MSG_HIDE_SOFT_INPUT, mCurMethod)); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( + MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver)); + res = true; + } else { + res = false; } mInputShown = false; mShowRequested = false; mShowExplicitlyRequested = false; + mShowForced = false; + return res; } - public void windowGainedFocus(IInputMethodClient client, + public void windowGainedFocus(IInputMethodClient client, IBinder windowToken, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags) { - synchronized (mMethodMap) { - if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder() - + " viewHasFocus=" + viewHasFocus - + " isTextEditor=" + isTextEditor - + " softInputMode=#" + Integer.toHexString(softInputMode) - + " first=" + first + " flags=#" - + Integer.toHexString(windowFlags)); - - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring focus gain of: " + client); - return; + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder() + + " viewHasFocus=" + viewHasFocus + + " isTextEditor=" + isTextEditor + + " softInputMode=#" + Integer.toHexString(softInputMode) + + " first=" + first + " flags=#" + + Integer.toHexString(windowFlags)); + + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Client not active, ignoring focus gain of: " + client); + return; + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } - } - - switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: - if (!isTextEditor || (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { - if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) { - // There is no focus view, and this window will - // be behind any soft input window, so hide the - // soft input window if it is shown. - if (DEBUG) Log.v(TAG, "Unspecified window will hide input"); - hideCurrentInputLocked(0); + + if (mCurFocusedWindow == windowToken) { + Log.w(TAG, "Window already focused, ignoring focus gain of: " + client); + return; + } + mCurFocusedWindow = windowToken; + + switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { + case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: + if (!isTextEditor || (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { + if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) { + // There is no focus view, and this window will + // be behind any soft input window, so hide the + // soft input window if it is shown. + if (DEBUG) Log.v(TAG, "Unspecified window will hide input"); + hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null); + } + } else if (isTextEditor && (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + && (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + // There is a focus view, and we are navigating forward + // into the window, so show the input window for the user. + if (DEBUG) Log.v(TAG, "Unspecified window will show input"); + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } - } else if (isTextEditor && (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - && (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - // There is a focus view, and we are navigating forward - // into the window, so show the input window for the user. - if (DEBUG) Log.v(TAG, "Unspecified window will show input"); - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: - // Do nothing. - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: - if (DEBUG) Log.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(0); - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: - if ((softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - if (DEBUG) Log.v(TAG, "Window asks to show input going forward"); - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: - if (DEBUG) Log.v(TAG, "Window asks to always show input"); - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - break; + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: + // Do nothing. + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: + if ((softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + if (DEBUG) Log.v(TAG, "Window asks to hide input going forward"); + hideCurrentInputLocked(0, null); + } + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: + if (DEBUG) Log.v(TAG, "Window asks to hide input"); + hideCurrentInputLocked(0, null); + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: + if ((softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + if (DEBUG) Log.v(TAG, "Window asks to show input going forward"); + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + } + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: + if (DEBUG) Log.v(TAG, "Window asks to always show input"); + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + break; + } } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -981,7 +1146,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void setInputMethod(IBinder token, String id) { synchronized (mMethodMap) { - if (mCurToken == null) { + if (token == null) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) { @@ -991,19 +1156,45 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } else if (mCurToken != token) { Log.w(TAG, "Ignoring setInputMethod of token: " + token); + return; } - setInputMethodLocked(id); + long ident = Binder.clearCallingIdentity(); + try { + setInputMethodLocked(id); + } finally { + Binder.restoreCallingIdentity(ident); + } } } public void hideMySoftInput(IBinder token, int flags) { synchronized (mMethodMap) { - if (mCurToken == null || mCurToken != token) { + if (token == null || mCurToken != token) { Log.w(TAG, "Ignoring hideInputMethod of token: " + token); + return; + } + long ident = Binder.clearCallingIdentity(); + try { + hideCurrentInputLocked(flags, null); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void showMySoftInput(IBinder token, int flags) { + synchronized (mMethodMap) { + if (token == null || mCurToken != token) { + Log.w(TAG, "Ignoring hideInputMethod of token: " + token); + return; + } + long ident = Binder.clearCallingIdentity(); + try { + showCurrentInputLocked(flags, null); + } finally { + Binder.restoreCallingIdentity(ident); } - - hideCurrentInputLocked(flags); } } @@ -1051,20 +1242,25 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return true; case MSG_SHOW_SOFT_INPUT: + args = (HandlerCaller.SomeArgs)msg.obj; try { - ((IInputMethod)msg.obj).showSoftInput(msg.arg1 != 0); + ((IInputMethod)args.arg1).showSoftInput(msg.arg1, + (ResultReceiver)args.arg2); } catch (RemoteException e) { } return true; case MSG_HIDE_SOFT_INPUT: + args = (HandlerCaller.SomeArgs)msg.obj; try { - ((IInputMethod)msg.obj).hideSoftInput(); + ((IInputMethod)args.arg1).hideSoftInput(0, + (ResultReceiver)args.arg2); } catch (RemoteException e) { } return true; case MSG_ATTACH_TOKEN: args = (HandlerCaller.SomeArgs)msg.obj; try { + if (DEBUG) Log.v(TAG, "Sending attach of token: " + args.arg2); ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2); } catch (RemoteException e) { } @@ -1077,7 +1273,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { } return true; - // --------------------------------------------------------- case MSG_START_INPUT: @@ -1085,7 +1280,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { SessionState session = (SessionState)args.arg1; setEnabledSessionInMainThread(session); - session.method.startInput((EditorInfo)args.arg2); + session.method.startInput((IInputContext)args.arg2, + (EditorInfo)args.arg3); } catch (RemoteException e) { } return true; @@ -1094,7 +1290,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { SessionState session = (SessionState)args.arg1; setEnabledSessionInMainThread(session); - session.method.restartInput((EditorInfo)args.arg2); + session.method.restartInput((IInputContext)args.arg2, + (EditorInfo)args.arg3); } catch (RemoteException e) { } return true; @@ -1128,10 +1325,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub PackageManager pm = mContext.getPackageManager(); - Object[][] buildin = {{ - DefaultInputMethod.class.getName(), - DefaultInputMethod.getMetaInfo()}}; - List<ResolveInfo> services = pm.queryIntentServices( new Intent(InputMethod.SERVICE_INTERFACE), PackageManager.GET_META_DATA); @@ -1150,39 +1343,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Log.d(TAG, "Checking " + compName); - /* Built-in input methods are not currently supported... this will - * need to be reworked to bring them back (all input methods must - * now be published in a manifest). - */ - /* - if (compName.getPackageName().equals( - InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) { - // System build-in input methods; - String inputMethodName = null; - int kbType = 0; - String skbName = null; - - for (int j = 0; j < buildin.length; ++j) { - Object[] obj = buildin[j]; - if (compName.getClassName().equals(obj[0])) { - InputMethodMetaInfo imp = (InputMethodMetaInfo) obj[1]; - inputMethodName = imp.inputMethodName; - } - } - - InputMethodMetaInfo p = new InputMethodMetaInfo(compName, - inputMethodName, ""); - - list.add(p); - - if (DEBUG) { - Log.d(TAG, "Found a build-in input method " + p); - } - - continue; - } - */ - try { InputMethodInfo p = new InputMethodInfo(mContext, ri); list.add(p); @@ -1202,7 +1362,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // ---------------------------------------------------------------------- - public void showInputMethodMenu() { + void showInputMethodMenu() { if (DEBUG) Log.v(TAG, "Show switching menu"); hideInputMethodMenu(); @@ -1302,91 +1462,96 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - // Make sure this is a valid input method. - InputMethodInfo imm = mMethodMap.get(id); - if (imm == null) { + long ident = Binder.clearCallingIdentity(); + try { + // Make sure this is a valid input method. + InputMethodInfo imm = mMethodMap.get(id); if (imm == null) { - throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + if (imm == null) { + throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + } } - } - - StringBuilder builder = new StringBuilder(256); - - boolean removed = false; - String firstId = null; - - // Look through the currently enabled input methods. - String enabledStr = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS); - if (enabledStr != null) { - final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; - splitter.setString(enabledStr); - while (splitter.hasNext()) { - String curId = splitter.next(); - if (curId.equals(id)) { - if (enabled) { - // We are enabling this input method, but it is - // already enabled. Nothing to do. The previous - // state was enabled. - return true; + + StringBuilder builder = new StringBuilder(256); + + boolean removed = false; + String firstId = null; + + // Look through the currently enabled input methods. + String enabledStr = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS); + if (enabledStr != null) { + final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; + splitter.setString(enabledStr); + while (splitter.hasNext()) { + String curId = splitter.next(); + if (curId.equals(id)) { + if (enabled) { + // We are enabling this input method, but it is + // already enabled. Nothing to do. The previous + // state was enabled. + return true; + } + // We are disabling this input method, and it is + // currently enabled. Skip it to remove from the + // new list. + removed = true; + } else if (!enabled) { + // We are building a new list of input methods that + // doesn't contain the given one. + if (firstId == null) firstId = curId; + if (builder.length() > 0) builder.append(':'); + builder.append(curId); } - // We are disabling this input method, and it is - // currently enabled. Skip it to remove from the - // new list. - removed = true; - } else if (!enabled) { - // We are building a new list of input methods that - // doesn't contain the given one. - if (firstId == null) firstId = curId; - if (builder.length() > 0) builder.append(':'); - builder.append(curId); } } - } - - if (!enabled) { - if (!removed) { - // We are disabling the input method but it is already - // disabled. Nothing to do. The previous state was - // disabled. - return false; - } - // Update the setting with the new list of input methods. - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, builder.toString()); - // We the disabled input method is currently selected, switch - // to another one. - String selId = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD); - if (id.equals(selId)) { + + if (!enabled) { + if (!removed) { + // We are disabling the input method but it is already + // disabled. Nothing to do. The previous state was + // disabled. + return false; + } + // Update the setting with the new list of input methods. Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD, - firstId != null ? firstId : ""); + Settings.Secure.ENABLED_INPUT_METHODS, builder.toString()); + // We the disabled input method is currently selected, switch + // to another one. + String selId = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD); + if (id.equals(selId)) { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + firstId != null ? firstId : ""); + } + // Previous state was enabled. + return true; } - // Previous state was enabled. - return true; - } - - // Add in the newly enabled input method. - if (enabledStr == null || enabledStr.length() == 0) { - enabledStr = id; - } else { - enabledStr = enabledStr + ':' + id; + + // Add in the newly enabled input method. + if (enabledStr == null || enabledStr.length() == 0) { + enabledStr = id; + } else { + enabledStr = enabledStr + ':' + id; + } + + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS, enabledStr); + + // Previous state was disabled. + return false; + } finally { + Binder.restoreCallingIdentity(ident); } - - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, enabledStr); - - // Previous state was disabled. - return false; } } - + // ---------------------------------------------------------------------- @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump InputMethodManager from from pid=" @@ -1395,8 +1560,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return; } + IInputMethod method; + ClientState client; + + final Printer p = new PrintWriterPrinter(pw); + synchronized (mMethodMap) { - final Printer p = new PrintWriterPrinter(pw); p.println("Current Input Method Manager state:"); int N = mMethodList.size(); p.println(" Input Methods:"); @@ -1416,17 +1585,41 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println(" mInputMethodIcon=" + mInputMethodIcon); p.println(" mInputMethodData=" + mInputMethodData); p.println(" mCurrentMethod=" + mCurMethodId); - p.println(" mCurSeq=" + mCurSeq + " mCurClient=" + mCurClient); + client = mCurClient; + p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq); + p.println(" mCurFocusedWindow=" + mCurFocusedWindow); p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection + " mBoundToMethod=" + mBoundToMethod); p.println(" mCurToken=" + mCurToken); p.println(" mCurIntent=" + mCurIntent); + method = mCurMethod; p.println(" mCurMethod=" + mCurMethod); p.println(" mEnabledSession=" + mEnabledSession); p.println(" mShowRequested=" + mShowRequested + " mShowExplicitlyRequested=" + mShowExplicitlyRequested + + " mShowForced=" + mShowForced + " mInputShown=" + mInputShown); p.println(" mScreenOn=" + mScreenOn); } + + if (client != null) { + p.println(" "); + pw.flush(); + try { + client.client.asBinder().dump(fd, args); + } catch (RemoteException e) { + p.println("Input method client dead: " + e); + } + } + + if (method != null) { + p.println(" "); + pw.flush(); + try { + method.asBinder().dump(fd, args); + } catch (RemoteException e) { + p.println("Input method service dead: " + e); + } + } } } diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java index 9874042..63b486c 100644 --- a/services/java/com/android/server/KeyInputQueue.java +++ b/services/java/com/android/server/KeyInputQueue.java @@ -165,6 +165,7 @@ public abstract class KeyInputQueue { 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 boolean hasKeys(int[] keycodes, boolean[] keyExists); public static KeyEvent newKeyEvent(InputDevice device, long downTime, long eventTime, boolean down, int keycode, int repeatCount, diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index ed9ee79..e48e047 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -18,14 +18,16 @@ 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; import java.util.HashSet; import java.util.List; -import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.regex.Pattern; @@ -46,6 +48,7 @@ import android.location.LocationManager; import android.location.LocationProvider; import android.location.LocationProviderImpl; import android.net.ConnectivityManager; +import android.net.Uri; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.os.Binder; @@ -57,19 +60,22 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; import android.telephony.CellLocation; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Config; import android.util.Log; +import android.util.PrintWriterPrinter; +import android.util.SparseIntArray; +import com.android.internal.app.IBatteryStats; import com.android.internal.location.CellState; import com.android.internal.location.GpsLocationProvider; -import com.android.internal.location.LocationCollector; -import com.android.internal.location.LocationMasfClient; -import com.android.internal.location.NetworkLocationProvider; +import com.android.internal.location.ILocationCollector; +import com.android.internal.location.INetworkLocationManager; +import com.android.internal.location.INetworkLocationProvider; import com.android.internal.location.TrackProvider; +import com.android.server.am.BatteryStatsService; /** * The service class that manages LocationProviders and issues location @@ -77,7 +83,8 @@ import com.android.internal.location.TrackProvider; * * {@hide} */ -public class LocationManagerService extends ILocationManager.Stub { +public class LocationManagerService extends ILocationManager.Stub + implements INetworkLocationManager { private static final String TAG = "LocationManagerService"; // Minimum time interval between last known location writes, in milliseconds. @@ -121,13 +128,16 @@ public class LocationManagerService extends ILocationManager.Stub { private final Context mContext; private GpsLocationProvider mGpsLocationProvider; - private NetworkLocationProvider mNetworkLocationProvider; + private boolean mGpsNavigating; + private LocationProviderImpl mNetworkLocationProvider; + private INetworkLocationProvider mNetworkLocationInterface; private LocationWorkerHandler mLocationHandler; // Handler messages private static final int MESSAGE_HEARTBEAT = 1; private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2; private static final int MESSAGE_RELEASE_WAKE_LOCK = 3; + private static final int MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER = 4; // Alarm manager and wakelock variables private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT"; @@ -143,33 +153,41 @@ public class LocationManagerService extends ILocationManager.Stub { private boolean mWakeLockNetworkReceived = true; private boolean mWifiWakeLockAcquired = false; private boolean mCellWakeLockAcquired = false; - + + private final IBatteryStats mBatteryStats; + /** * Mapping from listener IBinder/PendingIntent to local Listener wrappers. */ - private final HashMap<Object,Receiver> mListeners = - new HashMap<Object,Receiver>(); + private final ArrayList<Receiver> mListeners = new ArrayList<Receiver>(); /** + * Used for reporting which UIDs are causing the GPS to run. + */ + private final SparseIntArray mReportedGpsUids = new SparseIntArray(); + private int mReportedGpsSeq = 0; + + /** * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord. + * This also serves as the lock for our state. */ - private final HashMap<Object,HashMap<String,UpdateRecord>> mLocationListeners = - new HashMap<Object,HashMap<String,UpdateRecord>>(); + private final HashMap<Receiver,HashMap<String,UpdateRecord>> mLocationListeners = + new HashMap<Receiver,HashMap<String,UpdateRecord>>(); /** * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast * location. */ - private final HashMap<Object,HashMap<String,Location>> mLastFixBroadcast = - new HashMap<Object,HashMap<String,Location>>(); - private final HashMap<Object,HashMap<String,Long>> mLastStatusBroadcast = - new HashMap<Object,HashMap<String,Long>>(); + private final HashMap<Receiver,HashMap<String,Location>> mLastFixBroadcast = + new HashMap<Receiver,HashMap<String,Location>>(); + private final HashMap<Receiver,HashMap<String,Long>> mLastStatusBroadcast = + new HashMap<Receiver,HashMap<String,Long>>(); /** * Mapping from provider name to all its UpdateRecords */ - private final HashMap<String,HashSet<UpdateRecord>> mRecordsByProvider = - new HashMap<String,HashSet<UpdateRecord>>(); + private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider = + new HashMap<String,ArrayList<UpdateRecord>>(); /** * Mappings from provider name to object to use for current location. Locations @@ -196,17 +214,16 @@ public class LocationManagerService extends ILocationManager.Stub { // Last known cell service state private TelephonyManager mTelephonyManager; - private int mSignalStrength = -1; // Location collector - private LocationCollector mCollector; - - // Location MASF service - private LocationMasfClient mMasfClient; + private ILocationCollector mCollector; // Wifi Manager private WifiManager mWifiManager; + private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; + private boolean mWifiEnabled = false; + /** * A wrapper class holding either an ILocationListener or a PendingIntent to receive * location updates. @@ -214,22 +231,48 @@ public class LocationManagerService extends ILocationManager.Stub { private final class Receiver implements IBinder.DeathRecipient { final ILocationListener mListener; final PendingIntent mPendingIntent; + final int mUid; + final Object mKey; - Receiver(ILocationListener listener) { + Receiver(ILocationListener listener, int uid) { mListener = listener; mPendingIntent = null; + mUid = uid; + mKey = listener.asBinder(); } - Receiver(PendingIntent intent) { + Receiver(PendingIntent intent, int uid) { mPendingIntent = intent; mListener = null; + mUid = uid; + mKey = intent; + } + + @Override + public boolean equals(Object otherObj) { + if (otherObj instanceof Receiver) { + return mKey.equals( + ((Receiver)otherObj).mKey); + } + return false; } - public Object getKey() { + @Override + public int hashCode() { + return mKey.hashCode(); + } + + + @Override + public String toString() { if (mListener != null) { - return mListener.asBinder(); + return "Receiver{" + + Integer.toHexString(System.identityHashCode(this)) + + " uid " + mUid + " Listener " + mKey + "}"; } else { - return mPendingIntent; + return "Receiver{" + + Integer.toHexString(System.identityHashCode(this)) + + " uid " + mUid + " Intent " + mKey + "}"; } } @@ -255,10 +298,13 @@ public class LocationManagerService extends ILocationManager.Stub { throw new IllegalStateException("Request for non-existent intent"); } - public void onStatusChanged(String provider, int status, Bundle extras) - throws RemoteException { + public boolean callStatusChangedLocked(String provider, int status, Bundle extras) { if (mListener != null) { - mListener.onStatusChanged(provider, status, extras); + try { + mListener.onStatusChanged(provider, status, extras); + } catch (RemoteException e) { + return false; + } } else { Intent statusChanged = new Intent(); statusChanged.putExtras(extras); @@ -266,23 +312,29 @@ public class LocationManagerService extends ILocationManager.Stub { try { mPendingIntent.send(mContext, 0, statusChanged, null, null); } catch (PendingIntent.CanceledException e) { - _removeUpdates(this); + return false; } } + return true; } - public void onLocationChanged(Location location) throws RemoteException { + public boolean callLocationChangedLocked(Location location) { if (mListener != null) { - mListener.onLocationChanged(location); + try { + mListener.onLocationChanged(location); + } catch (RemoteException e) { + return false; + } } else { Intent locationChanged = new Intent(); locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location); try { mPendingIntent.send(mContext, 0, locationChanged, null, null); } catch (PendingIntent.CanceledException e) { - _removeUpdates(this); + return false; } } + return true; } public void binderDied() { @@ -290,12 +342,12 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "Location listener died"); } synchronized (mLocationListeners) { - _removeUpdates(this); + removeUpdatesLocked(this); } } } - private Location readLastKnownLocation(String provider) { + private Location readLastKnownLocationLocked(String provider) { Location location = null; String s = null; try { @@ -338,7 +390,7 @@ public class LocationManagerService extends ILocationManager.Stub { return location; } - private void writeLastKnownLocation(String provider, + private void writeLastKnownLocationLocked(String provider, Location location) { long now = SystemClock.elapsedRealtime(); Long last = mLastWriteTime.get(provider); @@ -396,36 +448,30 @@ public class LocationManagerService extends ILocationManager.Stub { * properties */ private void loadProviders() { - synchronized (LocationManagerService.class) { + synchronized (mLocationListeners) { if (sProvidersLoaded) { return; } // Load providers - loadProvidersNoSync(); + loadProvidersLocked(); sProvidersLoaded = true; } } - private void loadProvidersNoSync() { + private void loadProvidersLocked() { try { - _loadProvidersNoSync(); + _loadProvidersLocked(); } catch (Exception e) { Log.e(TAG, "Exception loading providers:", e); } } - private void _loadProvidersNoSync() { + private void _loadProvidersLocked() { // Attempt to load "real" providers first - if (NetworkLocationProvider.isSupported()) { - // Create a network location provider - mNetworkLocationProvider = new NetworkLocationProvider(mContext, mMasfClient); - LocationProviderImpl.addProvider(mNetworkLocationProvider); - } - if (GpsLocationProvider.isSupported()) { // Create a gps location provider - mGpsLocationProvider = new GpsLocationProvider(mContext, mCollector); + mGpsLocationProvider = new GpsLocationProvider(mContext); LocationProviderImpl.addProvider(mGpsLocationProvider); } @@ -494,7 +540,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - updateProviders(); + updateProvidersLocked(); } /** @@ -509,18 +555,15 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "Constructed LocationManager Service"); } - // Initialize the LocationMasfClient - mMasfClient = new LocationMasfClient(mContext); - - // Create location collector - mCollector = new LocationCollector(mMasfClient); - // Alarm manager, needs to be done before calling loadProviders() below mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); // Create a wake lock, needs to be done before calling loadProviders() below PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); + + // Battery statistics service to be notified when GPS turns on or off + mBatteryStats = BatteryStatsService.getService(); // Load providers loadProviders(); @@ -548,26 +591,71 @@ public class LocationManagerService extends ILocationManager.Stub { intentFilter.addAction(Intent.ACTION_SCREEN_OFF); intentFilter.addAction(Intent.ACTION_SCREEN_ON); intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); + intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); context.registerReceiver(powerStateReceiver, intentFilter); // Get the wifi manager mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); // Create a wifi lock for future use - mWifiLock = getWifiWakelock(); - - // There might be an existing wifi scan available - if (mWifiManager != null) { - List<ScanResult> wifiScanResults = mWifiManager.getScanResults(); - if (wifiScanResults != null && wifiScanResults.size() != 0) { - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateWifiScanResults(wifiScanResults); + mWifiLock = getWifiWakelockLocked(); + } + + public void setInstallCallback(InstallCallback callback) { + synchronized (mLocationListeners) { + mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER); + Message m = Message.obtain(mLocationHandler, + MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback); + mLocationHandler.sendMessageAtFrontOfQueue(m); + } + } + + public void setNetworkLocationProvider(INetworkLocationProvider provider) { + synchronized (mLocationListeners) { + mNetworkLocationInterface = provider; + provider.addListener(getPackageNames()); + mNetworkLocationProvider = (LocationProviderImpl)provider; + LocationProviderImpl.addProvider(mNetworkLocationProvider); + updateProvidersLocked(); + + // notify NetworkLocationProvider of any events it might have missed + synchronized (mLocationListeners) { + mNetworkLocationProvider.updateNetworkState(mNetworkState); + mNetworkLocationInterface.updateWifiEnabledState(mWifiEnabled); + mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired); + + if (mLastCellState != null) { + if (mCollector != null) { + mCollector.updateCellState(mLastCellState); + } + mNetworkLocationProvider.updateCellState(mLastCellState); + } + + // There might be an existing wifi scan available + if (mWifiManager != null) { + List<ScanResult> wifiScanResults = mWifiManager.getScanResults(); + if (wifiScanResults != null && wifiScanResults.size() != 0) { + mNetworkLocationInterface.updateWifiScanResults(wifiScanResults); + if (mCollector != null) { + mCollector.updateWifiScanResults(wifiScanResults); + } + } } } } } - private WifiManager.WifiLock getWifiWakelock() { + public void setLocationCollector(ILocationCollector collector) { + synchronized (mLocationListeners) { + mCollector = collector; + if (mGpsLocationProvider != null) { + mGpsLocationProvider.setLocationCollector(mCollector); + } + } + } + + private WifiManager.WifiLock getWifiWakelockLocked() { if (mWifiLock == null && mWifiManager != null) { mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY); mWifiLock.setReferenceCounted(false); @@ -575,7 +663,7 @@ public class LocationManagerService extends ILocationManager.Stub { return mWifiLock; } - private boolean isAllowedBySettings(String provider) { + private boolean isAllowedBySettingsLocked(String provider) { if (mEnabledProviders.contains(provider)) { return true; } @@ -590,7 +678,7 @@ public class LocationManagerService extends ILocationManager.Stub { return ((allowedProviders != null) && (allowedProviders.contains(provider))); } - private void checkPermissions(String provider) { + private void checkPermissionsSafe(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { @@ -606,7 +694,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private boolean isAllowedProvider(String provider) { + private boolean isAllowedProviderSafe(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { @@ -631,7 +719,9 @@ public class LocationManagerService extends ILocationManager.Stub { public List<String> getAllProviders() { try { - return _getAllProviders(); + synchronized (mLocationListeners) { + return _getAllProvidersLocked(); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -640,7 +730,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private List<String> _getAllProviders() { + private List<String> _getAllProvidersLocked() { if (Config.LOGD) { Log.d(TAG, "getAllProviders"); } @@ -655,7 +745,9 @@ public class LocationManagerService extends ILocationManager.Stub { public List<String> getProviders(boolean enabledOnly) { try { - return _getProviders(enabledOnly); + synchronized (mLocationListeners) { + return _getProvidersLocked(enabledOnly); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -664,7 +756,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private List<String> _getProviders(boolean enabledOnly) { + private List<String> _getProvidersLocked(boolean enabledOnly) { if (Config.LOGD) { Log.d(TAG, "getProviders"); } @@ -673,8 +765,8 @@ public class LocationManagerService extends ILocationManager.Stub { for (LocationProviderImpl p : providers) { String name = p.getName(); - if (isAllowedProvider(name)) { - if (enabledOnly && !isAllowedBySettings(name)) { + if (isAllowedProviderSafe(name)) { + if (enabledOnly && !isAllowedBySettingsLocked(name)) { continue; } out.add(name); @@ -684,26 +776,33 @@ public class LocationManagerService extends ILocationManager.Stub { } public void updateProviders() { + synchronized (mLocationListeners) { + updateProvidersLocked(); + } + } + + private void updateProvidersLocked() { for (LocationProviderImpl p : LocationProviderImpl.getProviders()) { boolean isEnabled = p.isEnabled(); String name = p.getName(); - boolean shouldBeEnabled = isAllowedBySettings(name); + boolean shouldBeEnabled = isAllowedBySettingsLocked(name); // Collection is only allowed when network provider is being used - if (p.getName().equals(LocationManager.NETWORK_PROVIDER)) { + if (mCollector != null && + p.getName().equals(LocationManager.NETWORK_PROVIDER)) { mCollector.updateNetworkProviderStatus(shouldBeEnabled); } if (isEnabled && !shouldBeEnabled) { - updateProviderListeners(name, false); + updateProviderListenersLocked(name, false); } else if (!isEnabled && shouldBeEnabled) { - updateProviderListeners(name, true); + updateProviderListenersLocked(name, true); } } } - private void updateProviderListeners(String provider, boolean enabled) { + private void updateProviderListenersLocked(String provider, boolean enabled) { int listeners = 0; LocationProviderImpl p = LocationProviderImpl.getProvider(provider); @@ -711,49 +810,63 @@ public class LocationManagerService extends ILocationManager.Stub { return; } - synchronized (mRecordsByProvider) { - HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); - if (records != null) { - for (UpdateRecord record : records) { - // Sends a notification message to the receiver - try { - Receiver receiver = record.mReceiver; - if (receiver.isListener()) { - if (enabled) { - receiver.getListener().onProviderEnabled(provider); - } else { - receiver.getListener().onProviderDisabled(provider); - } + ArrayList<Receiver> deadReceivers = null; + + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); + if (records != null) { + final int N = records.size(); + for (int i=0; i<N; i++) { + UpdateRecord record = records.get(i); + // Sends a notification message to the receiver + try { + Receiver receiver = record.mReceiver; + if (receiver.isListener()) { + if (enabled) { + receiver.getListener().onProviderEnabled(provider); } else { - PendingIntent intent = receiver.getPendingIntent(); - Intent providerIntent = new Intent(); - providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled); - try { - receiver.getPendingIntent().send(mContext, 0, - providerIntent, null, null); - } catch (PendingIntent.CanceledException e) { - _removeUpdates(receiver); + receiver.getListener().onProviderDisabled(provider); + } + } else { + Intent providerIntent = new Intent(); + providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled); + try { + receiver.getPendingIntent().send(mContext, 0, + providerIntent, null, null); + } catch (PendingIntent.CanceledException e) { + if (deadReceivers == null) { + deadReceivers = new ArrayList<Receiver>(); + deadReceivers.add(receiver); } } - } catch (RemoteException e) { - // The death link will clean this up. } - listeners++; + } catch (RemoteException e) { + // The death link will clean this up. } + listeners++; } } + if (deadReceivers != null) { + for (int i=deadReceivers.size()-1; i>=0; i--) { + removeUpdatesLocked(deadReceivers.get(i)); + } + } + if (enabled) { p.enable(); if (listeners > 0) { - p.setMinTime(getMinTime(provider)); + p.setMinTime(getMinTimeLocked(provider)); p.enableLocationTracking(true); - updateWakelockStatus(mScreenOn); + updateWakelockStatusLocked(mScreenOn); } } else { p.enableLocationTracking(false); + if (p == mGpsLocationProvider) { + mGpsNavigating = false; + reportStopGpsLocked(); + } p.disable(); - updateWakelockStatus(mScreenOn); + updateWakelockStatusLocked(mScreenOn); } if (enabled && listeners > 0) { @@ -765,40 +878,43 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private long getMinTime(String provider) { + private long getMinTimeLocked(String provider) { long minTime = Long.MAX_VALUE; - synchronized (mRecordsByProvider) { - HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); - if (records != null) { - for (UpdateRecord r : records) { - minTime = Math.min(minTime, r.mMinTime); - } + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); + if (records != null) { + for (int i=records.size()-1; i>=0; i--) { + minTime = Math.min(minTime, records.get(i).mMinTime); } } return minTime; } private class UpdateRecord { - String mProvider; - Receiver mReceiver; - long mMinTime; - float mMinDistance; - String[] mPackages; + final String mProvider; + final Receiver mReceiver; + final long mMinTime; + final float mMinDistance; + final int mUid; + final String[] mPackages; + /** + * Note: must be constructed with lock held. + */ UpdateRecord(String provider, long minTime, float minDistance, - Receiver receiver, String[] packages) { + Receiver receiver, int uid, String[] packages) { mProvider = provider; mReceiver = receiver; mMinTime = minTime; mMinDistance = minDistance; + mUid = uid; mPackages = packages; - synchronized (mRecordsByProvider) { - HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); - if (records == null) { - records = new HashSet<UpdateRecord>(); - mRecordsByProvider.put(provider, records); - } + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); + if (records == null) { + records = new ArrayList<UpdateRecord>(); + mRecordsByProvider.put(provider, records); + } + if (!records.contains(this)) { records.add(this); } } @@ -807,18 +923,39 @@ public class LocationManagerService extends ILocationManager.Stub { * Method to be called when a record will no longer be used. Calling this multiple times * must have the same effect as calling it once. */ - public void dispose() { - synchronized (mRecordsByProvider) { - HashSet<UpdateRecord> records = mRecordsByProvider.get(this.mProvider); - records.remove(this); - } + void disposeLocked() { + ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider); + records.remove(this); } + @Override + public String toString() { + return "UpdateRecord{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + mProvider + " " + mReceiver + "}"; + } + + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + this); + pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver); + pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance); + StringBuilder sb = new StringBuilder(); + if (mPackages != null) { + for (int i=0; i<mPackages.length; i++) { + if (i > 0) sb.append(", "); + sb.append(mPackages[i]); + } + } + pw.println(prefix + "mUid=" + mUid + " mPackages=" + sb); + } + /** * Calls dispose(). */ @Override protected void finalize() { - dispose(); + synchronized (mLocationListeners) { + disposeLocked(); + } } } @@ -826,8 +963,10 @@ public class LocationManagerService extends ILocationManager.Stub { long minTime, float minDistance, ILocationListener listener) { try { - _requestLocationUpdates(provider, minTime, minDistance, - new Receiver(listener)); + synchronized (mLocationListeners) { + requestLocationUpdatesLocked(provider, minTime, minDistance, + new Receiver(listener, Binder.getCallingUid())); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -838,8 +977,10 @@ public class LocationManagerService extends ILocationManager.Stub { public void requestLocationUpdatesPI(String provider, long minTime, float minDistance, PendingIntent intent) { try { - _requestLocationUpdates(provider, minTime, minDistance, - new Receiver(intent)); + synchronized (mLocationListeners) { + requestLocationUpdatesLocked(provider, minTime, minDistance, + new Receiver(intent, Binder.getCallingUid())); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -847,11 +988,10 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void _requestLocationUpdates(String provider, + private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance, Receiver receiver) { - Object key = receiver.getKey(); if (Config.LOGD) { - Log.d(TAG, "_requestLocationUpdates: listener = " + key); + Log.d(TAG, "_requestLocationUpdates: listener = " + receiver); } LocationProviderImpl impl = LocationProviderImpl.getProvider(provider); @@ -859,62 +999,63 @@ public class LocationManagerService extends ILocationManager.Stub { throw new IllegalArgumentException("provider=" + provider); } - checkPermissions(provider); + checkPermissionsSafe(provider); String[] packages = getPackageNames(); // so wakelock calls will succeed + final int callingUid = Binder.getCallingUid(); long identity = Binder.clearCallingIdentity(); try { - UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, packages); - synchronized (mLocationListeners) { - if (mListeners.get(key) == null) { - try { - if (receiver.isListener()) { - receiver.getListener().asBinder().linkToDeath(receiver, 0); - } - mListeners.put(key, receiver); - } catch (RemoteException e) { - return; + UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, + receiver, callingUid, packages); + if (!mListeners.contains(receiver)) { + try { + if (receiver.isListener()) { + receiver.getListener().asBinder().linkToDeath(receiver, 0); } + mListeners.add(receiver); + } catch (RemoteException e) { + return; } + } - HashMap<String,UpdateRecord> records = mLocationListeners.get(key); - if (records == null) { - records = new HashMap<String,UpdateRecord>(); - mLocationListeners.put(key, records); - } - UpdateRecord oldRecord = records.put(provider, r); - if (oldRecord != null) { - oldRecord.dispose(); - } - - if (impl instanceof NetworkLocationProvider) { - ((NetworkLocationProvider) impl).addListener(packages); - } - - boolean isProviderEnabled = isAllowedBySettings(provider); - if (isProviderEnabled) { - long minTimeForProvider = getMinTime(provider); - impl.setMinTime(minTimeForProvider); - impl.enableLocationTracking(true); - updateWakelockStatus(mScreenOn); + HashMap<String,UpdateRecord> records = mLocationListeners.get(receiver); + if (records == null) { + records = new HashMap<String,UpdateRecord>(); + mLocationListeners.put(receiver, records); + } + UpdateRecord oldRecord = records.put(provider, r); + if (oldRecord != null) { + oldRecord.disposeLocked(); + } - // Clear heartbeats if any before starting a new one - mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); - Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider); - mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000); + boolean isProviderEnabled = isAllowedBySettingsLocked(provider); + if (isProviderEnabled) { + long minTimeForProvider = getMinTimeLocked(provider); + impl.setMinTime(minTimeForProvider); + impl.enableLocationTracking(true); + updateWakelockStatusLocked(mScreenOn); - } else { - try { - // Notify the listener that updates are currently disabled - if (receiver.isListener()) { - receiver.getListener().onProviderDisabled(provider); - } - } catch(RemoteException e) { - Log.w(TAG, "RemoteException calling onProviderDisabled on " + - receiver.getListener()); + if (provider.equals(LocationManager.GPS_PROVIDER)) { + if (mGpsNavigating) { + updateReportedGpsLocked(); + } + } + + // Clear heartbeats if any before starting a new one + mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); + Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider); + mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000); + } else { + try { + // Notify the listener that updates are currently disabled + if (receiver.isListener()) { + receiver.getListener().onProviderDisabled(provider); } + } catch(RemoteException e) { + Log.w(TAG, "RemoteException calling onProviderDisabled on " + + receiver.getListener()); } } } finally { @@ -924,7 +1065,9 @@ public class LocationManagerService extends ILocationManager.Stub { public void removeUpdates(ILocationListener listener) { try { - _removeUpdates(new Receiver(listener)); + synchronized (mLocationListeners) { + removeUpdatesLocked(new Receiver(listener, Binder.getCallingUid())); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -934,7 +1077,9 @@ public class LocationManagerService extends ILocationManager.Stub { public void removeUpdatesPI(PendingIntent intent) { try { - _removeUpdates(new Receiver(intent)); + synchronized (mLocationListeners) { + removeUpdatesLocked(new Receiver(intent, Binder.getCallingUid())); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -942,72 +1087,75 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void _removeUpdates(Receiver receiver) { - Object key = receiver.getKey(); + private void removeUpdatesLocked(Receiver receiver) { if (Config.LOGD) { - Log.d(TAG, "_removeUpdates: listener = " + key); + Log.d(TAG, "_removeUpdates: listener = " + receiver); } // so wakelock calls will succeed + final int callingUid = Binder.getCallingUid(); long identity = Binder.clearCallingIdentity(); try { - synchronized (mLocationListeners) { - Receiver myReceiver = mListeners.remove(key); - if ((myReceiver != null) && (myReceiver.isListener())) { + int idx = mListeners.indexOf(receiver); + if (idx >= 0) { + Receiver myReceiver = mListeners.remove(idx); + if (myReceiver.isListener()) { myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0); } + } - // Record which providers were associated with this listener - HashSet<String> providers = new HashSet<String>(); - HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(key); - if (oldRecords != null) { - // Call dispose() on the obsolete update records. - for (UpdateRecord record : oldRecords.values()) { - if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) { - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.removeListener(record.mPackages); - } + // Record which providers were associated with this listener + HashSet<String> providers = new HashSet<String>(); + HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(receiver); + if (oldRecords != null) { + // Call dispose() on the obsolete update records. + for (UpdateRecord record : oldRecords.values()) { + if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) { + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.removeListener(record.mPackages); } - record.dispose(); } - // Accumulate providers - providers.addAll(oldRecords.keySet()); + record.disposeLocked(); + } + // Accumulate providers + providers.addAll(oldRecords.keySet()); + } + + mLocationListeners.remove(receiver); + mLastFixBroadcast.remove(receiver); + mLastStatusBroadcast.remove(receiver); + + // See if the providers associated with this listener have any + // other listeners; if one does, inform it of the new smallest minTime + // value; if one does not, disable location tracking for it + for (String provider : providers) { + // If provider is already disabled, don't need to do anything + if (!isAllowedBySettingsLocked(provider)) { + continue; } - mLocationListeners.remove(key); - mLastFixBroadcast.remove(key); - mLastStatusBroadcast.remove(key); - - // See if the providers associated with this listener have any - // other listeners; if one does, inform it of the new smallest minTime - // value; if one does not, disable location tracking for it - for (String provider : providers) { - // If provider is already disabled, don't need to do anything - if (!isAllowedBySettings(provider)) { - continue; - } + boolean hasOtherListener = false; + ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider); + if (recordsForProvider != null && recordsForProvider.size() > 0) { + hasOtherListener = true; + } - boolean hasOtherListener = false; - synchronized (mRecordsByProvider) { - HashSet<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider); - if (recordsForProvider != null && recordsForProvider.size() > 0) { - hasOtherListener = true; - } + LocationProviderImpl p = LocationProviderImpl.getProvider(provider); + if (p != null) { + if (hasOtherListener) { + p.setMinTime(getMinTimeLocked(provider)); + } else { + mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); + p.enableLocationTracking(false); } - - LocationProviderImpl p = LocationProviderImpl.getProvider(provider); - if (p != null) { - if (hasOtherListener) { - p.setMinTime(getMinTime(provider)); - } else { - mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); - p.enableLocationTracking(false); - } + + if (p == mGpsLocationProvider && mGpsNavigating) { + updateReportedGpsLocked(); } } - - updateWakelockStatus(mScreenOn); } + + updateWakelockStatusLocked(mScreenOn); } finally { Binder.restoreCallingIdentity(identity); } @@ -1018,7 +1166,7 @@ public class LocationManagerService extends ILocationManager.Stub { return false; } if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) != - PackageManager.PERMISSION_GRANTED) { + PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); } @@ -1032,36 +1180,42 @@ public class LocationManagerService extends ILocationManager.Stub { } public void removeGpsStatusListener(IGpsStatusListener listener) { - mGpsLocationProvider.removeGpsStatusListener(listener); + synchronized (mLocationListeners) { + mGpsLocationProvider.removeGpsStatusListener(listener); + } } public boolean sendExtraCommand(String provider, String command, Bundle extras) { // first check for permission to the provider - checkPermissions(provider); + checkPermissionsSafe(provider); // and check for ACCESS_LOCATION_EXTRA_COMMANDS if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission"); } - LocationProviderImpl impl = LocationProviderImpl.getProvider(provider); - if (provider == null) { - return false; + synchronized (mLocationListeners) { + LocationProviderImpl impl = LocationProviderImpl.getProvider(provider); + if (provider == null) { + return false; + } + + return impl.sendExtraCommand(command, extras); } - - return impl.sendExtraCommand(command, extras); } class ProximityAlert { - double mLatitude; - double mLongitude; - float mRadius; - long mExpiration; - PendingIntent mIntent; - Location mLocation; - - public ProximityAlert(double latitude, double longitude, + final int mUid; + final double mLatitude; + final double mLongitude; + final float mRadius; + final long mExpiration; + final PendingIntent mIntent; + final Location mLocation; + + public ProximityAlert(int uid, double latitude, double longitude, float radius, long expiration, PendingIntent intent) { + mUid = uid; mLatitude = latitude; mLongitude = longitude; mRadius = radius; @@ -1073,15 +1227,15 @@ public class LocationManagerService extends ILocationManager.Stub { mLocation.setLongitude(longitude); } - public long getExpiration() { + long getExpiration() { return mExpiration; } - public PendingIntent getIntent() { + PendingIntent getIntent() { return mIntent; } - public boolean isInProximity(double latitude, double longitude) { + boolean isInProximity(double latitude, double longitude) { Location loc = new Location(""); loc.setLatitude(latitude); loc.setLongitude(longitude); @@ -1089,6 +1243,22 @@ public class LocationManagerService extends ILocationManager.Stub { double radius = loc.distanceTo(mLocation); return radius <= mRadius; } + + @Override + public String toString() { + return "ProximityAlert{" + + Integer.toHexString(System.identityHashCode(this)) + + " uid " + mUid + mIntent + "}"; + } + + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + this); + pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude); + pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration); + pw.println(prefix + "mIntent=" + mIntent); + pw.println(prefix + "mLocation:"); + mLocation.dump(new PrintWriterPrinter(pw), prefix + " "); + } } // Listener for receiving locations to trigger proximity alerts @@ -1096,6 +1266,7 @@ public class LocationManagerService extends ILocationManager.Stub { boolean isGpsAvailable = false; + // Note: this is called with the lock held. public void onLocationChanged(Location loc) { // If Gps is available, then ignore updates from NetworkLocationProvider @@ -1180,16 +1351,19 @@ public class LocationManagerService extends ILocationManager.Stub { } + // Note: this is called with the lock held. public void onProviderDisabled(String provider) { if (provider.equals(LocationManager.GPS_PROVIDER)) { isGpsAvailable = false; } } + // Note: this is called with the lock held. public void onProviderEnabled(String provider) { // ignore } + // Note: this is called with the lock held. public void onStatusChanged(String provider, int status, Bundle extras) { if ((provider.equals(LocationManager.GPS_PROVIDER)) && (status != LocationProvider.AVAILABLE)) { @@ -1201,7 +1375,9 @@ public class LocationManagerService extends ILocationManager.Stub { public void addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent) { try { - _addProximityAlert(latitude, longitude, radius, expiration, intent); + synchronized (mLocationListeners) { + addProximityAlertLocked(latitude, longitude, radius, expiration, intent); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -1209,7 +1385,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void _addProximityAlert(double latitude, double longitude, + private void addProximityAlertLocked(double latitude, double longitude, float radius, long expiration, PendingIntent intent) { if (Config.LOGD) { Log.d(TAG, "addProximityAlert: latitude = " + latitude + @@ -1219,37 +1395,42 @@ public class LocationManagerService extends ILocationManager.Stub { } // Require ability to access all providers for now - if (!isAllowedProvider(LocationManager.GPS_PROVIDER) || - !isAllowedProvider(LocationManager.NETWORK_PROVIDER)) { + if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) || + !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) { throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); } if (expiration != -1) { expiration += System.currentTimeMillis(); } - ProximityAlert alert = new ProximityAlert(latitude, longitude, radius, expiration, intent); + ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(), + latitude, longitude, radius, expiration, intent); mProximityAlerts.put(intent, alert); if (mProximityListener == null) { - mProximityListener = new Receiver(new ProximityListener()); + mProximityListener = new Receiver(new ProximityListener(), -1); LocationProvider provider = LocationProviderImpl.getProvider( LocationManager.GPS_PROVIDER); if (provider != null) { - _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener); + requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener); } provider = LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER); if (provider != null) { - _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener); + requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener); } + } else if (mGpsNavigating) { + updateReportedGpsLocked(); } } public void removeProximityAlert(PendingIntent intent) { try { - _removeProximityAlert(intent); + synchronized (mLocationListeners) { + removeProximityAlertLocked(intent); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -1257,15 +1438,17 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void _removeProximityAlert(PendingIntent intent) { + private void removeProximityAlertLocked(PendingIntent intent) { if (Config.LOGD) { Log.d(TAG, "removeProximityAlert: intent = " + intent); } mProximityAlerts.remove(intent); if (mProximityAlerts.size() == 0) { - _removeUpdates(mProximityListener); + removeUpdatesLocked(mProximityListener); mProximityListener = null; + } else if (mGpsNavigating) { + updateReportedGpsLocked(); } } @@ -1276,7 +1459,9 @@ public class LocationManagerService extends ILocationManager.Stub { */ public Bundle getProviderInfo(String provider) { try { - return _getProviderInfo(provider); + synchronized (mLocationListeners) { + return _getProviderInfoLocked(provider); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -1285,13 +1470,13 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private Bundle _getProviderInfo(String provider) { + private Bundle _getProviderInfoLocked(String provider) { LocationProviderImpl p = LocationProviderImpl.getProvider(provider); if (p == null) { return null; } - checkPermissions(provider); + checkPermissionsSafe(provider); Bundle b = new Bundle(); b.putBoolean("network", p.requiresNetwork()); @@ -1309,7 +1494,9 @@ public class LocationManagerService extends ILocationManager.Stub { public boolean isProviderEnabled(String provider) { try { - return _isProviderEnabled(provider); + synchronized (mLocationListeners) { + return _isProviderEnabledLocked(provider); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -1318,19 +1505,21 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private boolean _isProviderEnabled(String provider) { - checkPermissions(provider); + private boolean _isProviderEnabledLocked(String provider) { + checkPermissionsSafe(provider); LocationProviderImpl p = LocationProviderImpl.getProvider(provider); if (p == null) { throw new IllegalArgumentException("provider=" + provider); } - return isAllowedBySettings(provider); + return isAllowedBySettingsLocked(provider); } public Location getLastKnownLocation(String provider) { try { - return _getLastKnownLocation(provider); + synchronized (mLocationListeners) { + return _getLastKnownLocationLocked(provider); + } } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -1339,22 +1528,22 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private Location _getLastKnownLocation(String provider) { - checkPermissions(provider); + private Location _getLastKnownLocationLocked(String provider) { + checkPermissionsSafe(provider); LocationProviderImpl p = LocationProviderImpl.getProvider(provider); if (p == null) { throw new IllegalArgumentException("provider=" + provider); } - if (!isAllowedBySettings(provider)) { + if (!isAllowedBySettingsLocked(provider)) { return null; } Location location = mLastKnownLocation.get(provider); if (location == null) { // Get the persistent last known location for the provider - location = readLastKnownLocation(provider); + location = readLastKnownLocationLocked(provider); if (location != null) { mLastKnownLocation.put(provider, location); } @@ -1363,7 +1552,7 @@ public class LocationManagerService extends ILocationManager.Stub { return location; } - private boolean shouldBroadcast(Location loc, Location lastLoc, UpdateRecord record) { + private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) { // Always broadcast the first update if (lastLoc == null) { return true; @@ -1386,8 +1575,8 @@ public class LocationManagerService extends ILocationManager.Stub { return true; } - private void handleLocationChanged(String provider) { - HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); + private void handleLocationChangedLocked(String provider) { + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); if (records == null || records.size() == 0) { return; } @@ -1424,9 +1613,9 @@ public class LocationManagerService extends ILocationManager.Stub { } else { location.set(loc); } - writeLastKnownLocation(provider, loc); + writeLastKnownLocationLocked(provider, loc); - if (p instanceof NetworkLocationProvider) { + if (p instanceof INetworkLocationProvider) { mWakeLockNetworkReceived = true; } else if (p instanceof GpsLocationProvider) { // Gps location received signal is in NetworkStateBroadcastReceiver @@ -1459,40 +1648,44 @@ public class LocationManagerService extends ILocationManager.Stub { extras.putAll(mockExtras); } + ArrayList<Receiver> deadReceivers = null; + // Broadcast location or status to all listeners - for (UpdateRecord r : records) { + final int N = records.size(); + for (int i=0; i<N; i++) { + UpdateRecord r = records.get(i); Receiver receiver = r.mReceiver; - Object key = receiver.getKey(); // Broadcast location only if it is valid if (locationValid) { - HashMap<String,Location> map = mLastFixBroadcast.get(key); + HashMap<String,Location> map = mLastFixBroadcast.get(receiver); if (map == null) { map = new HashMap<String,Location>(); - mLastFixBroadcast.put(key, map); + mLastFixBroadcast.put(receiver, map); } Location lastLoc = map.get(provider); - if ((lastLoc == null) || shouldBroadcast(loc, lastLoc, r)) { + if ((lastLoc == null) || shouldBroadcastSafe(loc, lastLoc, r)) { if (lastLoc == null) { lastLoc = new Location(loc); map.put(provider, lastLoc); } else { lastLoc.set(loc); } - try { - receiver.onLocationChanged(loc); - } catch (RemoteException doe) { + if (!receiver.callLocationChangedLocked(loc)) { Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver); - _removeUpdates(receiver); + if (deadReceivers == null) { + deadReceivers = new ArrayList<Receiver>(); + } + deadReceivers.add(receiver); } } } // Broadcast status message - HashMap<String,Long> statusMap = mLastStatusBroadcast.get(key); + HashMap<String,Long> statusMap = mLastStatusBroadcast.get(receiver); if (statusMap == null) { statusMap = new HashMap<String,Long>(); - mLastStatusBroadcast.put(key, statusMap); + mLastStatusBroadcast.put(receiver, statusMap); } long prevStatusUpdateTime = (statusMap.get(provider) != null) ? statusMap.get(provider) : 0; @@ -1501,14 +1694,23 @@ public class LocationManagerService extends ILocationManager.Stub { (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) { statusMap.put(provider, newStatusUpdateTime); - try { - receiver.onStatusChanged(provider, status, extras); - } catch (RemoteException doe) { + if (!receiver.callStatusChangedLocked(provider, status, extras)) { Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver); - _removeUpdates(receiver); + if (deadReceivers == null) { + deadReceivers = new ArrayList<Receiver>(); + } + if (!deadReceivers.contains(receiver)) { + deadReceivers.add(receiver); + } } } } + + if (deadReceivers != null) { + for (int i=deadReceivers.size()-1; i>=0; i--) { + removeUpdatesLocked(deadReceivers.get(i)); + } + } } private class LocationWorkerHandler extends Handler { @@ -1519,57 +1721,68 @@ public class LocationManagerService extends ILocationManager.Stub { if (msg.what == MESSAGE_HEARTBEAT) { // log("LocationWorkerHandler: Heartbeat!"); - synchronized (mRecordsByProvider) { + synchronized (mLocationListeners) { String provider = (String) msg.obj; - if (!isAllowedBySettings(provider)) { + if (!isAllowedBySettingsLocked(provider)) { return; } // Process the location fix if the screen is on or we're holding a wakelock if (mScreenOn || (mWakeLockAcquireTime != 0)) { - handleLocationChanged(provider); + handleLocationChangedLocked(provider); } // If it continues to have listeners - HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); if (records != null && records.size() > 0) { Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider); sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000); } - } - - if ((mWakeLockAcquireTime != 0) && - (SystemClock.elapsedRealtime() - mWakeLockAcquireTime - > MAX_TIME_FOR_WAKE_LOCK)) { - - removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); - removeMessages(MESSAGE_RELEASE_WAKE_LOCK); - - log("LocationWorkerHandler: Exceeded max time for wake lock"); - Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK); - sendMessageAtFrontOfQueue(m); - - } else if (mWakeLockAcquireTime != 0 && - mWakeLockGpsReceived && mWakeLockNetworkReceived) { - - removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); - removeMessages(MESSAGE_RELEASE_WAKE_LOCK); - log("LocationWorkerHandler: Locations received."); - mWakeLockAcquireTime = 0; - Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK); - sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK); + if ((mWakeLockAcquireTime != 0) && + (SystemClock.elapsedRealtime() - mWakeLockAcquireTime + > MAX_TIME_FOR_WAKE_LOCK)) { + + removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); + removeMessages(MESSAGE_RELEASE_WAKE_LOCK); + + log("LocationWorkerHandler: Exceeded max time for wake lock"); + Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK); + sendMessageAtFrontOfQueue(m); + + } else if (mWakeLockAcquireTime != 0 && + mWakeLockGpsReceived && mWakeLockNetworkReceived) { + + removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); + removeMessages(MESSAGE_RELEASE_WAKE_LOCK); + + log("LocationWorkerHandler: Locations received."); + mWakeLockAcquireTime = 0; + Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK); + sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK); + } } } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) { log("LocationWorkerHandler: Acquire"); - acquireWakeLock(); + synchronized (mLocationListeners) { + acquireWakeLockLocked(); + } } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) { log("LocationWorkerHandler: Release"); // Update wakelock status so the next alarm is set before releasing wakelock - updateWakelockStatus(mScreenOn); - releaseWakeLock(); + synchronized (mLocationListeners) { + updateWakelockStatusLocked(mScreenOn); + releaseWakeLockLocked(); + } + } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) { + synchronized (mLocationListeners) { + Log.d(TAG, "installing network location provider"); + INetworkLocationManager.InstallCallback callback = + (INetworkLocationManager.InstallCallback)msg.obj; + callback.installNetworkLocationProvider(LocationManagerService.this); + } } } catch (Exception e) { // Log, don't crash! @@ -1578,45 +1791,113 @@ public class LocationManagerService extends ILocationManager.Stub { } } + class CellLocationUpdater extends Thread { + CellLocation mNextLocation; + + CellLocationUpdater() { + super("CellLocationUpdater"); + } + + @Override + public void run() { + int curAsu = -1; + CellLocation curLocation = null; + + while (true) { + // See if there is more work to do... + synchronized (mLocationListeners) { + if (curLocation == mNextLocation) { + mCellLocationUpdater = null; + break; + } + + curLocation = mNextLocation; + if (curLocation == null) { + mCellLocationUpdater = null; + break; + } + + curAsu = mLastSignalStrength; + + mNextLocation = null; + } + + try { + // Gets cell state. This can block so must be done without + // locks held. + CellState cs = new CellState(mTelephonyManager, curLocation, curAsu); + + synchronized (mLocationListeners) { + mLastCellState = cs; + + cs.updateSignalStrength(mLastSignalStrength); + cs.updateRadioType(mLastRadioType); + + // Notify collector + if (mCollector != null) { + mCollector.updateCellState(cs); + } + + // Updates providers + List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); + for (LocationProviderImpl provider : providers) { + if (provider.requiresCell()) { + provider.updateCellState(cs); + } + } + } + } catch (RuntimeException e) { + Log.e(TAG, "Exception in PhoneStateListener.onCellLocationChanged:", e); + } + } + } + } + + CellLocationUpdater mCellLocationUpdater = null; + CellState mLastCellState = null; + int mLastSignalStrength = -1; + int mLastRadioType = -1; + PhoneStateListener mPhoneStateListener = new PhoneStateListener() { - private CellState mLastCellState = null; @Override public void onCellLocationChanged(CellLocation cellLocation) { - try { - int asu = mSignalStrength; - - // Gets cell state - mLastCellState = new CellState(mTelephonyManager, cellLocation, asu); - - // Notify collector - mCollector.updateCellState(mLastCellState); - - // Updates providers - List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); - for (LocationProviderImpl provider : providers) { - if (provider.requiresCell()) { - provider.updateCellState(mLastCellState); - } + synchronized (mLocationListeners) { + if (mCellLocationUpdater == null) { + mCellLocationUpdater = new CellLocationUpdater(); + mCellLocationUpdater.start(); } - } catch (Exception e) { - Log.e(TAG, "Exception in PhoneStateListener.onCellLocationCahnged:", e); + mCellLocationUpdater.mNextLocation = cellLocation; } } @Override public void onSignalStrengthChanged(int asu) { - mSignalStrength = asu; - - if (mLastCellState != null) { - mLastCellState.updateSignalStrength(asu); + synchronized (mLocationListeners) { + mLastSignalStrength = asu; + + if (mLastCellState != null) { + mLastCellState.updateSignalStrength(asu); + } } } @Override public void onDataConnectionStateChanged(int state) { - if (mLastCellState != null) { - mLastCellState.updateRadioType(mTelephonyManager); + synchronized (mLocationListeners) { + // Get radio type + int radioType = mTelephonyManager.getNetworkType(); + if (radioType == TelephonyManager.NETWORK_TYPE_GPRS || + radioType == TelephonyManager.NETWORK_TYPE_EDGE) { + radioType = CellState.RADIO_TYPE_GPRS; + } else if (radioType == TelephonyManager.NETWORK_TYPE_UMTS) { + radioType = CellState.RADIO_TYPE_WCDMA; + } + mLastRadioType = radioType; + + if (mLastCellState != null) { + mLastCellState.updateRadioType(radioType); + } } } }; @@ -1626,29 +1907,82 @@ public class LocationManagerService extends ILocationManager.Stub { String action = intent.getAction(); if (action.equals(ALARM_INTENT)) { - mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); - mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK); - - log("PowerStateBroadcastReceiver: Alarm received"); - Message m = mLocationHandler.obtainMessage(MESSAGE_ACQUIRE_WAKE_LOCK); - mLocationHandler.sendMessageAtFrontOfQueue(m); + synchronized (mLocationListeners) { + log("PowerStateBroadcastReceiver: Alarm received"); + mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); + // Have to do this immediately, rather than posting a + // message, so we execute our code while the system + // is holding a wake lock until the alarm broadcast + // is finished. + acquireWakeLockLocked(); + } } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { log("PowerStateBroadcastReceiver: Screen off"); - updateWakelockStatus(false); + synchronized (mLocationListeners) { + updateWakelockStatusLocked(false); + } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { log("PowerStateBroadcastReceiver: Screen on"); - updateWakelockStatus(true); + synchronized (mLocationListeners) { + updateWakelockStatusLocked(true); + } } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { log("PowerStateBroadcastReceiver: Battery changed"); - int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100); - int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0); - boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0; - - // Notify collector battery state - mCollector.updateBatteryState(scale, level, plugged); + synchronized (mLocationListeners) { + int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100); + int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0); + boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0; + + // Notify collector battery state + if (mCollector != null) { + mCollector.updateBatteryState(scale, level, plugged); + } + } + } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) + || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) { + synchronized (mLocationListeners) { + int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (uid >= 0) { + ArrayList<Receiver> removedRecs = null; + for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) { + for (int j=i.size()-1; j>=0; j--) { + UpdateRecord ur = i.get(j); + if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) { + if (removedRecs == null) { + removedRecs = new ArrayList<Receiver>(); + } + if (!removedRecs.contains(ur.mReceiver)) { + removedRecs.add(ur.mReceiver); + } + } + } + } + ArrayList<ProximityAlert> removedAlerts = null; + for (ProximityAlert i : mProximityAlerts.values()) { + if (i.mUid == uid) { + if (removedAlerts == null) { + removedAlerts = new ArrayList<ProximityAlert>(); + } + if (!removedAlerts.contains(i)) { + removedAlerts.add(i); + } + } + } + if (removedRecs != null) { + for (int i=removedRecs.size()-1; i>=0; i--) { + removeUpdatesLocked(removedRecs.get(i)); + } + } + if (removedAlerts != null) { + for (int i=removedAlerts.size()-1; i>=0; i--) { + removeProximityAlertLocked(removedAlerts.get(i).mIntent); + } + } + } + } } } } @@ -1666,25 +2000,31 @@ public class LocationManagerService extends ILocationManager.Stub { } // Notify provider and collector of Wifi scan results - mCollector.updateWifiScanResults(wifiScanResults); - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateWifiScanResults(wifiScanResults); + synchronized (mLocationListeners) { + if (mCollector != null) { + mCollector.updateWifiScanResults(wifiScanResults); + } + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateWifiScanResults(wifiScanResults); + } } } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - int networkState = LocationProvider.TEMPORARILY_UNAVAILABLE; - boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); if (!noConnectivity) { - networkState = LocationProvider.AVAILABLE; + mNetworkState = LocationProvider.AVAILABLE; + } else { + mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; } // Notify location providers of current network state - List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); - for (LocationProviderImpl provider : providers) { - if (provider.requiresNetwork()) { - provider.updateNetworkState(networkState); + synchronized (mLocationListeners) { + List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); + for (LocationProviderImpl provider : providers) { + if (provider.requiresNetwork()) { + provider.updateNetworkState(mNetworkState); + } } } @@ -1692,18 +2032,19 @@ public class LocationManagerService extends ILocationManager.Stub { int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN); - boolean enabled; if (state == WifiManager.WIFI_STATE_ENABLED) { - enabled = true; + mWifiEnabled = true; } else if (state == WifiManager.WIFI_STATE_DISABLED) { - enabled = false; + mWifiEnabled = false; } else { return; } // Notify network provider of current wifi enabled state - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateWifiEnabledState(enabled); + synchronized (mLocationListeners) { + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateWifiEnabledState(mWifiEnabled); + } } } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) { @@ -1711,9 +2052,16 @@ public class LocationManagerService extends ILocationManager.Stub { final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false); - if (!enabled) { - // When GPS is disabled, we are OK to release wake-lock - mWakeLockGpsReceived = true; + synchronized (mLocationListeners) { + if (enabled) { + updateReportedGpsLocked(); + mGpsNavigating = true; + } else { + reportStopGpsLocked(); + mGpsNavigating = false; + // When GPS is disabled, we are OK to release wake-lock + mWakeLockGpsReceived = true; + } } } @@ -1722,7 +2070,7 @@ public class LocationManagerService extends ILocationManager.Stub { // Wake locks - private void updateWakelockStatus(boolean screenOn) { + private void updateWakelockStatusLocked(boolean screenOn) { log("updateWakelockStatus(): " + screenOn); boolean needsLock = false; @@ -1737,11 +2085,10 @@ public class LocationManagerService extends ILocationManager.Stub { needsLock = true; minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime); if (screenOn) { - startGps(); + startGpsLocked(); } else if (mScreenOn && !screenOn) { - // We just turned the screen off so stop navigating - stopGps(); + stopGpsLocked(); } } @@ -1767,13 +2114,13 @@ public class LocationManagerService extends ILocationManager.Stub { // Clear out existing wakelocks mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK); - releaseWakeLock(); + releaseWakeLockLocked(); } } - private void acquireWakeLock() { + private void acquireWakeLockLocked() { try { - acquireWakeLockX(); + acquireWakeLockXLocked(); } catch (Exception e) { // This is to catch a runtime exception thrown when we try to release an // already released lock. @@ -1781,7 +2128,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void acquireWakeLockX() { + private void acquireWakeLockXLocked() { if (mWakeLock.isHeld()) { log("Must release wakelock before acquiring"); mWakeLockAcquireTime = 0; @@ -1808,7 +2155,7 @@ public class LocationManagerService extends ILocationManager.Stub { log("Acquired wakelock"); // Start the gps provider - startGps(); + startGpsLocked(); // Acquire cell lock if (mCellWakeLockAcquired) { @@ -1821,12 +2168,12 @@ public class LocationManagerService extends ILocationManager.Stub { } // Notify NetworkLocationProvider - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired); + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired); } // Acquire wifi lock - WifiManager.WifiLock wifiLock = getWifiWakelock(); + WifiManager.WifiLock wifiLock = getWifiWakelockLocked(); if (wifiLock != null) { if (mWifiWakeLockAcquired) { // Lock is already acquired @@ -1840,7 +2187,86 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void startGps() { + private boolean reportGpsUidLocked(int curSeq, int nextSeq, int uid) { + int seq = mReportedGpsUids.get(uid, -1); + if (seq == curSeq) { + // Already reported; propagate to next sequence. + mReportedGpsUids.put(uid, nextSeq); + return true; + } else if (seq != nextSeq) { + try { + // New UID; report it. + mBatteryStats.noteStartGps(uid); + mReportedGpsUids.put(uid, nextSeq); + return true; + } catch (RemoteException e) { + } + } + return false; + } + + private void updateReportedGpsLocked() { + if (mGpsLocationProvider == null) { + return; + } + + final String name = mGpsLocationProvider.getName(); + final int curSeq = mReportedGpsSeq; + final int nextSeq = (curSeq+1) >= 0 ? (curSeq+1) : 0; + mReportedGpsSeq = nextSeq; + + ArrayList<UpdateRecord> urs = mRecordsByProvider.get(name); + int num = 0; + final int N = urs.size(); + for (int i=0; i<N; i++) { + UpdateRecord ur = urs.get(i); + if (ur.mReceiver == mProximityListener) { + // We don't want the system to take the blame for this one. + continue; + } + if (reportGpsUidLocked(curSeq, nextSeq, ur.mUid)) { + num++; + } + } + + for (ProximityAlert pe : mProximityAlerts.values()) { + if (reportGpsUidLocked(curSeq, nextSeq, pe.mUid)) { + num++; + } + } + + if (num != mReportedGpsUids.size()) { + // The number of uids is processed is different than the + // array; report any that are no longer active. + for (int i=mReportedGpsUids.size()-1; i>=0; i--) { + if (mReportedGpsUids.valueAt(i) != nextSeq) { + try { + mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i)); + } catch (RemoteException e) { + } + mReportedGpsUids.removeAt(i); + } + } + } + } + + private void reportStopGpsLocked() { + int curSeq = mReportedGpsSeq; + for (int i=mReportedGpsUids.size()-1; i>=0; i--) { + if (mReportedGpsUids.valueAt(i) == curSeq) { + try { + mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i)); + } catch (RemoteException e) { + } + } + } + curSeq++; + if (curSeq < 0) curSeq = 0; + mReportedGpsSeq = curSeq; + mReportedGpsUids.clear(); + } + + private void startGpsLocked() { boolean gpsActive = (mGpsLocationProvider != null) && mGpsLocationProvider.isLocationTracking(); if (gpsActive) { @@ -1848,7 +2274,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void stopGps() { + private void stopGpsLocked() { boolean gpsActive = mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking(); if (gpsActive) { @@ -1856,9 +2282,9 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void releaseWakeLock() { + private void releaseWakeLockLocked() { try { - releaseWakeLockX(); + releaseWakeLockXLocked(); } catch (Exception e) { // This is to catch a runtime exception thrown when we try to release an // already released lock. @@ -1866,9 +2292,9 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void releaseWakeLockX() { + private void releaseWakeLockXLocked() { // Release wifi lock - WifiManager.WifiLock wifiLock = getWifiWakelock(); + WifiManager.WifiLock wifiLock = getWifiWakelockLocked(); if (wifiLock != null) { if (mWifiWakeLockAcquired) { wifiLock.release(); @@ -1877,9 +2303,8 @@ public class LocationManagerService extends ILocationManager.Stub { } if (!mScreenOn) { - // Stop the gps - stopGps(); + stopGpsLocked(); } // Release cell lock @@ -1889,8 +2314,8 @@ public class LocationManagerService extends ILocationManager.Stub { } // Notify NetworkLocationProvider - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired); + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired); } // Release wake lock @@ -1907,15 +2332,13 @@ public class LocationManagerService extends ILocationManager.Stub { public String getFromLocation(double latitude, double longitude, int maxResults, String language, String country, String variant, String appName, List<Address> addrs) { - try { - Locale locale = new Locale(language, country, variant); - mMasfClient.reverseGeocode(locale, appName, latitude, longitude, maxResults, addrs); - return null; - } catch(IOException e) { - return e.getMessage(); - } catch(Exception e) { - Log.e(TAG, "getFromLocation got exception:", e); - return null; + synchronized (mLocationListeners) { + if (mNetworkLocationInterface != null) { + return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults, + language, country, variant, appName, addrs); + } else { + return null; + } } } @@ -1923,18 +2346,14 @@ public class LocationManagerService extends ILocationManager.Stub { double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, String language, String country, String variant, String appName, List<Address> addrs) { - - try { - Locale locale = new Locale(language, country, variant); - mMasfClient.forwardGeocode(locale, appName, locationName, - lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, addrs); - return null; - } catch(IOException e) { - return e.getMessage(); - } catch(Exception e) { - Log.e(TAG, "getFromLocationName got exception:", e); - return null; + synchronized (mLocationListeners) { + if (mNetworkLocationInterface != null) { + return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults, + language, country, variant, appName, addrs); + } else { + return null; + } } } @@ -1970,43 +2389,63 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void disable() { String name = getName(); - mEnabledProviders.remove(name); - mDisabledProviders.add(name); + // We shouldn't normally need to lock, since this should only be called + // by the service with the lock held, but let's be paranid. + synchronized (mLocationListeners) { + mEnabledProviders.remove(name); + mDisabledProviders.add(name); + } } @Override public void enable() { String name = getName(); - mEnabledProviders.add(name); - mDisabledProviders.remove(name); + // We shouldn't normally need to lock, since this should only be called + // by the service with the lock held, but let's be paranid. + synchronized (mLocationListeners) { + mEnabledProviders.add(name); + mDisabledProviders.remove(name); + } } @Override public boolean getLocation(Location l) { - Location loc = mMockProviderLocation.get(getName()); - if (loc == null) { - return false; + // We shouldn't normally need to lock, since this should only be called + // by the service with the lock held, but let's be paranid. + synchronized (mLocationListeners) { + Location loc = mMockProviderLocation.get(getName()); + if (loc == null) { + return false; + } + l.set(loc); + return true; } - l.set(loc); - return true; } @Override public int getStatus(Bundle extras) { - String name = getName(); - Integer s = mMockProviderStatus.get(name); - int status = (s == null) ? AVAILABLE : s.intValue(); - Bundle newExtras = mMockProviderStatusExtras.get(name); - if (newExtras != null) { - extras.clear(); - extras.putAll(newExtras); + // We shouldn't normally need to lock, since this should only be called + // by the service with the lock held, but let's be paranid. + synchronized (mLocationListeners) { + String name = getName(); + Integer s = mMockProviderStatus.get(name); + int status = (s == null) ? AVAILABLE : s.intValue(); + Bundle newExtras = mMockProviderStatusExtras.get(name); + if (newExtras != null) { + extras.clear(); + extras.putAll(newExtras); + } + return status; } - return status; } @Override public boolean isEnabled() { - return mEnabledProviders.contains(getName()); + // We shouldn't normally need to lock, since this should only be called + // by the service with the lock held, but let's be paranid. + synchronized (mLocationListeners) { + return mEnabledProviders.contains(getName()); + } } @Override @@ -2055,14 +2494,9 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void checkMockPermissions() { - boolean allowMocks = false; - try { - allowMocks = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ALLOW_MOCK_LOCATION) == 1; - } catch (SettingNotFoundException e) { - // Do nothing - } + private void checkMockPermissionsSafe() { + boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1; if (!allowMocks) { throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting"); } @@ -2076,87 +2510,103 @@ public class LocationManagerService extends ILocationManager.Stub { public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { - checkMockPermissions(); - - MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite, - requiresCell, hasMonetaryCost, supportsAltitude, - supportsSpeed, supportsBearing, powerRequirement, accuracy); - if (LocationProviderImpl.getProvider(name) != null) { - throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); + checkMockPermissionsSafe(); + + synchronized (mLocationListeners) { + MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite, + requiresCell, hasMonetaryCost, supportsAltitude, + supportsSpeed, supportsBearing, powerRequirement, accuracy); + if (LocationProviderImpl.getProvider(name) != null) { + throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); + } + LocationProviderImpl.addProvider(provider); + updateProvidersLocked(); } - LocationProviderImpl.addProvider(provider); - updateProviders(); } public void removeTestProvider(String provider) { - checkMockPermissions(); - LocationProviderImpl p = LocationProviderImpl.getProvider(provider); - if (p == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + checkMockPermissionsSafe(); + synchronized (mLocationListeners) { + LocationProviderImpl p = LocationProviderImpl.getProvider(provider); + if (p == null) { + throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + } + LocationProviderImpl.removeProvider(p); + updateProvidersLocked(); } - LocationProviderImpl.removeProvider(p); - updateProviders(); } public void setTestProviderLocation(String provider, Location loc) { - checkMockPermissions(); - if (LocationProviderImpl.getProvider(provider) == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + checkMockPermissionsSafe(); + synchronized (mLocationListeners) { + if (LocationProviderImpl.getProvider(provider) == null) { + throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + } + mMockProviderLocation.put(provider, loc); } - mMockProviderLocation.put(provider, loc); } public void clearTestProviderLocation(String provider) { - checkMockPermissions(); - if (LocationProviderImpl.getProvider(provider) == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + checkMockPermissionsSafe(); + synchronized (mLocationListeners) { + if (LocationProviderImpl.getProvider(provider) == null) { + throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + } + mMockProviderLocation.remove(provider); } - mMockProviderLocation.remove(provider); } public void setTestProviderEnabled(String provider, boolean enabled) { - checkMockPermissions(); - if (LocationProviderImpl.getProvider(provider) == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); - } - if (enabled) { - mEnabledProviders.add(provider); - mDisabledProviders.remove(provider); - } else { - mEnabledProviders.remove(provider); - mDisabledProviders.add(provider); + checkMockPermissionsSafe(); + synchronized (mLocationListeners) { + if (LocationProviderImpl.getProvider(provider) == null) { + throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + } + if (enabled) { + mEnabledProviders.add(provider); + mDisabledProviders.remove(provider); + } else { + mEnabledProviders.remove(provider); + mDisabledProviders.add(provider); + } + updateProvidersLocked(); } - updateProviders(); } public void clearTestProviderEnabled(String provider) { - checkMockPermissions(); - if (LocationProviderImpl.getProvider(provider) == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + checkMockPermissionsSafe(); + synchronized (mLocationListeners) { + if (LocationProviderImpl.getProvider(provider) == null) { + throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + } + mEnabledProviders.remove(provider); + mDisabledProviders.remove(provider); + updateProvidersLocked(); } - mEnabledProviders.remove(provider); - mDisabledProviders.remove(provider); - updateProviders(); } public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) { - checkMockPermissions(); - if (LocationProviderImpl.getProvider(provider) == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + checkMockPermissionsSafe(); + synchronized (mLocationListeners) { + if (LocationProviderImpl.getProvider(provider) == null) { + throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + } + mMockProviderStatus.put(provider, new Integer(status)); + mMockProviderStatusExtras.put(provider, extras); + mMockProviderStatusUpdateTime.put(provider, new Long(updateTime)); } - mMockProviderStatus.put(provider, new Integer(status)); - mMockProviderStatusExtras.put(provider, extras); - mMockProviderStatusUpdateTime.put(provider, new Long(updateTime)); } public void clearTestProviderStatus(String provider) { - checkMockPermissions(); - if (LocationProviderImpl.getProvider(provider) == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + checkMockPermissionsSafe(); + synchronized (mLocationListeners) { + if (LocationProviderImpl.getProvider(provider) == null) { + throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); + } + mMockProviderStatus.remove(provider); + mMockProviderStatusExtras.remove(provider); + mMockProviderStatusUpdateTime.remove(provider); } - mMockProviderStatus.remove(provider); - mMockProviderStatusExtras.remove(provider); - mMockProviderStatusUpdateTime.remove(provider); } private void log(String log) { @@ -2164,5 +2614,157 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, log); } } + + 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 AlarmManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + synchronized (mLocationListeners) { + pw.println("Current Location Manager state:"); + pw.println(" sProvidersLoaded=" + sProvidersLoaded); + pw.println(" mGpsLocationProvider=" + mGpsLocationProvider); + pw.println(" mGpsNavigating=" + mGpsNavigating); + pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider); + pw.println(" mNetworkLocationInterface=" + mNetworkLocationInterface); + pw.println(" mLastSignalStrength=" + mLastSignalStrength + + " mLastRadioType=" + mLastRadioType); + pw.println(" mCellLocationUpdater=" + mCellLocationUpdater); + pw.println(" mLastCellState=" + mLastCellState); + pw.println(" mCollector=" + mCollector); + pw.println(" mAlarmInterval=" + mAlarmInterval + + " mScreenOn=" + mScreenOn + + " mWakeLockAcquireTime=" + mWakeLockAcquireTime); + pw.println(" mWakeLockGpsReceived=" + mWakeLockGpsReceived + + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived); + pw.println(" mWifiWakeLockAcquired=" + mWifiWakeLockAcquired + + " mCellWakeLockAcquired=" + mCellWakeLockAcquired); + pw.println(" Listeners:"); + int N = mListeners.size(); + for (int i=0; i<N; i++) { + pw.println(" " + mListeners.get(i)); + } + pw.println(" Location Listeners:"); + for (Map.Entry<Receiver, HashMap<String,UpdateRecord>> i + : mLocationListeners.entrySet()) { + pw.println(" " + i.getKey() + ":"); + for (Map.Entry<String,UpdateRecord> j : i.getValue().entrySet()) { + pw.println(" " + j.getKey() + ":"); + j.getValue().dump(pw, " "); + } + } + pw.println(" Last Fix Broadcasts:"); + for (Map.Entry<Receiver, HashMap<String,Location>> i + : mLastFixBroadcast.entrySet()) { + pw.println(" " + i.getKey() + ":"); + for (Map.Entry<String,Location> j : i.getValue().entrySet()) { + pw.println(" " + j.getKey() + ":"); + j.getValue().dump(new PrintWriterPrinter(pw), " "); + } + } + pw.println(" Last Status Broadcasts:"); + for (Map.Entry<Receiver, HashMap<String,Long>> i + : mLastStatusBroadcast.entrySet()) { + pw.println(" " + i.getKey() + ":"); + for (Map.Entry<String,Long> j : i.getValue().entrySet()) { + pw.println(" " + j.getKey() + " -> 0x" + + Long.toHexString(j.getValue())); + } + } + pw.println(" Records by Provider:"); + for (Map.Entry<String, ArrayList<UpdateRecord>> i + : mRecordsByProvider.entrySet()) { + pw.println(" " + i.getKey() + ":"); + for (UpdateRecord j : i.getValue()) { + pw.println(" " + j + ":"); + j.dump(pw, " "); + } + } + pw.println(" Locations by Provider:"); + for (Map.Entry<String, Location> i + : mLocationsByProvider.entrySet()) { + pw.println(" " + i.getKey() + ":"); + i.getValue().dump(new PrintWriterPrinter(pw), " "); + } + pw.println(" Last Known Locations:"); + for (Map.Entry<String, Location> i + : mLastKnownLocation.entrySet()) { + pw.println(" " + i.getKey() + ":"); + i.getValue().dump(new PrintWriterPrinter(pw), " "); + } + if (mProximityAlerts.size() > 0) { + pw.println(" Proximity Alerts:"); + for (Map.Entry<PendingIntent, ProximityAlert> i + : mProximityAlerts.entrySet()) { + pw.println(" " + i.getKey() + ":"); + i.getValue().dump(pw, " "); + } + } + if (mProximitiesEntered.size() > 0) { + pw.println(" Proximities Entered:"); + for (ProximityAlert i : mProximitiesEntered) { + pw.println(" " + i + ":"); + i.dump(pw, " "); + } + } + pw.println(" mProximityListener=" + mProximityListener); + if (mEnabledProviders.size() > 0) { + pw.println(" Enabled Providers:"); + for (String i : mEnabledProviders) { + pw.println(" " + i); + } + + } + if (mDisabledProviders.size() > 0) { + pw.println(" Disabled Providers:"); + for (String i : mDisabledProviders) { + pw.println(" " + i); + } + + } + if (mMockProviders.size() > 0) { + pw.println(" Mock Providers:"); + for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) { + pw.println(" " + i.getKey() + " -> " + i.getValue()); + } + } + if (mMockProviderLocation.size() > 0) { + pw.println(" Mock Provider Location:"); + for (Map.Entry<String, Location> i : mMockProviderLocation.entrySet()) { + pw.println(" " + i.getKey() + ":"); + i.getValue().dump(new PrintWriterPrinter(pw), " "); + } + } + if (mMockProviderStatus.size() > 0) { + pw.println(" Mock Provider Status:"); + for (Map.Entry<String, Integer> i : mMockProviderStatus.entrySet()) { + pw.println(" " + i.getKey() + " -> 0x" + + Integer.toHexString(i.getValue())); + } + } + if (mMockProviderStatusExtras.size() > 0) { + pw.println(" Mock Provider Status Extras:"); + for (Map.Entry<String, Bundle> i : mMockProviderStatusExtras.entrySet()) { + pw.println(" " + i.getKey() + " -> " + i.getValue()); + } + } + if (mMockProviderStatusUpdateTime.size() > 0) { + pw.println(" Mock Provider Status Update Time:"); + for (Map.Entry<String, Long> i : mMockProviderStatusUpdateTime.entrySet()) { + pw.println(" " + i.getKey() + " -> " + i.getValue()); + } + } + pw.println(" Reported GPS UIDs @ seq " + mReportedGpsSeq + ":"); + N = mReportedGpsUids.size(); + for (int i=0; i<N; i++) { + pw.println(" UID " + mReportedGpsUids.keyAt(i) + + " seq=" + mReportedGpsUids.valueAt(i)); + } + } + } } diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java index 40d1b72..2e430c8 100644 --- a/services/java/com/android/server/MountListener.java +++ b/services/java/com/android/server/MountListener.java @@ -30,7 +30,7 @@ import java.io.OutputStream; import java.net.Socket; /** - * Thread for communicating with the mount service daemon via a local socket. + * Thread for communicating with the vol service daemon via a local socket. * Events received from the daemon are passed to the MountService instance, * and the MountService instance calls MountListener to send commands to the daemon. */ @@ -38,39 +38,43 @@ final class MountListener implements Runnable { private static final String TAG = "MountListener"; - // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/mountd/mountd.h + // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/vold/ - // socket name for connecting to mountd - private static final String MOUNTD_SOCKET = "mountd"; + // socket name for connecting to vold + private static final String VOLD_SOCKET = "vold"; - // mountd commands - private static final String MOUNTD_ENABLE_UMS = "enable_ums"; - private static final String MOUNTD_DISABLE_UMS = "disable_ums"; - private static final String MOUNTD_SEND_STATUS = "send_status"; - private static final String MOUNTD_MOUNT_MEDIA = "mount_media:"; - private static final String MOUNTD_EJECT_MEDIA = "eject_media:"; + // vold commands + private static final String VOLD_CMD_ENABLE_UMS = "enable_ums"; + private static final String VOLD_CMD_DISABLE_UMS = "disable_ums"; + private static final String VOLD_CMD_SEND_UMS_STATUS = "send_ums_status"; + private static final String VOLD_CMD_MOUNT_VOLUME = "mount_volume:"; + private static final String VOLD_CMD_EJECT_MEDIA = "eject_media:"; + private static final String VOLD_CMD_FORMAT_MEDIA = "format_media:"; - // mountd events - private static final String MOUNTD_UMS_ENABLED = "ums_enabled"; - private static final String MOUNTD_UMS_DISABLED = "ums_disabled"; - private static final String MOUNTD_UMS_CONNECTED = "ums_connected"; - private static final String MOUNTD_UMS_DISCONNECTED = "ums_disconnected"; - private static final String MOUNTD_MEDIA_REMOVED = "media_removed:"; - private static final String MOUNTD_MEDIA_UNMOUNTED = "media_unmounted:"; - private static final String MOUNTD_MEDIA_MOUNTED = "media_mounted:"; - private static final String MOUNTD_MEDIA_MOUNTED_READ_ONLY = "media_mounted_ro:"; - private static final String MOUNTD_MEDIA_SHARED = "media_shared:"; - private static final String MOUNTD_MEDIA_BAD_REMOVAL = "media_bad_removal:"; - private static final String MOUNTD_MEDIA_UNMOUNTABLE = "media_unmountable:"; - private static final String MOUNTD_REQUEST_EJECT = "request_eject:"; + // vold events + private static final String VOLD_EVT_UMS_ENABLED = "ums_enabled"; + private static final String VOLD_EVT_UMS_DISABLED = "ums_disabled"; + private static final String VOLD_EVT_UMS_CONNECTED = "ums_connected"; + private static final String VOLD_EVT_UMS_DISCONNECTED = "ums_disconnected"; + + private static final String VOLD_EVT_NOMEDIA = "volume_nomedia:"; + private static final String VOLD_EVT_UNMOUNTED = "volume_unmounted:"; + private static final String VOLD_EVT_MOUNTED = "volume_mounted:"; + private static final String VOLD_EVT_MOUNTED_RO = "volume_mounted_ro:"; + private static final String VOLD_EVT_UMS = "volume_ums"; + private static final String VOLD_EVT_BAD_REMOVAL = "volume_badremoval:"; + private static final String VOLD_EVT_DAMAGED = "volume_damaged:"; + private static final String VOLD_EVT_CHECKING = "volume_checking:"; + private static final String VOLD_EVT_NOFS = "volume_nofs:"; + private static final String VOLD_EVT_EJECTING = "volume_ejecting:"; /** - * MountService that handles events received from the mount service daemon + * MountService that handles events received from the vol service daemon */ private MountService mService; /** - * Stream for sending commands to the mount service daemon. + * Stream for sending commands to the vol service daemon. */ private OutputStream mOutputStream; @@ -95,9 +99,9 @@ final class MountListener implements Runnable { } /** - * Process and dispatches events received from the mount service daemon + * Process and dispatches events received from the vol service daemon * - * @param event An event received from the mount service daemon + * @param event An event received from the vol service daemon */ private void handleEvent(String event) { if (Config.LOGD) Log.d(TAG, "handleEvent " + event); @@ -105,34 +109,38 @@ final class MountListener implements Runnable { int colonIndex = event.indexOf(':'); String path = (colonIndex > 0 ? event.substring(colonIndex + 1) : null); - if (event.equals(MOUNTD_UMS_ENABLED)) { + if (event.equals(VOLD_EVT_UMS_ENABLED)) { mUmsEnabled = true; - } else if (event.equals(MOUNTD_UMS_DISABLED)) { + } else if (event.equals(VOLD_EVT_UMS_DISABLED)) { mUmsEnabled = false; - } else if (event.equals(MOUNTD_UMS_CONNECTED)) { + } else if (event.equals(VOLD_EVT_UMS_CONNECTED)) { mUmsConnected = true; mService.notifyUmsConnected(); - } else if (event.equals(MOUNTD_UMS_DISCONNECTED)) { + } else if (event.equals(VOLD_EVT_UMS_DISCONNECTED)) { mUmsConnected = false; mService.notifyUmsDisconnected(); - } else if (event.startsWith(MOUNTD_MEDIA_REMOVED)) { + } else if (event.startsWith(VOLD_EVT_NOMEDIA)) { mService.notifyMediaRemoved(path); - } else if (event.startsWith(MOUNTD_MEDIA_UNMOUNTED)) { + } else if (event.startsWith(VOLD_EVT_UNMOUNTED)) { mService.notifyMediaUnmounted(path); - } else if (event.startsWith(MOUNTD_MEDIA_MOUNTED)) { + } else if (event.startsWith(VOLD_EVT_CHECKING)) { + mService.notifyMediaChecking(path); + } else if (event.startsWith(VOLD_EVT_NOFS)) { + mService.notifyMediaNoFs(path); + } else if (event.startsWith(VOLD_EVT_MOUNTED)) { mService.notifyMediaMounted(path, false); - } else if (event.startsWith(MOUNTD_MEDIA_MOUNTED_READ_ONLY)) { + } else if (event.startsWith(VOLD_EVT_MOUNTED_RO)) { mService.notifyMediaMounted(path, true); - } else if (event.startsWith(MOUNTD_MEDIA_SHARED)) { + } else if (event.startsWith(VOLD_EVT_UMS)) { mService.notifyMediaShared(path); - } else if (event.startsWith(MOUNTD_MEDIA_BAD_REMOVAL)) { + } else if (event.startsWith(VOLD_EVT_BAD_REMOVAL)) { mService.notifyMediaBadRemoval(path); // also send media eject intent, to notify apps to close any open // files on the media. mService.notifyMediaEject(path); - } else if (event.startsWith(MOUNTD_MEDIA_UNMOUNTABLE)) { + } else if (event.startsWith(VOLD_EVT_DAMAGED)) { mService.notifyMediaUnmountable(path); - } else if (event.startsWith(MOUNTD_REQUEST_EJECT)) { + } else if (event.startsWith(VOLD_EVT_EJECTING)) { mService.notifyMediaEject(path); } } @@ -156,7 +164,7 @@ final class MountListener implements Runnable { private void writeCommand2(String command, String argument) { synchronized (this) { if (mOutputStream == null) { - Log.e(TAG, "No connection to mountd", new IllegalStateException()); + Log.e(TAG, "No connection to vold", new IllegalStateException()); } else { StringBuilder builder = new StringBuilder(command); if (argument != null) { @@ -183,7 +191,7 @@ final class MountListener implements Runnable { try { socket = new LocalSocket(); - LocalSocketAddress address = new LocalSocketAddress(MOUNTD_SOCKET, + LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET, LocalSocketAddress.Namespace.RESERVED); socket.connect(address); @@ -193,7 +201,7 @@ final class MountListener implements Runnable { byte[] buffer = new byte[100]; - writeCommand(MOUNTD_SEND_STATUS); + writeCommand(VOLD_CMD_SEND_UMS_STATUS); while (true) { int count = inputStream.read(buffer); @@ -242,7 +250,7 @@ final class MountListener implements Runnable { * create tons of throwaway LocalSockets, making * system_server GC constantly. */ - Log.e(TAG, "Failed to connect to mountd", new IllegalStateException()); + Log.e(TAG, "Failed to connect to vold", new IllegalStateException()); SystemClock.sleep(2000); } @@ -283,7 +291,7 @@ final class MountListener implements Runnable { * @param enable true to enable USB mass storage support */ void setMassStorageEnabled(boolean enable) { - writeCommand(enable ? MOUNTD_ENABLE_UMS : MOUNTD_DISABLE_UMS); + writeCommand(enable ? VOLD_CMD_ENABLE_UMS : VOLD_CMD_DISABLE_UMS); } /** @@ -297,14 +305,20 @@ final class MountListener implements Runnable { * Mount media at given mount point. */ public void mountMedia(String mountPoint) { - writeCommand2(MOUNTD_MOUNT_MEDIA, mountPoint); + writeCommand2(VOLD_CMD_MOUNT_VOLUME, mountPoint); } /** * Unmount media at given mount point. */ public void ejectMedia(String mountPoint) { - writeCommand2(MOUNTD_EJECT_MEDIA, mountPoint); + writeCommand2(VOLD_CMD_EJECT_MEDIA, mountPoint); } -} + /** + * Format media at given mount point. + */ + public void formatMedia(String mountPoint) { + writeCommand2(VOLD_CMD_FORMAT_MEDIA, mountPoint); + } +} diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 002ebed..f81c519 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -19,15 +19,19 @@ package com.android.server; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; import android.os.IMountService; import android.os.Environment; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UEventObserver; +import android.text.TextUtils; import android.util.Log; import java.io.File; @@ -52,24 +56,34 @@ class MountService extends IMountService.Stub { private MountListener mListener; /** - * The notification that is shown when USB is connected. It leads the user - * to a dialog to enable mass storage mode. + * The notification that is shown when a USB mass storage host + * is connected. * <p> - * This is lazily created, so use {@link #getUsbStorageNotification()}. + * This is lazily created, so use {@link #setUsbStorageNotification()}. */ private Notification mUsbStorageNotification; - private class SdDoorListener extends UEventObserver { - static final String SD_DOOR_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/sd-door"; - static final String SD_DOOR_SWITCH_NAME = "sd-door"; - public void onUEvent(UEvent event) { - if (SD_DOOR_SWITCH_NAME.equals(event.get("SWITCH_NAME"))) { - sdDoorStateChanged(event.get("SWITCH_STATE")); - } - } - }; + /** + * The notification that is shown when the following media events occur: + * - Media is being checked + * - Media is blank (or unknown filesystem) + * - Media is corrupt + * - Media is safe to unmount + * - Media is missing + * <p> + * This is lazily created, so use {@link #setMediaStorageNotification()}. + */ + private Notification mMediaStorageNotification; + private boolean mShowSafeUnmountNotificationWhenUnmounted; + + private boolean mPlaySounds; + + private boolean mMounted; + + private boolean mAutoStartUms; + /** * Constructs a new MountService instance * @@ -77,13 +91,30 @@ class MountService extends IMountService.Stub { */ public MountService(Context context) { mContext = context; + + // Register a BOOT_COMPLETED handler so that we can start + // MountListener. We defer the startup so that we don't + // start processing events before we ought-to + mContext.registerReceiver(mBroadcastReceiver, + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + mListener = new MountListener(this); - Thread thread = new Thread(mListener, MountListener.class.getName()); - thread.start(); - SdDoorListener sdDoorListener = new SdDoorListener(); - sdDoorListener.startObserving(SdDoorListener.SD_DOOR_UEVENT_MATCH); + mShowSafeUnmountNotificationWhenUnmounted = false; + + mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1"); + + mAutoStartUms = SystemProperties.get("persist.service.mount.umsauto", "0").equals("1"); } + BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { + Thread thread = new Thread(mListener, MountListener.class.getName()); + thread.start(); + } + } + }; + /** * @return true if USB mass storage support is enabled. */ @@ -129,19 +160,119 @@ class MountService extends IMountService.Stub { throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); } + // Set a flag so that when we get the unmounted event, we know + // to display the notification + mShowSafeUnmountNotificationWhenUnmounted = true; + // tell mountd to unmount the media mListener.ejectMedia(mountPath); } /** + * Attempt to format external media + */ + public void formatMedia(String formatPath) throws RemoteException { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission"); + } + + mListener.formatMedia(formatPath); + } + + /** + * Returns true if we're playing media notification sounds. + */ + public boolean getPlayNotificationSounds() { + return mPlaySounds; + } + + /** + * Set whether or not we're playing media notification sounds. + */ + public void setPlayNotificationSounds(boolean enabled) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires WRITE_SETTINGS permission"); + } + mPlaySounds = enabled; + SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0")); + } + + /** + * Returns true if we auto-start UMS on cable insertion. + */ + public boolean getAutoStartUms() { + return mAutoStartUms; + } + + /** + * Set whether or not we're playing media notification sounds. + */ + public void setAutoStartUms(boolean enabled) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires WRITE_SETTINGS permission"); + } + mAutoStartUms = enabled; + SystemProperties.set("persist.service.mount.umsauto", (enabled ? "1" : "0")); + } + + /** + * Update the state of the USB mass storage notification + */ + void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) { + + try { + + if (getMassStorageConnected() && !suppressIfConnected) { + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + setUsbStorageNotification( + com.android.internal.R.string.usb_storage_notification_title, + com.android.internal.R.string.usb_storage_notification_message, + com.android.internal.R.drawable.stat_sys_data_usb, + sound, true, pi); + } else { + setUsbStorageNotification(0, 0, 0, false, false, null); + } + } catch (RemoteException e) { + // Nothing to do + } + } + + void handlePossibleExplicitUnmountBroadcast(String path) { + if (mMounted) { + mMounted = false; + Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, + Uri.parse("file://" + path)); + mContext.sendBroadcast(intent); + } + } + + /** * Broadcasts the USB mass storage connected event to all clients. */ void notifyUmsConnected() { String storageState = Environment.getExternalStorageState(); if (!storageState.equals(Environment.MEDIA_REMOVED) && - !storageState.equals(Environment.MEDIA_BAD_REMOVAL)) { - setUsbStorageNotificationVisibility(true); + !storageState.equals(Environment.MEDIA_BAD_REMOVAL) && + !storageState.equals(Environment.MEDIA_CHECKING)) { + + if (mAutoStartUms) { + try { + setMassStorageEnabled(true); + } catch (RemoteException e) { + } + } else { + updateUsbMassStorageNotification(false, true); + } } + Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED); mContext.sendBroadcast(intent); } @@ -150,7 +281,7 @@ class MountService extends IMountService.Stub { * Broadcasts the USB mass storage disconnected event to all clients. */ void notifyUmsDisconnected() { - setUsbStorageNotificationVisibility(false); + updateUsbMassStorageNotification(false, false); Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); mContext.sendBroadcast(intent); } @@ -159,6 +290,15 @@ class MountService extends IMountService.Stub { * Broadcasts the media removed event to all clients. */ void notifyMediaRemoved(String path) { + updateUsbMassStorageNotification(true, false); + + 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, + true, false, null); + handlePossibleExplicitUnmountBroadcast(path); + Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); @@ -168,18 +308,68 @@ class MountService extends IMountService.Stub { * Broadcasts the media unmounted event to all clients. */ void notifyMediaUnmounted(String path) { + if (mShowSafeUnmountNotificationWhenUnmounted) { + 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, + true, true, null); + mShowSafeUnmountNotificationWhenUnmounted = false; + } else { + setMediaStorageNotification(0, 0, 0, false, false, null); + } + updateUsbMassStorageNotification(false, false); + Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); } /** + * Broadcasts the media checking event to all clients. + */ + void notifyMediaChecking(String path) { + 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, + true, false, null); + + updateUsbMassStorageNotification(true, false); + Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, + Uri.parse("file://" + path)); + mContext.sendBroadcast(intent); + } + + /** + * Broadcasts the media nofs event to all clients. + */ + void notifyMediaNoFs(String path) { + + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title, + com.android.internal.R.string.ext_media_nofs_notification_message, + com.android.internal.R.drawable.stat_sys_no_sim, + true, false, pi); + updateUsbMassStorageNotification(false, false); + intent = new Intent(Intent.ACTION_MEDIA_NOFS, + Uri.parse("file://" + path)); + mContext.sendBroadcast(intent); + } + + /** * Broadcasts the media mounted event to all clients. */ void notifyMediaMounted(String path, boolean readOnly) { + setMediaStorageNotification(0, 0, 0, false, false, null); + updateUsbMassStorageNotification(false, false); Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path)); intent.putExtra("read-only", readOnly); + mMounted = true; mContext.sendBroadcast(intent); } @@ -187,7 +377,15 @@ class MountService extends IMountService.Stub { * Broadcasts the media shared event to all clients. */ void notifyMediaShared(String path) { - Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED, + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title, + com.android.internal.R.string.usb_storage_stop_notification_message, + com.android.internal.R.drawable.stat_sys_warning, + false, true, pi); + handlePossibleExplicitUnmountBroadcast(path); + intent = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); } @@ -196,16 +394,39 @@ class MountService extends IMountService.Stub { * Broadcasts the media bad removal event to all clients. */ void notifyMediaBadRemoval(String path) { + updateUsbMassStorageNotification(true, false); + setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title, + com.android.internal.R.string.ext_media_badremoval_notification_message, + com.android.internal.R.drawable.stat_sys_warning, + true, true, null); + + handlePossibleExplicitUnmountBroadcast(path); Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); + + intent = new Intent(Intent.ACTION_MEDIA_REMOVED, + Uri.parse("file://" + path)); + mContext.sendBroadcast(intent); } /** * Broadcasts the media unmountable event to all clients. */ void notifyMediaUnmountable(String path) { - Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title, + com.android.internal.R.string.ext_media_unmountable_notification_message, + com.android.internal.R.drawable.stat_sys_no_sim, + true, false, pi); + updateUsbMassStorageNotification(false, false); + + handlePossibleExplicitUnmountBroadcast(path); + + intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); } @@ -219,90 +440,132 @@ class MountService extends IMountService.Stub { mContext.sendBroadcast(intent); } - private void sdDoorStateChanged(String doorState) { - File directory = Environment.getExternalStorageDirectory(); - String storageState = Environment.getExternalStorageState(); - - if (directory != null) { - try { - if (doorState.equals("open") && (storageState.equals(Environment.MEDIA_MOUNTED) || - storageState.equals(Environment.MEDIA_MOUNTED_READ_ONLY))) { - // request SD card unmount if SD card door is opened - unmountMedia(directory.getPath()); - } else if (doorState.equals("closed") && storageState.equals(Environment.MEDIA_UNMOUNTED)) { - // attempt to remount SD card - mountMedia(directory.getPath()); - } - } catch (RemoteException e) { - // Nothing to do. - } - } - } - /** - * Sets the visibility of the USB storage notification. This should be - * called when a USB cable is connected and also when it is disconnected. - * - * @param visible Whether to show or hide the notification. + * Sets the USB storage notification. */ - private void setUsbStorageNotificationVisibility(boolean visible) { + private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible, + PendingIntent pi) { + + if (!visible && mUsbStorageNotification == null) { + return; + } + NotificationManager notificationManager = (NotificationManager) mContext .getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { return; } - - /* - * The convention for notification IDs is to use the icon's resource ID - * when the icon is only used by a single notification type, which is - * the case here. - */ - Notification notification = getUsbStorageNotification(); - final int notificationId = notification.icon; if (visible) { - notificationManager.notify(notificationId, notification); + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleId); + CharSequence message = r.getText(messageId); + + if (mUsbStorageNotification == null) { + mUsbStorageNotification = new Notification(); + mUsbStorageNotification.icon = icon; + mUsbStorageNotification.when = 0; + } + + if (sound && mPlaySounds) { + mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; + } else { + mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + } + + mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + + mUsbStorageNotification.tickerText = title; + if (pi == null) { + Intent intent = new Intent(); + pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); + } + + final int notificationId = mUsbStorageNotification.icon; + if (visible) { + notificationManager.notify(notificationId, mUsbStorageNotification); } else { notificationManager.cancel(notificationId); } } + private synchronized boolean getMediaStorageNotificationDismissable() { + if ((mMediaStorageNotification != null) && + ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == + Notification.FLAG_AUTO_CANCEL)) + return true; + + return false; + } + /** - * Gets the USB storage notification. - * - * @return A {@link Notification} that leads to the dialog to enable USB storage. + * Sets the media storage notification. */ - private synchronized Notification getUsbStorageNotification() { - Resources r = Resources.getSystem(); - CharSequence title = - r.getText(com.android.internal.R.string.usb_storage_notification_title); - CharSequence message = - r.getText(com.android.internal.R.string.usb_storage_notification_message); - - if (mUsbStorageNotification == null) { - mUsbStorageNotification = new Notification(); - mUsbStorageNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb; - mUsbStorageNotification.when = 0; - mUsbStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; - mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; + private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, + boolean dismissable, PendingIntent pi) { + + if (!visible && mMediaStorageNotification == null) { + return; } - mUsbStorageNotification.tickerText = title; - mUsbStorageNotification.setLatestEventInfo(mContext, title, message, - getUsbStorageDialogIntent()); + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); - return mUsbStorageNotification; - } + if (notificationManager == null) { + return; + } + + if (mMediaStorageNotification != null && visible) { + /* + * Dismiss the previous notification - we're about to + * re-use it. + */ + final int notificationId = mMediaStorageNotification.icon; + notificationManager.cancel(notificationId); + } + + if (visible) { + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleId); + CharSequence message = r.getText(messageId); + + if (mMediaStorageNotification == null) { + mMediaStorageNotification = new Notification(); + mMediaStorageNotification.when = 0; + } + + if (mPlaySounds) { + mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND; + } else { + mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + } + + if (dismissable) { + mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; + } else { + mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + } + + mMediaStorageNotification.tickerText = title; + if (pi == null) { + Intent intent = new Intent(); + pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + mMediaStorageNotification.icon = icon; + mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); + } - /** - * Creates a pending intent to start the USB storage activity. - * - * @return A {@link PendingIntent} that start the USB storage activity. - */ - private PendingIntent getUsbStorageDialogIntent() { - Intent intent = new Intent(); - intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); - return PendingIntent.getActivity(mContext, 0, intent, 0); + final int notificationId = mMediaStorageNotification.icon; + if (visible) { + notificationManager.notify(notificationId, mMediaStorageNotification); + } else { + notificationManager.cancel(notificationId); + } } } diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java index 11dbe63..1ea0bac 100644 --- a/services/java/com/android/server/NetStatService.java +++ b/services/java/com/android/server/NetStatService.java @@ -26,20 +26,35 @@ public class NetStatService extends INetStatService.Stub { } - public int getTxPackets() { - return NetStat.netStatGetTxPkts(); + public long getMobileTxPackets() { + return NetStat.getMobileTxPkts(); } - public int getRxPackets() { - return NetStat.netStatGetRxPkts(); + public long getMobileRxPackets() { + return NetStat.getMobileRxPkts(); } - public int getTxBytes() { - return NetStat.netStatGetTxBytes(); + public long getMobileTxBytes() { + return NetStat.getMobileTxBytes(); } - public int getRxBytes() { - return NetStat.netStatGetRxBytes(); + public long getMobileRxBytes() { + return NetStat.getMobileRxBytes(); } + public long getTotalTxPackets() { + return NetStat.getTotalTxPkts(); + } + + public long getTotalRxPackets() { + return NetStat.getTotalRxPkts(); + } + + public long getTotalTxBytes() { + return NetStat.getTotalTxBytes(); + } + + public long getTotalRxBytes() { + return NetStat.getTotalRxBytes(); + } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index eb9ebe9..bc4b169 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -311,6 +311,17 @@ class NotificationManagerService extends INotificationManager.Stub mBatteryFull = batteryFull; updateLights(); } + } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) + || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) { + Uri uri = intent.getData(); + if (uri == null) { + return; + } + String pkgName = uri.getSchemeSpecificPart(); + if (pkgName == null) { + return; + } + cancelAllNotifications(pkgName); } } }; @@ -331,6 +342,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_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); mContext.registerReceiver(mIntentReceiver, filter); } @@ -865,7 +878,7 @@ class NotificationManagerService extends INotificationManager.Stub // ====================================================================== @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump NotificationManager from from pid=" + Binder.getCallingPid() diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 58ad426..9f428e9 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -97,6 +97,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; @@ -983,6 +984,20 @@ class PackageManagerService extends IPackageManager.Stub { } return null; } + + public String[] getSystemSharedLibraryNames() { + 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; + } + return null; + } public int checkPermission(String permName, String pkgName) { synchronized (mPackages) { @@ -1813,7 +1828,7 @@ class PackageManagerService extends IPackageManager.Stub { } return true; } - + private PackageParser.Package scanPackageLI( File scanFile, File destCodeFile, File destResourceFile, PackageParser.Package pkg, int parseFlags, int scanMode) { @@ -1910,19 +1925,18 @@ class PackageManagerService extends IPackageManager.Stub { } } + // Just create the setting, don't add it yet pkgSetting = mSettings.getPackageLP(pkg, suid, destCodeFile, - destResourceFile, pkg.applicationInfo.flags, true); + destResourceFile, pkg.applicationInfo.flags, true, false); if (pkgSetting == null) { Log.w(TAG, "Creating application package " + pkgName + " failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } - synchronized(mPackages) { - if(mSettings.mDisabledSysPackages.get(pkg.packageName) != null) { - pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - } + if(mSettings.mDisabledSysPackages.get(pkg.packageName) != null) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } - + pkg.applicationInfo.uid = pkgSetting.userId; pkg.mExtras = pkgSetting; @@ -1970,24 +1984,12 @@ class PackageManagerService extends IPackageManager.Stub { long scanFileTime = scanFile.lastModified(); final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0; final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.getTimeStamp(); - - // At this point we know it is okay to accept the package, though - // errors can still happen as we try to install... - - if ((scanMode&SCAN_MONITOR) != 0) { - pkg.mPath = destCodeFile.getAbsolutePath(); - mAppDirs.put(pkg.mPath, pkg); - } pkg.applicationInfo.processName = fixProcessName( pkg.applicationInfo.packageName, pkg.applicationInfo.processName, pkg.applicationInfo.uid); pkg.applicationInfo.publicSourceDir = pkgSetting.resourcePathString; - synchronized (mPackages) { - mPackages.put(pkg.applicationInfo.packageName, pkg); - } - File dataPath; if (mPlatformPackage == pkg) { // The system package is special. @@ -2030,8 +2032,7 @@ class PackageManagerService extends IPackageManager.Stub { return null; } } - - } + } if (!recovered) { mHasSystemUidErrors = true; } @@ -2063,7 +2064,7 @@ class PackageManagerService extends IPackageManager.Stub { int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if(ret < 0) { - //error from installer + // Error from installer mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } @@ -2133,8 +2134,17 @@ class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST; } + if ((scanMode&SCAN_MONITOR) != 0) { + pkg.mPath = destCodeFile.getAbsolutePath(); + mAppDirs.put(pkg.mPath, pkg); + } + synchronized (mPackages) { - + // We don't expect installation to fail beyond this point + // Add the new setting to mSettings + mSettings.insertPackageSettingLP(pkgSetting, pkg.packageName, suid); + // Add the new setting to mPackages + mPackages.put(pkg.applicationInfo.packageName, pkg); int N = pkg.providers.size(); StringBuilder r = null; int i; @@ -2678,7 +2688,7 @@ class PackageManagerService extends IPackageManager.Stub { grantPermissionsLP(pkg, false); } } - + private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) { final PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps == null) { @@ -2724,7 +2734,19 @@ class PackageManagerService extends IPackageManager.Stub { == PackageManager.SIGNATURE_MATCH); if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { - allowed = true; + // For updated system applications, the signatureOrSystem permission + // is granted only if it had been defined by the original application. + if ((pkg.applicationInfo.flags + & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + PackageSetting sysPs = mSettings.getDisabledSystemPkg(pkg.packageName); + if(sysPs.grantedPermissions.contains(perm)) { + allowed = true; + } else { + allowed = false; + } + } else { + allowed = true; + } } } } else { @@ -3157,6 +3179,7 @@ class PackageManagerService extends IPackageManager.Stub { if (removedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, removedUid); + extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras); } if (addedPackage != null) { @@ -3169,7 +3192,7 @@ class PackageManagerService extends IPackageManager.Stub { private final String mRootDir; private final boolean mIsRom; } - + /* Called when a downloaded package installation has been confirmed by the user */ public void installPackage( final Uri packageURI, final IPackageInstallObserver observer, final int flags) { @@ -3191,12 +3214,15 @@ class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "Observer no longer exists."); } } - // There appears to be a subtle deadlock condition if the sendPackageBroadcast call appears - // in the synchronized block above. + // There appears to be a subtle deadlock condition if the sendPackageBroadcast + // call appears in the synchronized block above. if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - res.removedInfo.sendBroadcast(); + res.removedInfo.sendBroadcast(false, true); Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); + if (res.removedInfo.removedPackage != null) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, extras); @@ -3217,7 +3243,7 @@ class PackageManagerService extends IPackageManager.Stub { /* * Install a non-existing package. */ - private void installNewPackageLI(String pkgName, int parseFlags, + private void installNewPackageLI(String pkgName, File tmpPackageFile, String destFilePath, File destPackageFile, File destResourceFile, PackageParser.Package pkg, boolean forwardLocked, @@ -3241,7 +3267,7 @@ class PackageManagerService extends IPackageManager.Stub { } mLastScanError = PackageManager.INSTALL_SUCCEEDED; PackageParser.Package newPackage = scanPackageLI(tmpPackageFile, destPackageFile, - destResourceFile, pkg, parseFlags, + destResourceFile, pkg, 0, SCAN_MONITOR | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); @@ -3273,7 +3299,7 @@ class PackageManagerService extends IPackageManager.Stub { } } - private void replacePackageLI(String pkgName, int parseFlags, + private void replacePackageLI(String pkgName, File tmpPackageFile, String destFilePath, File destPackageFile, File destResourceFile, PackageParser.Package pkg, boolean forwardLocked, @@ -3290,16 +3316,15 @@ class PackageManagerService extends IPackageManager.Stub { boolean sysPkg = ((deletedPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); if(sysPkg) { replaceSystemPackageLI(deletedPackage, - parseFlags, tmpPackageFile, destFilePath, destPackageFile, destResourceFile, pkg, forwardLocked, res); } else { - replaceNonSystemPackageLI(deletedPackage, parseFlags, tmpPackageFile, destFilePath, + replaceNonSystemPackageLI(deletedPackage, tmpPackageFile, destFilePath, destPackageFile, destResourceFile, pkg, forwardLocked, res); } } - private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, int parseFlags, + private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, File tmpPackageFile, String destFilePath, File destPackageFile, File destResourceFile, PackageParser.Package pkg, boolean forwardLocked, @@ -3308,6 +3333,7 @@ class PackageManagerService extends IPackageManager.Stub { String pkgName = deletedPackage.packageName; boolean deletedPkg = true; boolean updatedSettings = false; + int parseFlags = PackageManager.REPLACE_EXISTING_PACKAGE; // First delete the existing package while retaining the data directory if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, res.removedInfo)) { @@ -3383,14 +3409,15 @@ class PackageManagerService extends IPackageManager.Stub { } } - private void replaceSystemPackageLI(PackageParser.Package deletedPackage, int parseFlags, + private void replaceSystemPackageLI(PackageParser.Package deletedPackage, File tmpPackageFile, String destFilePath, File destPackageFile, File destResourceFile, PackageParser.Package pkg, boolean forwardLocked, PackageInstalledInfo res) { PackageParser.Package newPackage = null; boolean updatedSettings = false; - parseFlags |= PackageParser.PARSE_IS_SYSTEM; + int parseFlags = PackageManager.REPLACE_EXISTING_PACKAGE | + PackageParser.PARSE_IS_SYSTEM; String packageName = deletedPackage.packageName; res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; if (packageName == null) { @@ -3534,7 +3561,6 @@ class PackageManagerService extends IPackageManager.Stub { String pkgName = null; boolean forwardLocked = false; boolean replacingExistingPackage = false; - // Result object to be returned PackageInstalledInfo res = new PackageInstalledInfo(); res.returnCode = PackageManager.INSTALL_SUCCEEDED; @@ -3640,13 +3666,13 @@ class PackageManagerService extends IPackageManager.Stub { } if(replacingExistingPackage) { - replacePackageLI(pkgName, pFlags, + replacePackageLI(pkgName, tmpPackageFile, destFilePath, destPackageFile, destResourceFile, pkg, forwardLocked, res); } else { - installNewPackageLI(pkgName, pFlags, + installNewPackageLI(pkgName, tmpPackageFile, destFilePath, destPackageFile, destResourceFile, pkg, forwardLocked, @@ -3842,7 +3868,7 @@ class PackageManagerService extends IPackageManager.Stub { } if(res && sendBroadCast) { - info.sendBroadcast(); + info.sendBroadcast(deleteCodeAndResources, false); } return res; } @@ -3852,9 +3878,13 @@ class PackageManagerService extends IPackageManager.Stub { int uid = -1; int removedUid = -1; - void sendBroadcast() { + void sendBroadcast(boolean fullRemove, boolean replacing) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid); + extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove); + if (replacing) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } if (removedPackage != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras); } @@ -3967,7 +3997,6 @@ class PackageManagerService extends IPackageManager.Stub { File sourceFile = new File(applicationInfo.sourceDir); if (!sourceFile.exists()) { Log.w(TAG, "Package source " + applicationInfo.sourceDir + " does not exist."); - return false; } outInfo.uid = applicationInfo.uid; @@ -4463,6 +4492,10 @@ class PackageManagerService extends IPackageManager.Stub { mSystemReady = true; } + public boolean isSafeMode() { + return mSafeMode; + } + public boolean hasSystemUidErrors() { return mHasSystemUidErrors; } @@ -4482,7 +4515,7 @@ class PackageManagerService extends IPackageManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump ActivityManager from from pid=" + Binder.getCallingPid() @@ -5279,10 +5312,10 @@ class PackageManagerService extends IPackageManager.Stub { PackageSetting getPackageLP(PackageParser.Package pkg, SharedUserSetting sharedUser, File codePath, File resourcePath, - int pkgFlags, boolean create) { + int pkgFlags, boolean create, boolean add) { final String name = pkg.packageName; PackageSetting p = getPackageLP(name, sharedUser, codePath, - resourcePath, pkgFlags, create); + resourcePath, pkgFlags, create, add); if (p != null) { p.pkg = pkg; @@ -5413,7 +5446,7 @@ class PackageManagerService extends IPackageManager.Stub { private PackageSetting getPackageLP(String name, SharedUserSetting sharedUser, File codePath, File resourcePath, - int pkgFlags, boolean create) { + int pkgFlags, boolean create, boolean add) { PackageSetting p = mPackages.get(name); if (p != null) { if (!p.codePath.equals(codePath)) { @@ -5427,6 +5460,12 @@ class PackageManagerService extends IPackageManager.Stub { // has to delete the one installed in the data partition in order to pick up the // new system package. return p; + } else if ((p.pkg != null) && (p.pkg.applicationInfo != null) && + ((p.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) { + // Check for non-system apps + reportSettingsProblem(Log.WARN, + "Package " + name + " codePath changed from " + p.codePath + + " to " + codePath + "; Retaining data and using new code"); } else { reportSettingsProblem(Log.WARN, "Package " + name + " codePath changed from " + p.codePath @@ -5458,15 +5497,25 @@ class PackageManagerService extends IPackageManager.Stub { } else { p.userId = FIRST_APPLICATION_UID; } - if (p.userId < 0) { reportSettingsProblem(Log.WARN, "Package " + name + " could not be assigned a valid uid"); return null; } - mPackages.put(name, p); + if (add) { + // Finish adding new package by adding it and updating shared + // user preferences + insertPackageSettingLP(p, name, sharedUser); + } } - + return p; + } + + // Utility method that adds a PackageSetting to mPackages and + // completes updating the shared user attributes + private void insertPackageSettingLP(PackageSetting p, String name, + SharedUserSetting sharedUser) { + mPackages.put(name, p); if (sharedUser != null) { if (p.sharedUser != null && p.sharedUser != sharedUser) { reportSettingsProblem(Log.ERROR, @@ -5476,17 +5525,16 @@ class PackageManagerService extends IPackageManager.Stub { p.sharedUser.packages.remove(p); } else if (p.userId != sharedUser.userId) { reportSettingsProblem(Log.ERROR, - "Package " + p.name + " was user id " + p.userId - + " but is now user " + sharedUser - + " with id " + sharedUser.userId - + "; I am not changing its files so it will probably fail!"); + "Package " + p.name + " was user id " + p.userId + + " but is now user " + sharedUser + + " with id " + sharedUser.userId + + "; I am not changing its files so it will probably fail!"); } sharedUser.packages.add(p); p.sharedUser = sharedUser; p.userId = sharedUser.userId; } - return p; } private void updateSharedUserPerms (PackageSetting deletedPs) { @@ -5637,23 +5685,8 @@ class PackageManagerService extends IPackageManager.Stub { } for (PackageSetting pkg : mDisabledSysPackages.values()) { - serializer.startTag(null, "updated-package"); - serializer.attribute(null, "name", pkg.name); - serializer.attribute(null, "codePath", pkg.codePathString); - serializer.attribute(null, "ts", pkg.getTimeStampStr()); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } - if (pkg.sharedUser == null) { - serializer.attribute(null, "userId", - Integer.toString(pkg.userId)); - } else { - serializer.attribute(null, "sharedUserId", - Integer.toString(pkg.userId)); - } - serializer.endTag(null, "updated-package"); + writeDisabledSysPackage(serializer, pkg); } - serializer.startTag(null, "preferred-packages"); int N = mPreferredPackages.size(); @@ -5716,6 +5749,43 @@ class PackageManagerService extends IPackageManager.Stub { //Debug.stopMethodTracing(); } + void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "updated-package"); + serializer.attribute(null, "name", pkg.name); + serializer.attribute(null, "codePath", pkg.codePathString); + serializer.attribute(null, "ts", pkg.getTimeStampStr()); + if (!pkg.resourcePathString.equals(pkg.codePathString)) { + serializer.attribute(null, "resourcePath", pkg.resourcePathString); + } + if (pkg.sharedUser == null) { + serializer.attribute(null, "userId", + Integer.toString(pkg.userId)); + } else { + serializer.attribute(null, "sharedUserId", + Integer.toString(pkg.userId)); + } + serializer.startTag(null, "perms"); + if (pkg.sharedUser == null) { + // If this is a shared user, the permissions will + // be written there. We still need to write an + // empty permissions list so permissionsFixed will + // be set. + for (final String name : pkg.grantedPermissions) { + BasePermission bp = mPermissions.get(name); + if ((bp != null) && (bp.perm != null) && (bp.perm.info != null)) { + // We only need to write signature or system permissions but this wont + // match the semantics of grantedPermissions. So write all permissions. + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + } + } + serializer.endTag(null, "perms"); + serializer.endTag(null, "updated-package"); + } + void writePackage(XmlSerializer serializer, final PackageSetting pkg) throws java.io.IOException { serializer.startTag(null, "package"); @@ -5892,33 +5962,7 @@ class PackageManagerService extends IPackageManager.Stub { } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLP(parser); } else if(tagName.equals("updated-package")) { - String name = parser.getAttributeValue(null, "name"); - String codePathStr = parser.getAttributeValue(null, "codePath"); - String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); - if(resourcePathStr == null) { - resourcePathStr = codePathStr; - } - - int pkgFlags = 0; - pkgFlags |= ApplicationInfo.FLAG_SYSTEM; - PackageSetting ps = new PackageSetting(name, - new File(codePathStr), - new File(resourcePathStr), pkgFlags); - String timeStampStr = parser.getAttributeValue(null, "ts"); - if (timeStampStr != null) { - try { - long timeStamp = Long.parseLong(timeStampStr); - ps.setTimeStamp(timeStamp, timeStampStr); - } catch (NumberFormatException e) { - } - } - String idStr = parser.getAttributeValue(null, "userId"); - ps.userId = idStr != null ? Integer.parseInt(idStr) : 0; - if(ps.userId <= 0) { - String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); - ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; - } - mDisabledSysPackages.put(name, ps); + readDisabledSysPackageLP(parser); } else { Log.w(TAG, "Unknown element under <packages>: " + parser.getName()); @@ -5945,7 +5989,7 @@ class PackageManagerService extends IPackageManager.Stub { if (idObj != null && idObj instanceof SharedUserSetting) { PackageSetting p = getPackageLP(pp.name, (SharedUserSetting)idObj, pp.codePath, pp.resourcePath, - pp.pkgFlags, true); + pp.pkgFlags, true, true); if (p == null) { Log.w(TAG, "Unable to create application package for " + pp.name); @@ -6056,6 +6100,58 @@ class PackageManagerService extends IPackageManager.Stub { } } + private void readDisabledSysPackageLP(XmlPullParser parser) + throws XmlPullParserException, IOException { + String name = parser.getAttributeValue(null, "name"); + String codePathStr = parser.getAttributeValue(null, "codePath"); + String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); + if(resourcePathStr == null) { + resourcePathStr = codePathStr; + } + + int pkgFlags = 0; + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + PackageSetting ps = new PackageSetting(name, + new File(codePathStr), + new File(resourcePathStr), pkgFlags); + String timeStampStr = parser.getAttributeValue(null, "ts"); + if (timeStampStr != null) { + try { + long timeStamp = Long.parseLong(timeStampStr); + ps.setTimeStamp(timeStamp, timeStampStr); + } catch (NumberFormatException e) { + } + } + String idStr = parser.getAttributeValue(null, "userId"); + ps.userId = idStr != null ? Integer.parseInt(idStr) : 0; + if(ps.userId <= 0) { + String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); + ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; + } + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("perms")) { + readGrantedPermissionsLP(parser, + ps.grantedPermissions); + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <updated-package>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + mDisabledSysPackages.put(name, ps); + } + private void readPackageLP(XmlPullParser parser) throws XmlPullParserException, IOException { String name = null; diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index ca0ad1a..ad30ffc 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -182,6 +182,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage private long mScreenOnTime; private long mScreenOnStartTime; private boolean mPreventScreenOn; + private int mScreenBrightnessOverride = -1; // Used when logging number and duration of touch-down cycles private long mTotalTouchDownTime; @@ -245,7 +246,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage public void acquire() { if (!mRefCounted || mCount++ == 0) { - PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken, mTag); + long ident = Binder.clearCallingIdentity(); + try { + PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken, + MY_UID, mTag); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -486,12 +493,18 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage public void acquireWakeLock(int flags, IBinder lock, String tag) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - synchronized (mLocks) { - acquireWakeLockLocked(flags, lock, tag); + int uid = Binder.getCallingUid(); + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLocks) { + acquireWakeLockLocked(flags, lock, uid, tag); + } + } finally { + Binder.restoreCallingIdentity(ident); } } - public void acquireWakeLockLocked(int flags, IBinder lock, String tag) { + public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) { int acquireUid = -1; String acquireName = null; int acquireType = -1; @@ -504,7 +517,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage WakeLock wl; boolean newlock; if (index < 0) { - wl = new WakeLock(flags, lock, tag, Binder.getCallingUid()); + wl = new WakeLock(flags, lock, tag, uid); switch (wl.flags & LOCK_MASK) { case PowerManager.FULL_WAKE_LOCK: @@ -573,9 +586,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage if (acquireType >= 0) { try { - long origId = Binder.clearCallingIdentity(); mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType); - Binder.restoreCallingIdentity(origId); } catch (RemoteException e) { // Ignore } @@ -627,12 +638,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage releaseType = wl.monitorType; if (releaseType >= 0) { + long origId = Binder.clearCallingIdentity(); try { - long origId = Binder.clearCallingIdentity(); mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType); - Binder.restoreCallingIdentity(origId); } catch (RemoteException e) { // Ignore + } finally { + Binder.restoreCallingIdentity(origId); } } } @@ -757,7 +769,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump PowerManager from from pid=" + Binder.getCallingPid() @@ -790,6 +802,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage + " mUserActivityAllowed=" + mUserActivityAllowed); pw.println(" mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay + " mScreenOffDelay=" + mScreenOffDelay); + pw.println(" mPreventScreenOn=" + mPreventScreenOn + + " mScreenBrightnessOverride=" + mScreenBrightnessOverride); pw.println(" mTotalDelaySetting=" + mTotalDelaySetting); pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock); pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock); @@ -959,7 +973,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage if (mSpew) { Log.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock); } - if (mContext != null) { + if (mContext != null && ActivityManagerNative.isSystemReady()) { mContext.sendOrderedBroadcast(mScreenOnIntent, null, mScreenOnBroadcastDone, mHandler, 0, null, null); } else { @@ -980,7 +994,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage // ignore it. } - if (mContext != null) { + if (mContext != null && ActivityManagerNative.isSystemReady()) { mContext.sendOrderedBroadcast(mScreenOffIntent, null, mScreenOffBroadcastDone, mHandler, 0, null, null); } else { @@ -992,11 +1006,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } else { - synchronized (mLocks) { - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 4, - mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } + // If we're in this case, then this handler is running for a previous + // paired transaction. mBroadcastWakeLock will already have been released + // in sendNotificationLocked. } } }; @@ -1070,7 +1082,6 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage * lock (rather than an IPowerManager call). */ public void preventScreenOn(boolean prevent) { - // TODO: use a totally new permission (separate from DEVICE_POWER) for this? mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); synchronized (mLocks) { @@ -1119,6 +1130,17 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } + public void setScreenBrightnessOverride(int brightness) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + synchronized (mLocks) { + if (mScreenBrightnessOverride != brightness) { + mScreenBrightnessOverride = brightness; + updateLightsLocked(mPowerState, SCREEN_ON_BIT); + } + } + } + /** * Sanity-check that gets called 5 seconds after any call to * preventScreenOn(true). This ensures that the original call @@ -1204,7 +1226,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } if (mPowerState != newState) { - err = updateLightsLocked(newState, becauseOfUser); + err = updateLightsLocked(newState, 0); if (err != 0) { return; } @@ -1232,6 +1254,14 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } if (reallyTurnScreenOn) { err = Power.setScreenState(true); + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteScreenOn(); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e); + } finally { + Binder.restoreCallingIdentity(identity); + } } else { Power.setScreenState(false); // But continue as if we really did turn the screen on... @@ -1250,8 +1280,17 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } else { mScreenOffTime = SystemClock.elapsedRealtime(); + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteScreenOff(); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e); + } finally { + Binder.restoreCallingIdentity(identity); + } + mPowerState &= ~SCREEN_ON_BIT; if (!mScreenBrightness.animating) { - err = turnScreenOffLocked(becauseOfUser); + err = screenOffFinishedAnimating(becauseOfUser); } else { mOffBecauseOfUser = becauseOfUser; err = 0; @@ -1262,24 +1301,25 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } - private int turnScreenOffLocked(boolean becauseOfUser) { - if ((mPowerState&SCREEN_ON_BIT) != 0) { - EventLog.writeEvent(LOG_POWER_SCREEN_STATE, 0, becauseOfUser ? 1 : 0, - mTotalTouchDownTime, mTouchCycles); - mLastTouchDown = 0; - int err = Power.setScreenState(false); + private int screenOffFinishedAnimating(boolean becauseOfUser) { + // I don't think we need to check the current state here because all of these + // Power.setScreenState and sendNotificationLocked can both handle being + // called multiple times in the same state. -joeo + EventLog.writeEvent(LOG_POWER_SCREEN_STATE, 0, becauseOfUser ? 1 : 0, + mTotalTouchDownTime, mTouchCycles); + mLastTouchDown = 0; + int err = Power.setScreenState(false); + if (mScreenOnStartTime != 0) { mScreenOnTime += SystemClock.elapsedRealtime() - mScreenOnStartTime; mScreenOnStartTime = 0; - if (err == 0) { - mPowerState &= ~SCREEN_ON_BIT; - int why = becauseOfUser - ? WindowManagerPolicy.OFF_BECAUSE_OF_USER - : WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT; - sendNotificationLocked(false, why); - } - return err; } - return 0; + if (err == 0) { + int why = becauseOfUser + ? WindowManagerPolicy.OFF_BECAUSE_OF_USER + : WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT; + sendNotificationLocked(false, why); + } + return err; } private boolean batteryIsLow() { @@ -1287,9 +1327,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage mBatteryService.getBatteryLevel() <= Power.LOW_BATTERY_THRESHOLD); } - private int updateLightsLocked(int newState, boolean becauseOfUser) { + private int updateLightsLocked(int newState, int forceState) { int oldState = mPowerState; - int difference = newState ^ oldState; + int difference = (newState ^ oldState) | forceState; if (difference == 0) { return 0; } @@ -1494,7 +1534,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage animating = more; if (!more) { if (mask == Power.SCREEN_LIGHT && curIntValue == Power.BRIGHTNESS_OFF) { - turnScreenOffLocked(mOffBecauseOfUser); + screenOffFinishedAnimating(mOffBecauseOfUser); } } return more; @@ -1521,6 +1561,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage private int getPreferredBrightness() { try { + if (mScreenBrightnessOverride >= 0) { + return mScreenBrightnessOverride; + } final int brightness = Settings.System.getInt(mContext.getContentResolver(), SCREEN_BRIGHTNESS); // Don't let applications turn the screen all the way off diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java index f56088c..b253038 100644 --- a/services/java/com/android/server/SensorService.java +++ b/services/java/com/android/server/SensorService.java @@ -45,7 +45,7 @@ class SensorService extends ISensorService.Stub { private static final int SENSOR_DISABLE = -1; /** - * Battery statistics to be updated when sensors are enabled and diabled. + * Battery statistics to be updated when sensors are enabled and disabled. */ final IBatteryStats mBatteryStats = BatteryStatsService.getService(); @@ -105,9 +105,9 @@ class SensorService extends ISensorService.Stub { return _sensors_control_open(); } - public boolean enableSensor(IBinder binder, int sensor, int enable) + public boolean enableSensor(IBinder binder, String name, int sensor, int enable) throws RemoteException { - if (localLOGV) Log.d(TAG, "enableSensor " + sensor + " " + enable); + if (localLOGV) Log.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable); // Inform battery statistics service of status change int uid = Binder.getCallingUid(); @@ -119,7 +119,10 @@ class SensorService extends ISensorService.Stub { } Binder.restoreCallingIdentity(identity); - if (binder == null) throw new NullPointerException("listener is null in enableSensor"); + if (binder == null) { + Log.w(TAG, "listener is null (sensor=" + name + ", id=" + sensor + ")"); + return false; + } synchronized(mListeners) { if (enable!=SENSOR_DISABLE && !_sensors_control_activate(sensor, true)) { @@ -145,7 +148,11 @@ class SensorService extends ISensorService.Stub { } if (l == null) { - throw new NullPointerException("no Listener object in enableSensor"); + // by construction, this means we're disabling a listener we + // don't know about... + Log.w(TAG, "listener with binder " + binder + + ", doesn't exist (sensor=" + name + ", id=" + sensor + ")"); + return false; } if (minDelay >= 0) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7f7a52e..a629ec6 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -16,35 +16,36 @@ package com.android.server; +import com.android.server.am.ActivityManagerService; +import com.android.server.status.StatusBarService; + +import dalvik.system.PathClassLoader; +import dalvik.system.VMRuntime; + import android.app.ActivityManagerNative; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentService; import android.content.Context; +import android.content.Intent; import android.content.pm.IPackageManager; import android.database.ContentObserver; import android.database.Cursor; import android.media.AudioService; +import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.IBinder; -import android.provider.Settings; import android.provider.Contacts.People; -import android.server.BluetoothDeviceService; +import android.provider.Settings; import android.server.BluetoothA2dpService; -import android.server.checkin.FallbackCheckinService; +import android.server.BluetoothDeviceService; import android.server.search.SearchManagerService; import android.util.EventLog; import android.util.Log; -import dalvik.system.TouchDex; -import dalvik.system.VMRuntime; - -import com.android.server.am.ActivityManagerService; -import com.android.server.status.StatusBarService; - import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -180,13 +181,13 @@ class ServerThread extends Thread { StatusBarService statusBar = null; InputMethodManagerService imm = null; - + AppWidgetService appWidget = null; + if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { try { Log.i(TAG, "Starting Status Bar Service."); statusBar = new StatusBarService(context); ServiceManager.addService("statusbar", statusBar); - com.android.server.status.StatusBarPolicy.installIcons(context, statusBar); } catch (Throwable e) { Log.e(TAG, "Failure starting StatusBarService", e); } @@ -205,7 +206,7 @@ class ServerThread extends Thread { } catch (Throwable e) { Log.e(TAG, "Failure starting Input Manager Service", e); } - + try { Log.i(TAG, "Starting Hardware Service."); ServiceManager.addService("hardware", new HardwareService(context)); @@ -272,9 +273,14 @@ class ServerThread extends Thread { } try { - Log.i(TAG, "Starting Checkin Service"); - addService(context, "checkin", "com.google.android.server.checkin.CheckinService", - FallbackCheckinService.class); + Log.i(TAG, "Starting Checkin Service."); + Intent intent = new Intent().setComponent(new ComponentName( + "com.google.android.server.checkin", + "com.google.android.server.checkin.CheckinService")); + if (context.startService(intent) == null) { + Log.w(TAG, "Using fallback Checkin Service."); + ServiceManager.addService("checkin", new FallbackCheckinService(context)); + } } catch (Throwable e) { Log.e(TAG, "Failure starting Checkin Service", e); } @@ -302,10 +308,17 @@ class ServerThread extends Thread { } try { - Log.i(TAG, "Starting Gadget Service"); - ServiceManager.addService(Context.GADGET_SERVICE, new GadgetService(context)); + Log.i(TAG, "Starting AppWidget Service"); + appWidget = new AppWidgetService(context); + ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget); } catch (Throwable e) { - Log.e(TAG, "Failure starting Gadget Service", e); + Log.e(TAG, "Failure starting AppWidget Service", e); + } + + try { + com.android.server.status.StatusBarPolicy.installIcons(context, statusBar); + } catch (Throwable e) { + Log.e(TAG, "Failure installing status bar icons", e); } } @@ -318,6 +331,7 @@ class ServerThread extends Thread { false, new AdbSettingsObserver()); // It is now time to start up the app processes... + boolean safeMode = wm.detectSafeMode(); if (statusBar != null) { statusBar.systemReady(); } @@ -330,6 +344,9 @@ class ServerThread extends Thread { pm.systemReady(); } catch (RemoteException e) { } + if (appWidget != null) { + appWidget.systemReady(safeMode); + } // After making the following code, third party code may be running... try { @@ -342,51 +359,6 @@ class ServerThread extends Thread { Looper.loop(); Log.d(TAG, "System ServerThread is exiting!"); } - - private void addService(Context context, String name, String serviceClass, - Class<? extends IBinder> fallback) { - - final IBinder service = findService(context, serviceClass, fallback); - if (service != null) { - ServiceManager.addService(name, service); - } else { - Log.e(TAG, "Failure starting service '" + name + "' with class " + serviceClass); - } - } - - private IBinder findService(Context context, String serviceClass, - Class<? extends IBinder> fallback) { - - IBinder service = null; - try { - Class<?> klass = Class.forName(serviceClass); - Constructor<?> c = klass.getConstructor(Context.class); - service = (IBinder) c.newInstance(context); - } catch (ClassNotFoundException e) { - // Ignore - } catch (IllegalAccessException e) { - // Ignore - } catch (NoSuchMethodException e) { - // Ignore - } catch (InvocationTargetException e) { - // Ignore - } catch (InstantiationException e) { - // Ignore - } - - if (service == null && fallback != null) { - Log.w(TAG, "Could not find " + serviceClass + ", trying fallback"); - try { - service = fallback.newInstance(); - } catch (IllegalAccessException e) { - // Ignore - } catch (InstantiationException e) { - // Ignore - } - } - - return service; - } } class DemoThread extends Thread diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index dbaf086..a74915c 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -28,17 +28,20 @@ import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.util.Log; import java.util.ArrayList; import java.io.FileDescriptor; import java.io.PrintWriter; +import com.android.internal.app.IBatteryStats; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.DefaultPhoneNotifier; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneStateIntentReceiver; import com.android.internal.telephony.TelephonyIntents; +import com.android.server.am.BatteryStatsService; /** @@ -55,8 +58,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { int events; } - private Context mContext; - private ArrayList<Record> mRecords = new ArrayList(); + private final Context mContext; + private final ArrayList<Record> mRecords = new ArrayList(); + private final IBatteryStats mBatteryStats; private int mCallState = TelephonyManager.CALL_STATE_IDLE; private String mCallIncomingNumber = ""; @@ -81,6 +85,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { TelephonyRegistry(Context context) { CellLocation.getEmpty().fillInNotifierBundle(mCellLocation); mContext = context; + mBatteryStats = BatteryStatsService.getService(); } public void listen(String pkgForDebug, IPhoneStateListener callback, int events, @@ -183,6 +188,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCallState(int state, String incomingNumber) { + if (!checkPhoneStatePermission("notifyCallState()")) { + return; + } synchronized (mRecords) { mCallState = state; mCallIncomingNumber = incomingNumber; @@ -202,6 +210,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyServiceState(ServiceState state) { + if (!checkPhoneStatePermission("notifyServiceState()")) { + return; + } synchronized (mRecords) { mServiceState = state; final int N = mRecords.size(); @@ -216,6 +227,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifySignalStrength(int signalStrengthASU) { + if (!checkPhoneStatePermission("notifySignalStrength()")) { + return; + } synchronized (mRecords) { mSignalStrength = signalStrengthASU; final int N = mRecords.size(); @@ -234,6 +248,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyMessageWaitingChanged(boolean mwi) { + if (!checkPhoneStatePermission("notifyMessageWaitingChanged()")) { + return; + } synchronized (mRecords) { mMessageWaiting = mwi; final int N = mRecords.size(); @@ -251,6 +268,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCallForwardingChanged(boolean cfi) { + if (!checkPhoneStatePermission("notifyCallForwardingChanged()")) { + return; + } synchronized (mRecords) { mCallForwarding = cfi; final int N = mRecords.size(); @@ -268,6 +288,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyDataActivity(int state) { + if (!checkPhoneStatePermission("notifyDataActivity()")) { + return; + } synchronized (mRecords) { mDataActivity = state; final int N = mRecords.size(); @@ -286,6 +309,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { public void notifyDataConnection(int state, boolean isDataConnectivityPissible, String reason, String apn, String interfaceName) { + if (!checkPhoneStatePermission("notifyDataConnection()")) { + return; + } synchronized (mRecords) { mDataConnectionState = state; mDataConnectionPossible = isDataConnectivityPissible; @@ -309,6 +335,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyDataConnectionFailed(String reason) { + if (!checkPhoneStatePermission("notifyDataConnectionFailed()")) { + return; + } /* * This is commented out because there is on onDataConnectionFailed callback * on PhoneStateListener. There should be. @@ -327,6 +356,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCellLocation(Bundle cellLocation) { + if (!checkPhoneStatePermission("notifyCellLocation()")) { + return; + } synchronized (mRecords) { mCellLocation = cellLocation; final int N = mRecords.size(); @@ -363,7 +395,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump telephony.registry from from pid=" + Binder.getCallingPid() @@ -414,6 +446,18 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } private void broadcastCallStateChanged(int state, String incomingNumber) { + long ident = Binder.clearCallingIdentity(); + try { + if (state == TelephonyManager.CALL_STATE_IDLE) { + mBatteryStats.notePhoneOff(); + } else { + mBatteryStats.notePhoneOn(); + } + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString()); @@ -443,4 +487,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { intent.putExtra(Phone.FAILURE_REASON_KEY, reason); mContext.sendStickyBroadcast(intent); } + + private boolean checkPhoneStatePermission(String method) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + String msg = "Modify Phone State Permission Denial: " + method + " from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid(); + Log.w(TAG, msg); + return false; + } } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 2d61b1e..e298f49 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -24,6 +24,7 @@ import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; import android.app.AlarmManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothA2dp; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -86,10 +87,13 @@ public class WifiService extends IWifiManager.Stub { private final LockList mLocks = new LockList(); /** - * See {@link Settings.System#WIFI_IDLE_MS}. This is the default value if a - * Settings.System value is not present. + * See {@link Settings.Gservices#WIFI_IDLE_MS}. This is the default value if a + * Settings.Gservices value is not present. This timeout value is chosen as + * the approximate point at which the battery drain caused by Wi-Fi + * being enabled but not active exceeds the battery drain caused by + * re-establishing a connection to the mobile data network. */ - private static final long DEFAULT_IDLE_MILLIS = 2 * 60 * 1000; /* 2 minutes */ + private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */ private static final String WAKELOCK_TAG = "WifiService"; @@ -100,7 +104,7 @@ public class WifiService extends IWifiManager.Stub { * provides a bit of extra margin. * <p> * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}. - * This is the default value if a Settings.System value is not present. + * This is the default value if a Settings.Secure value is not present. */ private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000; @@ -204,8 +208,6 @@ public class WifiService extends IWifiManager.Stub { mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK); synchronized (sDriverStopWakeLock) { if (sDriverStopWakeLock.isHeld()) { - if (DBG) Log.d(TAG, "Releasing driver-stop wakelock " + - sDriverStopWakeLock); sDriverStopWakeLock.release(); } } @@ -213,9 +215,18 @@ public class WifiService extends IWifiManager.Stub { } ); - Log.d(TAG, "WifiService starting up with Wi-Fi " + - (wifiEnabled ? "enabled" : "disabled")); - registerForBroadcasts(); + Log.i(TAG, "WifiService starting up with Wi-Fi " + + (wifiEnabled ? "enabled" : "disabled")); + + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateWifiState(); + } + }, + new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); + setWifiEnabledBlocking(wifiEnabled, false); } @@ -293,7 +304,7 @@ public class WifiService extends IWifiManager.Stub { decrementHiddentNetworkPresentCounter(); } } - mIsHiddenNetworkPresent.put(netId, new Boolean(present)); + mIsHiddenNetworkPresent.put(netId, present); } } else { Log.e(TAG, "addOrUpdateHiddenNetwork(): Invalid (negative) network id!"); @@ -422,6 +433,7 @@ public class WifiService extends IWifiManager.Stub { /** * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} + * @param enable {@code true} to enable, {@code false} to disable. * @return {@code true} if the enable/disable operation was * started or is already in the queue. */ @@ -429,10 +441,6 @@ public class WifiService extends IWifiManager.Stub { enforceChangePermission(); if (mWifiHandler == null) return false; - /* - * Remove any enable/disable Wi-Fi messages we may have in the queue - * before adding a new one - */ synchronized (mWifiHandler) { sWakeLock.acquire(); sendEnableMessage(enable, true); @@ -458,40 +466,42 @@ public class WifiService extends IWifiManager.Stub { return false; } - updateWifiState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING); + setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING); if (enable) { if (!WifiNative.loadDriver()) { Log.e(TAG, "Failed to load Wi-Fi driver."); - updateWifiState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN); return false; } if (!WifiNative.startSupplicant()) { WifiNative.unloadDriver(); Log.e(TAG, "Failed to start supplicant daemon."); - updateWifiState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN); return false; } + registerForBroadcasts(); mWifiStateTracker.startEventLoop(); } else { - // Remove notification (it will no-op if it isn't visible) + mContext.unregisterReceiver(mReceiver); + // Remove notification (it will no-op if it isn't visible) mWifiStateTracker.setNotificationVisible(false, 0, false, 0); boolean failedToStopSupplicantOrUnloadDriver = false; if (!WifiNative.stopSupplicant()) { Log.e(TAG, "Failed to stop supplicant daemon."); - updateWifiState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN); failedToStopSupplicantOrUnloadDriver = true; } - + // We must reset the interface before we unload the driver mWifiStateTracker.resetInterface(); - + if (!WifiNative.unloadDriver()) { Log.e(TAG, "Failed to unload Wi-Fi driver."); if (!failedToStopSupplicantOrUnloadDriver) { - updateWifiState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN); failedToStopSupplicantOrUnloadDriver = true; } } @@ -505,7 +515,7 @@ public class WifiService extends IWifiManager.Stub { if (persist) { persistWifiEnabled(enable); } - updateWifiState(eventualWifiState); + setWifiEnabledState(eventualWifiState); /* * Initialize the hidden networks state and the number of allowed @@ -519,7 +529,7 @@ public class WifiService extends IWifiManager.Stub { return true; } - private void updateWifiState(int wifiState) { + private void setWifiEnabledState(int wifiState) { final int previousWifiState = mWifiState; // Update state @@ -527,6 +537,7 @@ public class WifiService extends IWifiManager.Stub { // Broadcast final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState); intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState); mContext.sendStickyBroadcast(intent); @@ -551,7 +562,7 @@ public class WifiService extends IWifiManager.Stub { * {@link WifiManager#WIFI_STATE_ENABLING}, * {@link WifiManager#WIFI_STATE_UNKNOWN} */ - public int getWifiState() { + public int getWifiEnabledState() { enforceAccessPermission(); return mWifiState; } @@ -1082,9 +1093,7 @@ public class WifiService extends IWifiManager.Stub { */ removeNetworkIfHidden(netId); - synchronized (mWifiStateTracker) { - return WifiNative.removeNetworkCommand(netId); - } + return mWifiStateTracker.removeNetwork(netId); } /** @@ -1221,7 +1230,6 @@ public class WifiService extends IWifiManager.Stub { if (scanResult != null) { scanResult.level = -scanResultLevel; scanList.add(scanResult); - //if (DBG) Log.d(TAG, "ScanResult: " + scanResult); } } else if (DBG) { Log.w(TAG, @@ -1344,7 +1352,7 @@ public class WifiService extends IWifiManager.Stub { if (result && mNeedReconfig) { mNeedReconfig = false; result = WifiNative.reloadConfigCommand(); - + if (result) { Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION); mContext.sendBroadcast(intent); @@ -1359,7 +1367,7 @@ public class WifiService extends IWifiManager.Stub { * 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 - * persisted as a System setting. + * persisted as a Secure setting. * @param numChannels the number of allowed channels. Must be greater than 0 * and less than or equal to 16. * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., @@ -1441,14 +1449,12 @@ public class WifiService extends IWifiManager.Stub { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - long idleMillis = Settings.System.getLong(mContext.getContentResolver(), - Settings.System.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); + long idleMillis = Settings.Gservices.getLong(mContext.getContentResolver(), + Settings.Gservices.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); int stayAwakeConditions = Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); - if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { - /* do nothing, we'll check isAirplaneModeOn later. */ - } else if (action.equals(Intent.ACTION_SCREEN_ON)) { + if (action.equals(Intent.ACTION_SCREEN_ON)) { mAlarmManager.cancel(mIdleIntent); mDeviceIdle = false; mScreenOff = false; @@ -1485,6 +1491,12 @@ public class WifiService extends IWifiManager.Stub { return; } mPluggedType = pluggedType; + } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) { + boolean isBluetoothPlaying = + intent.getIntExtra( + BluetoothA2dp.SINK_STATE, + BluetoothA2dp.STATE_DISCONNECTED) == BluetoothA2dp.STATE_PLAYING; + mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying); } else { return; } @@ -1543,7 +1555,6 @@ public class WifiService extends IWifiManager.Stub { } private void sendStartMessage(boolean scanOnlyMode) { - if (DBG) Log.d(TAG, "sendStartMessage(" + scanOnlyMode + ")"); Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget(); } @@ -1561,6 +1572,9 @@ public class WifiService extends IWifiManager.Stub { } synchronized (mWifiHandler) { + if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) { + return; + } if (wifiShouldBeEnabled) { if (wifiShouldBeStarted) { sWakeLock.acquire(); @@ -1573,6 +1587,15 @@ public class WifiService extends IWifiManager.Stub { mContext.getContentResolver(), Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, DEFAULT_WAKELOCK_TIMEOUT); + /* + * The following wakelock is held in order to ensure + * that the connectivity manager has time to fail over + * to the mobile data network. The connectivity manager + * releases it once mobile data connectivity has been + * established. If connectivity cannot be established, + * the wakelock is released after wakeLockTimeout + * milliseconds have elapsed. + */ sDriverStopWakeLock.acquire(); mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI); mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout); @@ -1585,18 +1608,13 @@ public class WifiService extends IWifiManager.Stub { } private void registerForBroadcasts() { - if (isAirplaneSensitive()) { - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); - } - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_SCREEN_ON)); - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_SCREEN_OFF)); - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - mContext.registerReceiver(mReceiver, - new IntentFilter(ACTION_DEVICE_IDLE)); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_SCREEN_ON); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); + intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); + intentFilter.addAction(ACTION_DEVICE_IDLE); + intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION); + mContext.registerReceiver(mReceiver, intentFilter); } private boolean isAirplaneSensitive() { @@ -1664,7 +1682,7 @@ public class WifiService extends IWifiManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump WifiService from from pid=" + Binder.getCallingPid() @@ -1672,11 +1690,14 @@ public class WifiService extends IWifiManager.Stub { return; } pw.println("Wi-Fi is " + stateName(mWifiState)); - pw.println("stay-awake conditions: " + + pw.println("Stay-awake conditions: " + Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0)); pw.println(); + pw.println("Internal state:"); + pw.println(mWifiStateTracker); + pw.println(); pw.println("Latest scan results:"); List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList(); if (scanResults != null && scanResults.size() != 0) { @@ -1786,13 +1807,6 @@ public class WifiService extends IWifiManager.Stub { return -1; } - private synchronized void clear() { - if (!mList.isEmpty()) { - mList.clear(); - updateWifiState(); - } - } - private void dump(PrintWriter pw) { for (WifiLock l : mList) { pw.print(" "); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 7ed0aec..e4bd3c3 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -66,6 +66,7 @@ import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.ParcelFileDescriptor; +import android.os.Power; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -122,7 +123,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo static final boolean DEBUG = false; static final boolean DEBUG_FOCUS = false; static final boolean DEBUG_ANIM = false; + static final boolean DEBUG_LAYERS = false; static final boolean DEBUG_INPUT = false; + static final boolean DEBUG_INPUT_METHOD = false; static final boolean DEBUG_VISIBILITY = false; static final boolean DEBUG_ORIENTATION = false; static final boolean DEBUG_APP_TRANSITIONS = false; @@ -202,6 +205,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final boolean mHaveInputMethods; + final boolean mLimitedAlphaCompositing; + final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager(); final IActivityManager mActivityManager; @@ -306,6 +311,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final float[] mTmpFloats = new float[9]; + boolean mSafeMode; boolean mDisplayEnabled = false; boolean mSystemBooted = false; int mRotation = 0; @@ -316,12 +322,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean mLayoutNeeded = true; boolean mAnimationPending = false; - boolean mSurfacesChanged = false; boolean mDisplayFrozen = false; boolean mWindowsFreezingScreen = false; long mFreezeGcPending = 0; int mAppsFreezingScreen = 0; + // This is held as long as we have the screen frozen, to give us time to + // perform a rotation animation when turning off shows the lock screen which + // changes the orientation. + PowerManager.WakeLock mScreenFrozenLock; + // State management of app transitions. When we are preparing for a // transition, mNextAppTransition will be the kind of transition to // perform or TRANSIT_NONE if we are not waiting. If we are waiting, @@ -347,6 +357,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // This just indicates the window the input method is on top of, not // necessarily the window its input is going to. WindowState mInputMethodTarget = null; + WindowState mUpcomingInputMethodTarget = null; + boolean mInputMethodTargetWaitingAnim; + int mInputMethodAnimLayerAdjustment; WindowState mInputMethodWindow = null; final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>(); @@ -461,9 +474,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean haveInputMethods) { mContext = context; mHaveInputMethods = haveInputMethods; + mLimitedAlphaCompositing = context.getResources().getBoolean( + com.android.internal.R.bool.config_sf_limitedAlpha); mPowerManager = pm; mPowerManager.setPolicy(mPolicy); + PowerManager pmc = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mScreenFrozenLock = pmc.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "SCREEN_FROZEN"); + mScreenFrozenLock.setReferenceCounted(false); mActivityManager = ActivityManagerNative.getDefault(); mBatteryStats = BatteryStatsService.getService(); @@ -734,7 +753,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } - int findDesiredInputMethodWindowIndexLocked() { + static boolean canBeImeTarget(WindowState w) { + final int fl = w.mAttrs.flags + & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); + if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) { + return w.isVisibleOrAdding(); + } + return false; + } + + int findDesiredInputMethodWindowIndexLocked(boolean willMove) { final ArrayList localmWindows = mWindows; final int N = localmWindows.size(); WindowState w = null; @@ -742,28 +770,120 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo while (i > 0) { i--; w = (WindowState)localmWindows.get(i); - final int fl = w.mAttrs.flags - & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); + //Log.i(TAG, "Checking window @" + i + " " + w + " fl=0x" - // + Integer.toHexString(fl)); - if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) { + // + Integer.toHexString(w.mAttrs.flags)); + if (canBeImeTarget(w)) { //Log.i(TAG, "Putting input method here!"); - if (w.isVisibleOrAdding()) { - break; + + // Yet more tricksyness! If this window is a "starting" + // window, we do actually want to be on top of it, but + // it is not -really- where input will go. So if the caller + // is not actually looking to move the IME, look down below + // for a real window to target... + if (!willMove + && w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING + && i > 0) { + WindowState wb = (WindowState)localmWindows.get(i-1); + if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) { + i--; + w = wb; + } } + break; } } + + mUpcomingInputMethodTarget = w; + + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target=" + + w + " willMove=" + willMove); + + if (willMove && w != null) { + final WindowState curTarget = mInputMethodTarget; + if (curTarget != null && curTarget.mAppToken != null) { + + // Now some fun for dealing with window animations that + // modify the Z order. We need to look at all windows below + // the current target that are in this app, finding the highest + // visible one in layering. + AppWindowToken token = curTarget.mAppToken; + WindowState highestTarget = null; + int highestPos = 0; + if (token.animating || token.animation != null) { + int pos = 0; + pos = localmWindows.indexOf(curTarget); + while (pos >= 0) { + WindowState win = (WindowState)localmWindows.get(pos); + if (win.mAppToken != token) { + break; + } + if (!win.mRemoved) { + if (highestTarget == null || win.mAnimLayer > + highestTarget.mAnimLayer) { + highestTarget = win; + highestPos = pos; + } + } + pos--; + } + } + + if (highestTarget != null) { + if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + + mNextAppTransition + " " + highestTarget + + " animating=" + highestTarget.isAnimating() + + " layer=" + highestTarget.mAnimLayer + + " new layer=" + w.mAnimLayer); + + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + // If we are currently setting up for an animation, + // hold everything until we can find out what will happen. + mInputMethodTargetWaitingAnim = true; + mInputMethodTarget = highestTarget; + return highestPos + 1; + } else if (highestTarget.isAnimating() && + highestTarget.mAnimLayer > w.mAnimLayer) { + // If the window we are currently targeting is involved + // with an animation, and it is on top of the next target + // we will be over, then hold off on moving until + // that is done. + mInputMethodTarget = highestTarget; + return highestPos + 1; + } + } + } + } + //Log.i(TAG, "Placing input method @" + (i+1)); if (w != null) { - mInputMethodTarget = w; + if (willMove) { + RuntimeException e = new RuntimeException(); + e.fillInStackTrace(); + if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " + + mInputMethodTarget + " to " + w, e); + mInputMethodTarget = w; + if (w.mAppToken != null) { + setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment); + } else { + setInputMethodAnimLayerAdjustment(0); + } + } return i+1; } - mInputMethodTarget = null; + if (willMove) { + RuntimeException e = new RuntimeException(); + e.fillInStackTrace(); + if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " + + mInputMethodTarget + " to null", e); + mInputMethodTarget = null; + setInputMethodAnimLayerAdjustment(0); + } return -1; } void addInputMethodWindowToListLocked(WindowState win) { - int pos = findDesiredInputMethodWindowIndexLocked(); + int pos = findDesiredInputMethodWindowIndexLocked(true); if (pos >= 0) { win.mTargetAppToken = mInputMethodTarget.mAppToken; mWindows.add(pos, win); @@ -775,6 +895,33 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo moveInputMethodDialogsLocked(pos); } + void setInputMethodAnimLayerAdjustment(int adj) { + if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj); + mInputMethodAnimLayerAdjustment = adj; + WindowState imw = mInputMethodWindow; + if (imw != null) { + imw.mAnimLayer = imw.mLayer + adj; + if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw + + " anim layer: " + imw.mAnimLayer); + int wi = imw.mChildWindows.size(); + while (wi > 0) { + wi--; + WindowState cw = (WindowState)imw.mChildWindows.get(wi); + cw.mAnimLayer = cw.mLayer + adj; + if (DEBUG_LAYERS) Log.v(TAG, "IM win " + cw + + " anim layer: " + cw.mAnimLayer); + } + } + int di = mInputMethodDialogs.size(); + while (di > 0) { + di --; + imw = mInputMethodDialogs.get(di); + imw.mAnimLayer = imw.mLayer + adj; + if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw + + " anim layer: " + imw.mAnimLayer); + } + } + private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { int wpos = mWindows.indexOf(win); if (wpos >= 0) { @@ -806,29 +953,55 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + void logWindowList(String prefix) { + int N = mWindows.size(); + while (N > 0) { + N--; + Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); + } + } + void moveInputMethodDialogsLocked(int pos) { ArrayList<WindowState> dialogs = mInputMethodDialogs; + final int N = dialogs.size(); + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos); for (int i=0; i<N; i++) { pos = tmpRemoveWindowLocked(pos, dialogs.get(i)); } + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Window list w/pos=" + pos); + logWindowList(" "); + } + if (pos >= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; - WindowState wp = (WindowState)mWindows.get(pos); - if (wp == mInputMethodWindow) { - pos++; + if (pos < mWindows.size()) { + WindowState wp = (WindowState)mWindows.get(pos); + if (wp == mInputMethodWindow) { + pos++; + } } + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Adding " + N + " dialogs at pos=" + pos); for (int i=0; i<N; i++) { WindowState win = dialogs.get(i); win.mTargetAppToken = targetAppToken; pos = reAddWindowLocked(pos, win); } + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Final window list:"); + logWindowList(" "); + } return; } for (int i=0; i<N; i++) { WindowState win = dialogs.get(i); win.mTargetAppToken = null; reAddWindowToListInOrderLocked(win); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "No IM target, final list:"); + logWindowList(" "); + } } } @@ -839,7 +1012,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return false; } - int imPos = findDesiredInputMethodWindowIndexLocked(); + int imPos = findDesiredInputMethodWindowIndexLocked(true); if (imPos >= 0) { // In this case, the input method windows are to be placed // immediately above the window they are targeting. @@ -884,9 +1057,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } if (imWin != null) { + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Moving IM from " + imPos); + logWindowList(" "); + } imPos = tmpRemoveWindowLocked(imPos, imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List after moving with new pos " + imPos + ":"); + logWindowList(" "); + } imWin.mTargetAppToken = mInputMethodTarget.mAppToken; reAddWindowLocked(imPos, imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List after moving IM to " + imPos + ":"); + logWindowList(" "); + } if (DN > 0) moveInputMethodDialogsLocked(imPos+1); } else { moveInputMethodDialogsLocked(imPos); @@ -897,9 +1082,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // because they aren't currently associated with a focus window. if (imWin != null) { + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos); tmpRemoveWindowLocked(0, imWin); imWin.mTargetAppToken = null; reAddWindowToListInOrderLocked(imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List with no IM target:"); + logWindowList(" "); + } if (DN > 0) moveInputMethodDialogsLocked(-1);; } else { moveInputMethodDialogsLocked(-1);; @@ -915,7 +1105,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } void adjustInputMethodDialogsLocked() { - moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked()); + moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); } public int addWindow(Session session, IWindow client, @@ -975,7 +1165,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } - token = new WindowToken(attrs.token, -1); + token = new WindowToken(attrs.token, -1, false); addToken = true; } else if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { @@ -1053,8 +1243,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo addWindowToListInOrderLocked(win, true); } - Binder.restoreCallingIdentity(origId); - win.mEnterAnimationPending = true; mPolicy.getContentInsetHintLw(attrs, outContentInsets); @@ -1066,8 +1254,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE; } + boolean focusChanged = false; if (win.canReceiveKeys()) { - if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS)) { + if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS)) + == true) { imMayMove = false; } } @@ -1082,16 +1272,34 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //dump(); + if (focusChanged) { + if (mCurrentFocus != null) { + mKeyWaiter.handleNewWindowLocked(mCurrentFocus); + } + } + if (localLOGV) Log.v( TAG, "New client " + client.asBinder() + ": window=" + win); } + // sendNewConfiguration() checks caller permissions so we must call it with + // privilege. updateOrientationFromAppTokens() clears and resets the caller + // identity anyway, so it's safe to just clear & restore around this whole + // block. + final long origId = Binder.clearCallingIdentity(); if (reportNewConfig) { - final long origId = Binder.clearCallingIdentity(); sendNewConfiguration(); - Binder.restoreCallingIdentity(origId); + } else { + // Update Orientation after adding a window, only if the window needs to be + // displayed right away + if (win.isVisibleOrAdding()) { + if (updateOrientationFromAppTokens(null) != null) { + sendNewConfiguration(); + } + } } + Binder.restoreCallingIdentity(origId); return res; } @@ -1125,7 +1333,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " inPendingTransaction=" + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false) + " mDisplayFrozen=" + mDisplayFrozen); - + // Visibility of the removed window. Will be used later to update orientation later on. + boolean wasVisible = false; // First, see if we need to run an animation. If we do, we have // to hold off on removing the window until the animation is done. // If the display is frozen, just remove immediately, since the @@ -1133,7 +1342,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.mSurface != null && !mDisplayFrozen) { // If we are not currently running the exit animation, we // need to see about starting one. - if (win.isVisibleLw()) { + if (wasVisible=win.isWinVisibleLw()) { + int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; @@ -1161,6 +1371,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } removeWindowInnerLocked(session, win); + // Removing a visible window will effect the computed orientation + // So just update orientation if needed. + if (wasVisible && computeForcedAppOrientationLocked() + != mForcedAppOrientation) { + mH.sendMessage(mH.obtainMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION)); + } updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); Binder.restoreCallingIdentity(origId); } @@ -1169,6 +1385,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.releasePendingPointerLocked(win.mSession); mKeyWaiter.releasePendingTrackballLocked(win.mSession); + win.mRemoved = true; + + if (mInputMethodTarget == win) { + moveInputMethodWindowsIfNeededLocked(false); + } + mPolicy.removeWindowLw(win); win.removeLocked(); @@ -1191,10 +1413,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "**** Removing window " + win + ": count=" + token.windows.size()); if (token.windows.size() == 0) { - if (atoken != token) { + if (!token.explicit) { mTokenMap.remove(token.token); mTokenList.remove(token); - } else { + } else if (atoken != null) { atoken.firstWindowDrawn = false; } } @@ -1287,7 +1509,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Surface outSurface) { boolean displayed = false; boolean inTouchMode; - + Configuration newConfig = null; long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { @@ -1359,6 +1581,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo && !win.mCommitDrawPending && !mDisplayFrozen) { applyEnterAnimationLocked(win); } + if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) { + // To change the format, we need to re-build the surface. + win.destroySurfaceLocked(); + displayed = true; + } try { Surface surface = win.createSurfaceLocked(); if (surface != null) { @@ -1393,7 +1620,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } - if (win.isVisibleLw() && + if (win.isWinVisibleLw() && applyAnimationLocked(win, transit, false)) { win.mExiting = true; mKeyWaiter.finishedKey(session, client, true, @@ -1403,27 +1630,28 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // an exit. win.mExiting = true; } else { + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } win.destroySurfaceLocked(); } } } - if (mInputMethodWindow == win) { - mInputMethodWindow = null; - } outSurface.clear(); } - boolean assignLayers = false; - if (focusMayChange) { //System.out.println("Focus may change: " + win.mAttrs.getTitle()); if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) { - assignLayers = true; imMayMove = false; } //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus); } + // updateFocusedWindowLocked() already assigned layers so we only need to + // reassign them at this point if the IM window state gets shuffled + boolean assignLayers = false; + if (imMayMove) { if (moveInputMethodWindowsIfNeededLocked(false)) { assignLayers = true; @@ -1435,6 +1663,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (assignLayers) { assignLayersLocked(); } + newConfig = updateOrientationFromAppTokensLocked(null); performLayoutAndPlaceSurfacesLocked(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); @@ -1456,6 +1685,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo inTouchMode = mInTouchMode; } + if (newConfig != null) { + sendNewConfiguration(); + } + Binder.restoreCallingIdentity(origId); return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) @@ -1507,7 +1740,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private boolean applyAnimationLocked(WindowState win, int transit, boolean isEntrance) { - if (win.mAnimating && win.mAnimationIsEntrance == isEntrance) { + if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. return true; @@ -1553,6 +1786,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, "Loaded animation " + a + " for " + win, e); } win.setAnimation(a); + win.mAnimationIsEntrance = isEntrance; } } else { win.clearAnimation(); @@ -1708,7 +1942,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.w(TAG, "Attempted to add existing input method token: " + token); return; } - wtoken = new WindowToken(token, type); + wtoken = new WindowToken(token, type, true); mTokenMap.put(token, wtoken); mTokenList.add(wtoken); } @@ -1812,15 +2046,36 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } - public Configuration updateOrientationFromAppTokens( - IBinder freezeThisOneIfNeeded) { - boolean changed = false; - synchronized(mWindowMap) { + public int getOrientationFromWindowsLocked() { + int pos = mWindows.size() - 1; + while (pos >= 0) { + WindowState wtoken = (WindowState) mWindows.get(pos); + pos--; + if (wtoken.mAppToken != null) { + // We hit an application window. so the orientation will be determined by the + // app window. No point in continuing further. + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + if (!wtoken.isVisibleLw()) { + continue; + } + int req = wtoken.mAttrs.screenOrientation; + if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || + (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ + continue; + } else { + return req; + } + } + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + + public int getOrientationFromAppTokensLocked() { int pos = mAppTokens.size() - 1; - int req = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; int curGroup = 0; int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean haveGroup = false; + boolean lastFullscreen = false; while (pos >= 0) { AppWindowToken wtoken = mAppTokens.get(pos); pos--; @@ -1835,13 +2090,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else if (curGroup != wtoken.groupId) { // If we have hit a new application group, and the bottom // of the previous group didn't explicitly say to use - // the orientation behind it, then we'll stick with the + // the orientation behind it, and the last app was + // full screen, then we'll stick with the // user's orientation. - if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { - break; + if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND + && lastFullscreen) { + return lastOrientation; } } int or = wtoken.requestedOrientation; + // If this application is fullscreen, then just take whatever + // orientation it has and ignores whatever is under it. + lastFullscreen = wtoken.appFullscreen; + if (lastFullscreen) { + return or; + } // If this application has requested an explicit orientation, // then use it. if (or == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || @@ -1849,16 +2112,47 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo or == ActivityInfo.SCREEN_ORIENTATION_SENSOR || or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR || or == ActivityInfo.SCREEN_ORIENTATION_USER) { - req = or; - break; + return or; } } + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + + public Configuration updateOrientationFromAppTokens( + IBinder freezeThisOneIfNeeded) { + Configuration config; + long ident = Binder.clearCallingIdentity(); + synchronized(mWindowMap) { + config = updateOrientationFromAppTokensLocked(freezeThisOneIfNeeded); + } + if (config != null) { + mLayoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + Binder.restoreCallingIdentity(ident); + return config; + } + + /* + * The orientation is computed from non-application windows first. If none of + * the non-application windows specify orientation, the orientation is computed from + * application tokens. + * @see android.view.IWindowManager#updateOrientationFromAppTokens( + * android.os.IBinder) + */ + Configuration updateOrientationFromAppTokensLocked( + IBinder freezeThisOneIfNeeded) { + boolean changed = false; + long ident = Binder.clearCallingIdentity(); + try { + int req = computeForcedAppOrientationLocked(); + if (req != mForcedAppOrientation) { changed = true; mForcedAppOrientation = req; //send a message to Policy indicating orientation change to take //action like disabling/enabling sensors etc., - mPolicy.setCurrentOrientation(req); + mPolicy.setCurrentOrientationLw(req); } if (changed) { @@ -1873,19 +2167,24 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ActivityInfo.CONFIG_ORIENTATION); } } - Configuration config = computeNewConfigurationLocked(); - if (config != null) { - mLayoutNeeded = true; - performLayoutAndPlaceSurfacesLocked(); - } - return config; + return computeNewConfiguration(); } } + } finally { + Binder.restoreCallingIdentity(ident); } return null; } + int computeForcedAppOrientationLocked() { + int req = getOrientationFromWindowsLocked(); + if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + req = getOrientationFromAppTokensLocked(); + } + return req; + } + public void setAppOrientation(IApplicationToken token, int requestedOrientation) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppOrientation()")) { @@ -2088,7 +2387,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); - assignLayersLocked(); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); Binder.restoreCallingIdentity(origId); @@ -2230,7 +2528,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (changed && performLayout) { mLayoutNeeded = true; updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); - assignLayersLocked(); performLayoutAndPlaceSurfacesLocked(); } } @@ -2559,7 +2856,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean added = false; for (int j=0; j<NCW; j++) { WindowState cwin = (WindowState)win.mChildWindows.get(j); - if (cwin.mSubLayer >= 0) { + if (!added && cwin.mSubLayer >= 0) { mWindows.add(index, win); index++; added = true; @@ -2611,7 +2908,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_REORDER) Log.v(TAG, "Final window list:"); if (DEBUG_REORDER) dumpWindowsLocked(); updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); - assignLayersLocked(); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } @@ -2659,7 +2955,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); - assignLayersLocked(); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); @@ -2871,6 +3166,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return KeyInputQueue.getKeycodeState(devid, sw); } + public boolean hasKeys(int[] keycodes, boolean[] keyExists) { + return KeyInputQueue.hasKeys(keycodes, keyExists); + } + public void enableScreenAfterBoot() { synchronized(mWindowMap) { if (mSystemBooted) { @@ -2987,18 +3286,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { mRequestedRotation = rotation; } - if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from "+rotation); - rotation = mPolicy.rotationForOrientation(mForcedAppOrientation); - if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to "+rotation); + if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from " + rotation); + rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, + mRotation, mDisplayEnabled); + if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to " + rotation); changed = mDisplayEnabled && mRotation != rotation; if (changed) { - mRotation = rotation; if (DEBUG_ORIENTATION) Log.v(TAG, "Rotation changed to " + rotation + " from " + mRotation + " (forceApp=" + mForcedAppOrientation + ", req=" + mRequestedRotation + ")"); + mRotation = rotation; mWindowsFreezingScreen = true; mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), @@ -3298,21 +3598,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return null; } + /* + * Instruct the Activity Manager to fetch the current configuration and broadcast + * that to config-changed listeners if appropriate. + */ void sendNewConfiguration() { - Configuration config; - synchronized (mWindowMap) { - config = computeNewConfigurationLocked(); - } - - if (config != null) { - try { - mActivityManager.updateConfiguration(config); - } catch (RemoteException e) { - } + try { + mActivityManager.updateConfiguration(null); + } catch (RemoteException e) { } } - Configuration computeNewConfigurationLocked() { + public Configuration computeNewConfiguration() { synchronized (mWindowMap) { if (mDisplay == null) { return null; @@ -3669,6 +3966,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized(mWindowMap) { mKeyWaiter.bindTargetWindowLocked(focus); } + + // NOSHIP extra state logging + mKeyWaiter.recordDispatchState(event, focus); + // END NOSHIP try { if (DEBUG_INPUT || DEBUG_FOCUS) { @@ -3812,6 +4113,58 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * but not the other way around. */ final class KeyWaiter { + // NOSHIP debugging + public class DispatchState { + private KeyEvent event; + private WindowState focus; + private long time; + private WindowState lastWin; + private IBinder lastBinder; + private boolean finished; + private boolean gotFirstWindow; + private boolean eventDispatching; + private long timeToSwitch; + private boolean wasFrozen; + private boolean focusPaused; + private WindowState curFocus; + + DispatchState(KeyEvent theEvent, WindowState theFocus) { + focus = theFocus; + event = theEvent; + time = System.currentTimeMillis(); + // snapshot KeyWaiter state + lastWin = mLastWin; + lastBinder = mLastBinder; + finished = mFinished; + gotFirstWindow = mGotFirstWindow; + eventDispatching = mEventDispatching; + timeToSwitch = mTimeToSwitch; + wasFrozen = mWasFrozen; + curFocus = mCurrentFocus; + // cache the paused state at ctor time as well + if (theFocus == null || theFocus.mToken == null) { + Log.i(TAG, "focus " + theFocus + " mToken is null at event dispatch!"); + focusPaused = false; + } else { + focusPaused = theFocus.mToken.paused; + } + } + + public String toString() { + return "{{" + event + " to " + focus + " @ " + time + + " lw=" + lastWin + " lb=" + lastBinder + + " fin=" + finished + " gfw=" + gotFirstWindow + + " ed=" + eventDispatching + " tts=" + timeToSwitch + + " wf=" + wasFrozen + " fp=" + focusPaused + + " mcf=" + mCurrentFocus + "}}"; + } + }; + private DispatchState mDispatchState = null; + public void recordDispatchState(KeyEvent theEvent, WindowState theFocus) { + mDispatchState = new DispatchState(theEvent, theFocus); + } + // END NOSHIP + public static final int RETURN_NOTHING = 0; public static final int RETURN_PENDING_POINTER = 1; public static final int RETURN_PENDING_TRACKBALL = 2; @@ -3923,7 +4276,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " targetIsNew=" + targetIsNew + " paused=" + (targetWin != null ? targetWin.mToken.paused : false) - + " mFocusedApp=" + mFocusedApp); + + " mFocusedApp=" + mFocusedApp + + " mCurrentFocus=" + mCurrentFocus); targetApp = targetWin != null ? targetWin.mAppToken : mFocusedApp; @@ -3952,7 +4306,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wait(curTimeout); if (DEBUG_INPUT) Log.v(TAG, "Finished waiting @" + SystemClock.uptimeMillis() + " startTime=" - + startTime + " switchTime=" + mTimeToSwitch); + + startTime + " switchTime=" + mTimeToSwitch + + " target=" + targetWin + " mLW=" + mLastWin + + " mLB=" + mLastBinder + " fin=" + mFinished + + " mCurrentFocus=" + mCurrentFocus); } catch (InterruptedException e) { } } @@ -3973,6 +4330,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.w(TAG, "Key dispatching timed out sending to " + (targetWin != null ? targetWin.mAttrs.getTitle() : "<null>")); + // NOSHIP debugging + Log.w(TAG, "Dispatch state: " + mDispatchState); + Log.w(TAG, "Current state: " + new DispatchState(nextKey, targetWin)); + // END NOSHIP //dump(); if (targetWin != null) { at = targetWin.getAppToken(); @@ -4355,7 +4716,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo newWindow.mToken.paused = false; mGotFirstWindow = true; - boolean doNotify = true; if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) { if (DEBUG_INPUT) Log.v(TAG, @@ -4372,20 +4732,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "Last win layer=" + mLastWin.mLayer + ", new win layer=" + newWindow.mLayer); if (newWindow.mLayer >= mLastWin.mLayer) { - if (!mLastWin.canReceiveKeys()) { - mLastWin.mToken.paused = false; - doFinishedKeyLocked(true); // does a notifyAll() - doNotify = false; - } - } else { - // the new window is lower; no need to wake key waiters - doNotify = false; + // The new window is above the old; finish pending input to the last + // window and start directing it to the new one. + mLastWin.mToken.paused = false; + doFinishedKeyLocked(true); // does a notifyAll() } + // Either the new window is lower, so there is no need to wake key waiters, + // or we just finished key input to the previous window, which implicitly + // notified the key waiters. In both cases, we don't need to issue the + // notification here. + return; } - if (doNotify) { - notifyAll(); - } + // Now that we've put a new window state in place, make the event waiter + // take notice and retarget its attentions. + notifyAll(); } } @@ -4478,8 +4839,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo KeyQ() { super(mContext); PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK - | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG"); + mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, + "KEEP_SCREEN_ON_FLAG"); mHoldingScreen.setReferenceCounted(false); } @@ -4602,14 +4963,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (holding) { mHoldingScreen.acquire(); } else { - long curTime = SystemClock.uptimeMillis(); - mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT); + mPolicy.screenOnStoppedLw(); mHoldingScreen.release(); } } } }; + public boolean detectSafeMode() { + mSafeMode = mPolicy.detectSafeMode(); + return mSafeMode; + } + public void systemReady() { mPolicy.systemReady(); } @@ -4923,6 +5288,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + public boolean performHapticFeedback(IWindow window, int effectId, + boolean always) { + synchronized(mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + return mPolicy.performHapticFeedbackLw( + windowForClientLocked(this, window), effectId, always); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + void windowAddedLocked() { if (mSurfaceSession == null) { if (localLOGV) Log.v( @@ -5083,9 +5461,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Currently running animation. boolean mAnimating; + boolean mLocalAnimating; Animation mAnimation; boolean mAnimationIsEntrance; boolean mHasTransformation; + boolean mHasLocalTransformation; final Transformation mTransformation = new Transformation(); // This is set after IWindowSession.relayout() has been called at @@ -5125,6 +5505,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // been updated for the new orientation. boolean mOrientationChanging; + // Is this window now (or just being) removed? + boolean mRemoved; + WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) { @@ -5292,6 +5675,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return mFrame; } + public Rect getShownFrameLw() { + return mShownFrame; + } + public Rect getDisplayFrameLw() { return mDisplayFrame; } @@ -5344,6 +5731,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (localLOGV) Log.v( TAG, "Setting animation in " + this + ": " + anim); mAnimating = false; + mLocalAnimating = false; mAnimation = anim; mAnimation.restrictDuration(MAX_ANIMATION_DURATION); mAnimation.scaleCurrentDuration(mWindowAnimationScale); @@ -5352,6 +5740,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void clearAnimation() { if (mAnimation != null) { mAnimating = true; + mLocalAnimating = false; mAnimation = null; } } @@ -5580,13 +5969,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { mHasTransformation = true; - if (!mAnimating) { + mHasLocalTransformation = true; + if (!mLocalAnimating) { if (DEBUG_ANIM) Log.v( TAG, "Starting animation in " + this + " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale); mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); mAnimation.setStartTime(currentTime); + mLocalAnimating = true; mAnimating = true; } mTransformation.clear(); @@ -5595,9 +5986,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_ANIM) Log.v( TAG, "Stepped animation in " + this + ": more=" + more + ", xform=" + mTransformation); - if (mAppToken != null && mAppToken.hasTransformation) { - mTransformation.compose(mAppToken.transformation); - } if (more) { // we're not done! return true; @@ -5608,10 +5996,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAnimation = null; //WindowManagerService.this.dump(); } - if (mAppToken != null && mAppToken.hasTransformation) { + mHasLocalTransformation = false; + if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null + && mAppToken.hasTransformation) { + // 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 + // our window is not currently exiting, or it is not + // locally animating itself. The idea being that one that + // is exiting and doing a local animation should be removed + // once that animation is done. mAnimating = true; mHasTransformation = true; - mTransformation.set(mAppToken.transformation); + mTransformation.clear(); return false; } else if (mHasTransformation) { // Little trick to get through the path below to act like @@ -5624,10 +6021,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // If the display is frozen, and there is a pending animation, // clear it and make sure we run the cleanup code. mAnimating = true; + mLocalAnimating = true; mAnimation = null; } - if (!mAnimating) { + if (!mAnimating && !mLocalAnimating) { return false; } @@ -5637,9 +6035,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + (mAppToken != null ? mAppToken.reportedVisible : false)); mAnimating = false; + mLocalAnimating = false; mAnimation = null; mAnimLayer = mLayer; + if (mIsImWindow) { + mAnimLayer += mInputMethodAnimLayerAdjustment; + } + if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this + + " anim layer: " + mAnimLayer); mHasTransformation = false; + mHasLocalTransformation = false; mPolicyVisibility = mPolicyVisibilityAfterAnim; mTransformation.clear(); if (mHasDrawn @@ -5714,10 +6119,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } void computeShownFrameLocked() { - final boolean selfTransformation = mHasTransformation; - final boolean attachedTransformation = (mAttachedWindow != null - && mAttachedWindow.mHasTransformation); - if (selfTransformation || attachedTransformation) { + final boolean selfTransformation = mHasLocalTransformation; + Transformation attachedTransformation = + (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation) + ? mAttachedWindow.mTransformation : null; + Transformation appTransformation = + (mAppToken != null && mAppToken.hasTransformation) + ? mAppToken.transformation : null; + if (selfTransformation || attachedTransformation != null + || appTransformation != null) { // cache often used attributes locally final Rect frame = mFrame; final float tmpFloats[] = mTmpFloats; @@ -5728,8 +6138,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (selfTransformation) { tmpMatrix.preConcat(mTransformation.getMatrix()); } - if (attachedTransformation) { - tmpMatrix.preConcat(mAttachedWindow.mTransformation.getMatrix()); + if (attachedTransformation != null) { + tmpMatrix.preConcat(attachedTransformation.getMatrix()); + } + if (appTransformation != null) { + tmpMatrix.preConcat(appTransformation.getMatrix()); } // "convert" it into SurfaceFlinger's format @@ -5755,15 +6168,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // transforming since it is more important to have that // animation be smooth. mShownAlpha = mAlpha; - if (false && (!PixelFormat.formatHasAlpha(mAttrs.format) + if (!mLimitedAlphaCompositing + || (!PixelFormat.formatHasAlpha(mAttrs.format) || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) && x == frame.left && y == frame.top))) { //Log.i(TAG, "Applying alpha transform"); if (selfTransformation) { mShownAlpha *= mTransformation.getAlpha(); } - if (attachedTransformation) { - mShownAlpha *= mAttachedWindow.mTransformation.getAlpha(); + if (attachedTransformation != null) { + mShownAlpha *= attachedTransformation.getAlpha(); + } + if (appTransformation != null) { + mShownAlpha *= appTransformation.getAlpha(); } } else { //Log.i(TAG, "Not applying alpha transform"); @@ -5787,7 +6204,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo /** * Is this window visible? 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. + * that will remove the surface, or its app token has been hidden. */ public boolean isVisibleLw() { final AppWindowToken atoken = mAppToken; @@ -5797,6 +6214,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } /** + * 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. + */ + public boolean isWinVisibleLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested || atoken.animating) + && !mExiting && !mDestroying; + } + + /** * The same as isVisible(), but follows the current hidden state of * the associated app token, not the pending requested hidden state. */ @@ -5877,10 +6306,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } public boolean fillsScreenLw(int screenWidth, int screenHeight, - boolean shownFrame) { + boolean shownFrame, boolean onlyOpaque) { if (mSurface == null) { return false; } + if (mAppToken != null && !mAppToken.appFullscreen) { + return false; + } + if (onlyOpaque && mAttrs.format != PixelFormat.OPAQUE) { + return false; + } final Rect frame = shownFrame ? mShownFrame : mFrame; if (frame.left <= 0 && frame.top <= 0 && frame.right >= screenWidth @@ -5944,21 +6379,23 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return mHasDrawn; } - public void showLw(boolean doAnimation) { + public boolean showLw(boolean doAnimation) { if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim) { - mSurfacesChanged = true; mPolicyVisibility = true; mPolicyVisibilityAfterAnim = true; if (doAnimation) { applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true); } requestAnimationLocked(0); + return true; } + return false; } - public void hideLw(boolean doAnimation) { - if (mPolicyVisibility || mPolicyVisibilityAfterAnim) { - mSurfacesChanged = true; + public boolean hideLw(boolean doAnimation) { + boolean current = doAnimation ? mPolicyVisibilityAfterAnim + : mPolicyVisibility; + if (current) { if (doAnimation) { applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false); if (mAnimation == null) { @@ -5968,10 +6405,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (doAnimation) { mPolicyVisibilityAfterAnim = false; } else { + mPolicyVisibilityAfterAnim = false; mPolicyVisibility = false; } requestAnimationLocked(0); + return true; } + return false; } void dump(PrintWriter pw, String prefix) { @@ -6020,6 +6460,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(prefix + "mShownAlpha=" + mShownAlpha + " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha); pw.println(prefix + "mAnimating=" + mAnimating + + " mLocalAnimating=" + mLocalAnimating + " mAnimationIsEntrance=" + mAnimationIsEntrance + " mAnimation=" + mAnimation); pw.println(prefix + "XForm: has=" + mHasTransformation @@ -6030,7 +6471,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " mHasDrawn=" + mHasDrawn); pw.println(prefix + "mExiting=" + mExiting + " mRemoveOnExit=" + mRemoveOnExit - + " mDestroying=" + mDestroying); + + " mDestroying=" + mDestroying + + " mRemoved=" + mRemoved); pw.println(prefix + "mOrientationChanging=" + mOrientationChanging + " mAppFreezing=" + mAppFreezing); } @@ -6039,7 +6481,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public String toString() { return "Window{" + Integer.toHexString(System.identityHashCode(this)) - + " " + mAttrs.getTitle() + "}"; + + " " + mAttrs.getTitle() + " paused=" + mToken.paused + "}"; } } @@ -6054,6 +6496,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // The type of window this token is for, as per WindowManager.LayoutParams. final int windowType; + // Set if this token was explicitly added by a client, so should + // not be removed when all windows are removed. + final boolean explicit; + // If this is an AppWindowToken, this is non-null. AppWindowToken appWindowToken; @@ -6069,9 +6515,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Temporary for finding which tokens no longer have visible windows. boolean hasVisible; - WindowToken(IBinder _token, int type) { + WindowToken(IBinder _token, int type, boolean _explicit) { token = _token; windowType = type; + explicit = _explicit; } void dump(PrintWriter pw, String prefix) { @@ -6151,7 +6598,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean firstWindowDrawn; AppWindowToken(IApplicationToken _token) { - super(_token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION); + super(_token.asBinder(), + WindowManager.LayoutParams.TYPE_APPLICATION, true); appWindowToken = this; appToken = _token; } @@ -6198,17 +6646,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo for (int i=0; i<N; i++) { WindowState w = allAppWindows.get(i); w.mAnimLayer = w.mLayer + adj; + if (DEBUG_LAYERS) Log.v(TAG, "Updating layer " + w + ": " + + w.mAnimLayer); if (w == mInputMethodTarget) { - WindowState imw = mInputMethodWindow; - if (imw != null) { - imw.mAnimLayer = imw.mLayer + adj; - } - int di = mInputMethodDialogs.size(); - while (di > 0) { - di --; - imw = mInputMethodDialogs.get(di); - imw.mAnimLayer = imw.mLayer + adj; - } + setInputMethodAnimLayerAdjustment(adj); } } } @@ -6295,7 +6736,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo clearAnimation(); animating = false; - + if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) { + moveInputMethodWindowsIfNeededLocked(true); + } + if (DEBUG_ANIM) Log.v( TAG, "Animation done in " + this + ": reportedVisible=" + reportedVisible); @@ -6503,6 +6947,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public static final int FORCE_GC = 15; public static final int ENABLE_SCREEN = 16; public static final int APP_FREEZE_TIMEOUT = 17; + public static final int COMPUTE_AND_SEND_NEW_CONFIGURATION = 18; private Session mLastReportedHold; @@ -6519,6 +6964,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized(mWindowMap) { lastFocus = mLastFocus; newFocus = mCurrentFocus; + if (lastFocus == newFocus) { + // Focus is not changing, so nothing to do. + return; + } mLastFocus = newFocus; //Log.i(TAG, "Focus moving from " + lastFocus // + " to " + newFocus); @@ -6829,6 +7278,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo break; } + case COMPUTE_AND_SEND_NEW_CONFIGURATION: { + if (updateOrientationFromAppTokens(null) != null) { + sendNewConfiguration(); + } + break; + } + } } } @@ -6848,12 +7304,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized (mWindowMap) { // The focus for the client is the window immediately below // where we would place the input method window. - int idx = findDesiredInputMethodWindowIndexLocked(); + int idx = findDesiredInputMethodWindowIndexLocked(false); + WindowState imFocus; if (idx > 0) { - WindowState imFocus = (WindowState)mWindows.get(idx-1); - if (imFocus != null && imFocus.mSession.mClient != null && - imFocus.mSession.mClient.asBinder() == client.asBinder()) { - return true; + imFocus = (WindowState)mWindows.get(idx-1); + if (imFocus != null) { + if (imFocus.mSession.mClient != null && + imFocus.mSession.mClient.asBinder() == client.asBinder()) { + return true; + } } } } @@ -6888,13 +7347,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } private final void assignLayersLocked() { - if (mInLayout) { - if (Config.DEBUG) { - throw new RuntimeException("Recursive call!"); - } - return; - } - int N = mWindows.size(); int curBaseLayer = 0; int curLayer = 0; @@ -6916,6 +7368,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { w.mAnimLayer = w.mLayer; } + if (w.mIsImWindow) { + w.mAnimLayer += mInputMethodAnimLayerAdjustment; + } + if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": " + + w.mAnimLayer); //System.out.println( // "Assigned layer " + curLayer + " to " + w.mClient.asBinder()); } @@ -6927,6 +7384,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (Config.DEBUG) { throw new RuntimeException("Recursive call!"); } + Log.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout"); return; } @@ -6999,7 +7457,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo WindowState win = (WindowState) mWindows.get(i); boolean gone = win.mViewVisibility == View.GONE - || !win.mRelayoutCalled; + || !win.mRelayoutCalled + || win.mToken.hidden; // If this view is GONE, then skip it -- keep the current // frame, and let the caller know so they can ignore it @@ -7073,6 +7532,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean orientationChangeComplete = true; Session holdScreen = null; + float screenBrightness = -1; boolean focusDisplayed = false; boolean animating = false; @@ -7285,6 +7745,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // This has changed the visibility of windows, so perform // a new layout to get them all up-to-date. mLayoutNeeded = true; + moveInputMethodWindowsIfNeededLocked(true); performLayoutLockedInner(); updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES); @@ -7297,12 +7758,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final boolean someoneLosingFocus = mLosingFocus.size() != 0; - mSurfacesChanged = false; boolean obscured = false; boolean blurring = false; boolean dimming = false; boolean covered = false; - int tint = 0; for (i=N-1; i>=0; i--) { WindowState w = (WindowState)mWindows.get(i); @@ -7558,9 +8017,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Update effect. if (!obscured) { - if (w.mSurface != null && - (attrFlags&FLAG_KEEP_SCREEN_ON) != 0) { - holdScreen = w.mSession; + if (w.mSurface != null) { + if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) { + holdScreen = w.mSession; + } + if (w.mAttrs.screenBrightness >= 0 && screenBrightness < 0) { + screenBrightness = w.mAttrs.screenBrightness; + } } if (w.isFullscreenOpaque(dw, dh)) { // This window completely covers everything behind it, @@ -7619,6 +8082,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // the user. duration *= DIM_DURATION_MULTIPLIER; } + if (duration < 1) { + // Don't divide by zero + duration = 1; + } mDimTargetAlpha = target; mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration; @@ -7673,7 +8140,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mDimCurrentAlpha += mDimDeltaPerMs * (currentTime-mLastDimAnimTime); boolean more = true; - if (mDimDeltaPerMs > 0) { + if (mDisplayFrozen) { + // If the display is frozen, there is no reason to animate. + more = false; + } else if (mDimDeltaPerMs > 0) { if (mDimCurrentAlpha > mDimTargetAlpha) { more = false; } @@ -7767,6 +8237,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo i--; WindowState win = mDestroySurface.get(i); win.mDestroying = false; + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } win.destroySurfaceLocked(); } while (i > 0); mDestroySurface.clear(); @@ -7796,6 +8269,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis()); } mQueue.setHoldScreenLocked(holdScreen != null); + if (screenBrightness < 0 || screenBrightness > 1.0f) { + mPowerManager.setScreenBrightnessOverride(-1); + } else { + mPowerManager.setScreenBrightnessOverride((int) + (screenBrightness * Power.BRIGHTNESS_ON)); + } if (holdScreen != mHoldingScreenOn) { mHoldingScreenOn = holdScreen; Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen); @@ -7811,7 +8290,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } /** - * Have the surface flinger show a surface, robustly dealing wit + * Have the surface flinger show a surface, robustly dealing with * error conditions. In particular, if there is not enough memory * to show the surface, then we will try to get rid of other surfaces * in order to succeed. @@ -7925,31 +8404,33 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mCurrentFocus != newFocus) { // This check makes sure that we don't already have the focus // change message pending. - if (mLastFocus == mCurrentFocus) { - mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE); - } + mH.removeMessages(H.REPORT_FOCUS_CHANGE); + mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE); if (localLOGV) Log.v( TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus); final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; mLosingFocus.remove(newFocus); - if (newFocus != null) { - mKeyWaiter.handleNewWindowLocked(newFocus); - } final WindowState imWindow = mInputMethodWindow; if (newFocus != imWindow && oldFocus != imWindow) { - moveInputMethodWindowsIfNeededLocked( + if (moveInputMethodWindowsIfNeededLocked( mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS && - mode != UPDATE_FOCUS_WILL_PLACE_SURFACES); - mLayoutNeeded = true; + mode != UPDATE_FOCUS_WILL_PLACE_SURFACES)) { + mLayoutNeeded = true; + } if (mode == UPDATE_FOCUS_PLACING_SURFACES) { performLayoutLockedInner(); - } else if (mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) { - mLayoutNeeded = true; - performLayoutAndPlaceSurfacesLocked(); + } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) { + // Client will do the layout, but we need to assign layers + // for handleNewWindowLocked() below. + assignLayersLocked(); } } + + if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) { + mKeyWaiter.handleNewWindowLocked(newFocus); + } return true; } return false; @@ -8029,6 +8510,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return; } + mScreenFrozenLock.acquire(); + long now = SystemClock.uptimeMillis(); //Log.i(TAG, "Freezing, gc pending: " + mFreezeGcPending + ", now " + now); if (mFreezeGcPending != 0) { @@ -8084,6 +8567,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.removeMessages(H.FORCE_GC); mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC), 2000); + + mScreenFrozenLock.release(); } @Override @@ -8218,13 +8703,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(" mSystemBooted=" + mSystemBooted + " mDisplayEnabled=" + mDisplayEnabled); pw.println(" mLayoutNeeded=" + mLayoutNeeded - + " mSurfacesChanged=" + mSurfacesChanged + " mBlurShown=" + mBlurShown); pw.println(" mDimShown=" + mDimShown + " current=" + mDimCurrentAlpha + " target=" + mDimTargetAlpha + " delta=" + mDimDeltaPerMs + " lastAnimTime=" + mLastDimAnimTime); + pw.println(" mInputMethodAnimLayerAdjustment=" + + mInputMethodAnimLayerAdjustment); pw.println(" mDisplayFrozen=" + mDisplayFrozen + " mWindowsFreezingScreen=" + mWindowsFreezingScreen + " mAppsFreezingScreen=" + mAppsFreezingScreen); @@ -8249,7 +8735,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " mLastBinder=" + mKeyWaiter.mLastBinder); pw.println(" mFinished=" + mKeyWaiter.mFinished + " mGotFirstWindow=" + mKeyWaiter.mGotFirstWindow - + " mEventDispatching" + mKeyWaiter.mEventDispatching + + " mEventDispatching=" + mKeyWaiter.mEventDispatching + " mTimeToSwitch=" + mKeyWaiter.mTimeToSwitch); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 361c7f7..9d720d1 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -48,6 +48,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; @@ -96,6 +97,7 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.PrintWriter; +import java.lang.IllegalStateException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; @@ -157,7 +159,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int LOG_AM_BROADCAST_DISCARD_APP = 30025; static final int LOG_AM_CREATE_SERVICE = 30030; static final int LOG_AM_DESTROY_SERVICE = 30031; - + static final int LOG_AM_PROCESS_CRASHED_TOO_MUCH = 30032; + static final int LOG_AM_DROP_PROCESS = 30033; + static final int LOG_AM_SERVICE_CRASHED_TOO_MUCH = 30034; + static final int LOG_AM_SCHEDULE_SERVICE_RESTART = 30035; + static final int LOG_AM_PROVIDER_LOST_PROCESS = 30036; + static final int LOG_BOOT_PROGRESS_AMS_READY = 3040; static final int LOG_BOOT_PROGRESS_ENABLE_SCREEN = 3050; @@ -197,6 +204,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ static final int LAUNCH_TIMEOUT = 10*1000; + // How long we wait for a launched process to attach to the activity manager + // before we decide it's never going to come up for real. + static final int PROC_START_TIMEOUT = 10*1000; + // How long we wait until giving up on the last activity telling us it // is idle. static final int IDLE_TIMEOUT = 10*1000; @@ -252,6 +263,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final int HIDDEN_APP_MAX_ADJ; static int HIDDEN_APP_MIN_ADJ; + // This is a process holding the home application -- we want to try + // avoiding killing it, even if it would normally be in the background, + // because the user interacts with it so much. + final int HOME_APP_ADJ; + // This is a process holding a secondary server -- killing it will not // have much of an impact as far as the user is concerned. Value set in // system/rootdir/init.rc on startup. @@ -279,6 +295,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Corresponding memory levels for above adjustments. final int EMPTY_APP_MEM; final int HIDDEN_APP_MEM; + final int HOME_APP_MEM; final int SECONDARY_SERVER_MEM; final int VISIBLE_APP_MEM; final int FOREGROUND_APP_MEM; @@ -350,6 +367,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord mFocusedActivity = null; /** + * This is the last activity that we put into the paused state. This is + * used to determine if we need to do an activity transition while sleeping, + * when we normally hold the top activity paused. + */ + HistoryRecord mLastPausedActivity = null; + + /** * List of activities that are waiting for a new activity * to become visible before completing whatever operation they are * supposed to do. @@ -469,6 +493,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new ArrayList<ProcessRecord>(); /** + * This is the process holding what we currently consider to be + * the "home" activity. + */ + private ProcessRecord mHomeProcess; + + /** * List of running activities, sorted by recent usage. * The first entry in the list is the least recently used. * It contains HistoryRecord objects. @@ -631,6 +661,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * any user id that can impact battery performance. */ final BatteryStatsService mBatteryStatsService; + + /** + * information about component usage + */ + final UsageStatsService mUsageStatsService; /** * Current configuration information. HistoryRecord objects are given @@ -799,6 +834,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int DESTROY_TIMEOUT_MSG = 17; static final int SERVICE_ERROR_MSG = 18; static final int RESUME_TOP_ACTIVITY_MSG = 19; + static final int PROC_START_TIMEOUT_MSG = 20; AlertDialog mUidAlert; @@ -840,17 +876,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } break; case SHOW_NOT_RESPONDING_MSG: { - HashMap data = (HashMap) msg.obj; - // This needs to be *un*synchronized to avoid deadlock. - Checkin.logEvent(mContext.getContentResolver(), - Checkin.Events.Tag.SYSTEM_APP_NOT_RESPONDING, - (String)data.get("info")); synchronized (ActivityManagerService.this) { + HashMap data = (HashMap) msg.obj; ProcessRecord proc = (ProcessRecord)data.get("app"); if (proc != null && proc.anrDialog != null) { Log.e(TAG, "App already has anr dialog: " + proc); return; } + + broadcastIntentLocked(null, null, new Intent("android.intent.action.ANR"), + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID); + Dialog d = new AppNotRespondingDialog(ActivityManagerService.this, mContext, proc, (HistoryRecord)data.get("activity")); d.show(); @@ -981,6 +1018,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen resumeTopActivityLocked(null); } } + case PROC_START_TIMEOUT_MSG: { + ProcessRecord app = (ProcessRecord)msg.obj; + synchronized (ActivityManagerService.this) { + processStartTimedOutLocked(app); + } + } } } }; @@ -1053,6 +1096,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen m.mLaunchingActivity.setReferenceCounted(false); m.mBatteryStatsService.publish(context); + m.mUsageStatsService.publish(context); synchronized (thr) { thr.mReady = true; @@ -1186,8 +1230,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen procs = service.mLRUProcesses; } } - pw.println("Applications Memory Usage (kB):"); - dumpApplicationMemoryUsage(fd, pw, procs, " "); + dumpApplicationMemoryUsage(fd, pw, procs, " ", args); } } @@ -1224,6 +1267,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen systemDir, "batterystats.bin").toString()); mBatteryStatsService.getActiveStatistics().readLocked(); mBatteryStatsService.getActiveStatistics().writeLocked(); + + mUsageStatsService = new UsageStatsService( new File( + systemDir, "usagestats.bin").toString()); mConfiguration.makeDefault(); mProcessStats.init(); @@ -1238,6 +1284,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ")); SECONDARY_SERVER_ADJ = Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ")); + HOME_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ")); HIDDEN_APP_MIN_ADJ = Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MIN_ADJ")); CONTENT_PROVIDER_ADJ = @@ -1251,6 +1299,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE; SECONDARY_SERVER_MEM = Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + HOME_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE; HIDDEN_APP_MEM = Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MEM"))*PAGE_SIZE; EMPTY_APP_MEM = @@ -1519,9 +1569,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen System.identityHashCode(r), r.task.taskId, r.shortComponentName); } + if (r.isHomeActivity) { + mHomeProcess = app; + } app.thread.scheduleLaunchActivity(new Intent(r.intent), r, r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward()); + // Update usage stats for launched activity + updateUsageStats(r, true); } catch (RemoteException e) { if (r.launchFailed) { // This is the second time we failed -- finish activity @@ -1668,6 +1723,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (app.pid > 0 && app.pid != MY_PID) { synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(app.pid); + mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } app.pid = 0; } @@ -1760,6 +1816,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.removed = false; synchronized (mPidsSelfLocked) { this.mPidsSelfLocked.put(pid, app); + Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); + msg.obj = app; + mHandler.sendMessageDelayed(msg, PROC_START_TIMEOUT); } } else { app.pid = 0; @@ -1775,7 +1834,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void startPausingLocked(boolean userLeaving) { + private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) { if (mPausingActivity != null) { RuntimeException e = new RuntimeException(); Log.e(TAG, "Trying to pause when pause is already pending for " @@ -1791,6 +1850,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (DEBUG_PAUSE) Log.v(TAG, "Start pausing: " + prev); mResumedActivity = null; mPausingActivity = prev; + mLastPausedActivity = prev; prev.state = ActivityState.PAUSING; prev.task.touchActiveTime(); @@ -1804,13 +1864,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen prev.shortComponentName); prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving, prev.configChangeFlags); + updateUsageStats(prev, false); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. Log.w(TAG, "Exception thrown during pause", e); mPausingActivity = null; + mLastPausedActivity = null; } } else { mPausingActivity = null; + mLastPausedActivity = null; } // If we are not going to sleep, we want to ensure the device is @@ -1827,9 +1890,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity != null) { // Have the window manager pause its key dispatching until the new - // activity has started... - prev.pauseKeyDispatchingLocked(); - + // activity has started. If we're pausing the activity just because + // the screen is being turned off and the UI is sleeping, don't interrupt + // key dispatch; the same activity will pick it up again on wakeup. + if (!uiSleeping) { + prev.pauseKeyDispatchingLocked(); + } else { + if (DEBUG_PAUSE) Log.v(TAG, "Key dispatch not paused for screen off"); + } + // Schedule a pause timeout in case the app doesn't respond. // We don't give it much time because this directly impacts the // responsiveness seen by the user. @@ -1848,6 +1917,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final void completePauseLocked() { HistoryRecord prev = mPausingActivity; if (DEBUG_PAUSE) Log.v(TAG, "Complete pause: " + prev); + if (prev != null) { if (prev.finishing) { if (DEBUG_PAUSE) Log.v(TAG, "Executing finish of activity: " + prev); @@ -1966,7 +2036,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // First: if this is not the current activity being started, make // sure it matches the current configuration. if (r != starting && doThisProcess) { - ensureActivityConfigurationLocked(r); + ensureActivityConfigurationLocked(r, 0); } if (r.app == null || r.app.thread == null) { @@ -2085,6 +2155,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ensureActivitiesVisibleLocked(r, starting, null, configChanges); } } + + private void updateUsageStats(HistoryRecord resumedComponent, boolean resumed) { + if (resumed) { + mUsageStatsService.noteResumeComponent(resumedComponent.realActivity); + } else { + mUsageStatsService.notePauseComponent(resumedComponent.realActivity); + } + } /** * Ensure that the top activity in the stack is resumed. @@ -2134,7 +2212,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (app == null || app.instrumentationClass == null) { intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); startActivityLocked(null, intent, null, null, 0, aInfo, - null, null, 0, 0, 0, false); + null, null, 0, 0, 0, false, false); } } return true; @@ -2148,6 +2226,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } + // If we are sleeping, and there is no resumed activity, and the top + // activity is paused, well that is the state we want. + if (mSleeping && mLastPausedActivity == next && next.state == ActivityState.PAUSED) { + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + mWindowManager.executeAppTransition(); + return false; + } + // The activity may be waiting for stop, but that is no longer // appropriate for it. mStoppingActivities.remove(next); @@ -2166,7 +2253,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // can be resumed... if (mResumedActivity != null) { if (DEBUG_SWITCH) Log.v(TAG, "Skip resume: need to start pausing"); - startPausingLocked(userLeaving); + startPausingLocked(userLeaving, false); return true; } @@ -2267,6 +2354,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Do over! mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG); } + mWindowManager.executeAppTransition(); return true; } @@ -2290,6 +2378,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen EventLog.writeEvent(LOG_AM_RESUME_ACTIVITY, System.identityHashCode(next), next.task.taskId, next.shortComponentName); + updateUsageStats(next, true); next.app.thread.scheduleResumeActivity(next, isNextTransitionForward()); @@ -2453,9 +2542,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We don't want to reuse the previous starting preview if: // (1) The current activity is in a different task. if (prev.task != r.task) prev = null; - // (2) The current activity is not the first in the task. - else if (!prev.frontOfTask) prev = null; - // (3) The current activity is already displayed. + // (2) The current activity is already displayed. else if (prev.nowVisible) prev = null; } mWindowManager.setAppStartingWindow( @@ -2537,6 +2624,40 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } /** + * Find the activity in the history stack within the given task. Returns + * the index within the history at which it's found, or < 0 if not found. + */ + private final int findActivityInHistoryLocked(HistoryRecord r, int task) { + int i = mHistory.size(); + while (i > 0) { + i--; + HistoryRecord candidate = (HistoryRecord)mHistory.get(i); + if (candidate.task.taskId != task) { + break; + } + if (candidate.realActivity.equals(r.realActivity)) { + return i; + } + } + + return -1; + } + + /** + * Reorder the history stack so that the activity at the given index is + * brought to the front. + */ + private final HistoryRecord moveActivityToFrontLocked(int where) { + HistoryRecord newTop = (HistoryRecord)mHistory.remove(where); + int top = mHistory.size(); + HistoryRecord oldTop = (HistoryRecord)mHistory.get(top-1); + mHistory.add(top, newTop); + oldTop.frontOfTask = false; + newTop.frontOfTask = true; + return newTop; + } + + /** * Deliver a new Intent to an existing activity, so that its onNewIntent() * method will be called at the proper time. */ @@ -2550,8 +2671,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.thread.scheduleNewIntent(ar, r); sent = true; } catch (Exception e) { - Log.w(TAG, - "Exception thrown sending new intent to " + r, e); + Log.w(TAG, "Exception thrown sending new intent to " + r, e); } } if (!sent) { @@ -2573,7 +2693,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Uri[] grantedUriPermissions, int grantedMode, ActivityInfo aInfo, IBinder resultTo, String resultWho, int requestCode, - int callingPid, int callingUid, boolean onlyIfNeeded) { + int callingPid, int callingUid, boolean onlyIfNeeded, + boolean componentSpecified) { Log.i(TAG, "Starting activity: " + intent); HistoryRecord sourceRecord = null; @@ -2688,7 +2809,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord r = new HistoryRecord(this, callerApp, callingUid, intent, resolvedType, aInfo, mConfiguration, - resultRecord, resultWho, requestCode); + resultRecord, resultWho, requestCode, componentSpecified); r.startTime = SystemClock.uptimeMillis(); HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) @@ -2969,6 +3090,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen resumeTopActivityLocked(null); return START_DELIVERED_TO_TOP; } + } else if (!addingToTask && + (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) { + // In this case, we are launching an activity in our own task + // that may already be running somewhere in the history, and + // we want to shuffle it to the front of the stack if so. + int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId); + if (where >= 0) { + HistoryRecord top = moveActivityToFrontLocked(where); + logStartActivity(LOG_AM_NEW_INTENT, r, top.task); + deliverNewIntentLocked(top, r.intent); + resumeTopActivityLocked(null); + return START_DELIVERED_TO_TOP; + } } // An existing activity is starting this new activity, so we want // to keep the new one in the same task as the one that is starting @@ -3009,6 +3143,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen throw new IllegalArgumentException("File descriptors passed in Intent"); } + final boolean componentSpecified = intent.getComponent() != null; + // Don't modify the client's object! intent = new Intent(intent); @@ -3048,7 +3184,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int res = startActivityLocked(caller, intent, resolvedType, grantedUriPermissions, grantedMode, aInfo, resultTo, resultWho, requestCode, -1, -1, - onlyIfNeeded); + onlyIfNeeded, componentSpecified); Binder.restoreCallingIdentity(origId); return res; } @@ -3138,7 +3274,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // those are not yet exposed to user code, so there is no need. int res = startActivityLocked(r.app.thread, intent, r.resolvedType, null, 0, aInfo, resultTo, resultWho, - requestCode, -1, r.launchedFromUid, false); + requestCode, -1, r.launchedFromUid, false, false); Binder.restoreCallingIdentity(origId); r.finishing = wasFinishing; @@ -3152,6 +3288,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final int startActivityInPackage(int uid, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, boolean onlyIfNeeded) { + final boolean componentSpecified = intent.getComponent() != null; + // Don't modify the client's object! intent = new Intent(intent); @@ -3182,7 +3320,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { return startActivityLocked(null, intent, resolvedType, null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid, - onlyIfNeeded); + onlyIfNeeded, componentSpecified); } } @@ -3388,7 +3526,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity == null) { if (DEBUG_PAUSE) Log.v(TAG, "Finish needs to pause: " + r); if (DEBUG_USER_LEAVING) Log.v(TAG, "finish() => pause with userLeaving=false"); - startPausingLocked(false); + startPausingLocked(false, false); } } else if (r.state != ActivityState.PAUSING) { @@ -3522,9 +3660,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.thread.scheduleSendResult(r, list); return; } catch (Exception e) { - Log.w(TAG, - "Exception thrown sending result to " + r, - e); + Log.w(TAG, "Exception thrown sending result to " + r, e); } } @@ -3710,8 +3846,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.configChangeFlags = 0; if (!mLRUActivities.remove(r)) { - Log.w(TAG, "Activity " + r - + " being finished, but not in LRU list"); + Log.w(TAG, "Activity " + r + " being finished, but not in LRU list"); } return removedFromHistory; @@ -3753,6 +3888,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (DEBUG_PAUSE) Log.v(TAG, "App died while pausing: " + mPausingActivity); mPausingActivity = null; } + if (mLastPausedActivity != null && mLastPausedActivity.app == app) { + mLastPausedActivity = null; + } // Remove this application's activities from active lists. removeHistoryRecordsForAppLocked(mLRUActivities, app); @@ -3958,9 +4096,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen info.append("\nCPU usage:\n"); info.append(processInfo); } - info.append("\n/proc/meminfo:\n"); - info.append(readFile("/proc/meminfo")); - Log.i(TAG, info.toString()); // The application is not responding. Dump as many thread traces as we can. @@ -4012,7 +4147,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen msg.what = SHOW_NOT_RESPONDING_MSG; msg.obj = map; map.put("app", app); - map.put("info", info.toString()); if (activity != null) { map.put("activity", activity); } @@ -4142,21 +4276,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen long callingId = Binder.clearCallingIdentity(); try { IPackageManager pm = ActivityThread.getPackageManager(); + int pkgUid = -1; synchronized(this) { - int pkgUid = -1; try { pkgUid = pm.getPackageUid(packageName); } catch (RemoteException e) { } if (pkgUid == -1) { - Log.w(TAG, "Invalid packageName:"+packageName); + Log.w(TAG, "Invalid packageName:" + packageName); return false; } if (uid == pkgUid || checkComponentPermission( android.Manifest.permission.CLEAR_APP_USER_DATA, pid, uid, -1) == PackageManager.PERMISSION_GRANTED) { - uninstallPackageLocked(packageName, pkgUid, false); + restartPackageLocked(packageName, pkgUid); } else { throw new SecurityException(pid+" does not have permission:"+ android.Manifest.permission.CLEAR_APP_USER_DATA+" to clear data" + @@ -4167,6 +4301,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { //clear application user data pm.clearApplicationUserData(packageName, observer); + Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED, + Uri.fromParts("package", packageName, null)); + intent.putExtra(Intent.EXTRA_UID, pkgUid); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID); } catch (RemoteException e) { } } finally { @@ -4188,25 +4328,48 @@ public final class ActivityManagerService extends ActivityManagerNative implemen long callingId = Binder.clearCallingIdentity(); try { + IPackageManager pm = ActivityThread.getPackageManager(); + int pkgUid = -1; synchronized(this) { - uninstallPackageLocked(packageName, -1, false); - broadcastIntentLocked(null, null, - new Intent(Intent.ACTION_PACKAGE_RESTARTED, - Uri.fromParts("package", packageName, null)), - null, null, 0, null, null, null, - false, false, MY_PID, Process.SYSTEM_UID); + try { + pkgUid = pm.getPackageUid(packageName); + } catch (RemoteException e) { + } + if (pkgUid == -1) { + Log.w(TAG, "Invalid packageName: " + packageName); + return; + } + restartPackageLocked(packageName, pkgUid); } } finally { Binder.restoreCallingIdentity(callingId); } } + private void restartPackageLocked(final String packageName, int uid) { + uninstallPackageLocked(packageName, uid, false); + Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, + Uri.fromParts("package", packageName, null)); + intent.putExtra(Intent.EXTRA_UID, uid); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID); + } + private final void uninstallPackageLocked(String name, int uid, boolean callerWillRestart) { if (Config.LOGD) Log.d(TAG, "Uninstalling process " + name); int i, N; + final String procNamePrefix = name + ":"; + if (uid < 0) { + try { + uid = ActivityThread.getPackageManager().getPackageUid(name); + } catch (RemoteException e) { + } + } + Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator(); while (badApps.hasNext()) { SparseArray<Long> ba = badApps.next(); @@ -4217,14 +4380,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); - final String procNamePrefix = name + ":"; - if (uid < 0) { - try { - uid = ActivityThread.getPackageManager().getPackageUid(name); - } catch (RemoteException e) { - } - } - // Remove all processes this package may have touched: all with the // same UID (except for the system or root user), and all whose name // matches the package name. @@ -4294,6 +4449,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int pid = app.pid; synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(pid); + mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } handleAppDiedLocked(app, true); mLRUProcesses.remove(app); @@ -4313,6 +4469,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return needRestart; } + private final void processStartTimedOutLocked(ProcessRecord app) { + final int pid = app.pid; + boolean gone = false; + synchronized (mPidsSelfLocked) { + ProcessRecord knownApp = mPidsSelfLocked.get(pid); + if (knownApp != null && knownApp.thread == null) { + mPidsSelfLocked.remove(pid); + gone = true; + } + } + + if (gone) { + Log.w(TAG, "Process " + app + " failed to attach"); + mProcessNames.remove(app.processName, app.info.uid); + Process.killProcess(pid); + if (mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid) { + Log.w(TAG, "Unattached app died before broadcast acknowledged, skipping"); + mPendingBroadcast = null; + scheduleBroadcastsLocked(); + } + } else { + Log.w(TAG, "Spurious process start timeout - pid not known for " + app); + } + } + private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { @@ -4334,6 +4515,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (app == null) { Log.w(TAG, "No pending application record for pid " + pid + " (IApplicationThread " + thread + "); dropping process"); + EventLog.writeEvent(LOG_AM_DROP_PROCESS, pid); if (pid > 0 && pid != MY_PID) { Process.killProcess(pid); } else { @@ -4375,6 +4557,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.foregroundServices = false; app.debugging = false; + mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); + List providers = generateApplicationProvidersLocked(app); if (localLOGV) Log.v( @@ -5523,9 +5707,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) { outInfo.availMem = Process.getFreeMemory(); - outInfo.threshold = SECONDARY_SERVER_MEM; + outInfo.threshold = HOME_APP_MEM; outInfo.lowMemory = outInfo.availMem < - (SECONDARY_SERVER_MEM + ((HIDDEN_APP_MEM-SECONDARY_SERVER_MEM)/2)); + (HOME_APP_MEM + ((HIDDEN_APP_MEM-HOME_APP_MEM)/2)); } // ========================================================= @@ -6490,10 +6674,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (caller != null) { r = getRecordForAppLocked(caller); if (r == null) { - throw new SecurityException( - "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() - + ") when getting content provider " + name); + throw new SecurityException( + "Unable to find app for caller " + caller + + " (pid=" + Binder.getCallingPid() + + ") when getting content provider " + name); } } @@ -6650,6 +6834,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": launching app became null"); + EventLog.writeEvent(LOG_AM_PROVIDER_LOST_PROCESS, + cpi.applicationInfo.packageName, + cpi.applicationInfo.uid, name); + return null; } try { cpr.wait(); @@ -6755,9 +6943,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ContentProviderRecord dst = (ContentProviderRecord)r.pubProviders.get(src.info.name); if (dst != null) { - dst.provider = src.provider; - dst.app = r; - mProvidersByClass.put(dst.info.name, dst); String names[] = dst.info.authority.split(";"); for (int j = 0; j < names.length; j++) { @@ -6774,6 +6959,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } synchronized (dst) { + dst.provider = src.provider; + dst.app = r; dst.notifyAll(); } updateOomAdjLocked(r); @@ -6904,7 +7091,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity == null) { if (DEBUG_PAUSE) Log.v(TAG, "Sleep needs to pause..."); if (DEBUG_USER_LEAVING) Log.v(TAG, "Sleep => pause with userLeaving=false"); - startPausingLocked(false); + startPausingLocked(false, true); } } } @@ -7226,6 +7413,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public boolean testIsSystemReady() { + // no need to synchronize(this) just to read & return the value + return mSystemReady; + } + public void systemReady() { // In the simulator, startRunning will never have been called, which // normally sets a few crucial variables. Do it here instead. @@ -7405,6 +7597,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // This process loses! Log.w(TAG, "Process " + app.info.processName + " has crashed too many times: killing!"); + EventLog.writeEvent(LOG_AM_PROCESS_CRASHED_TOO_MUCH, + app.info.processName, app.info.uid); killServicesLocked(app, false); for (int i=mHistory.size()-1; i>=0; i--) { HistoryRecord r = (HistoryRecord)mHistory.get(i); @@ -7628,6 +7822,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ActivityManager.RunningAppProcessInfo currApp = new ActivityManager.RunningAppProcessInfo(app.processName, app.pid, app.getPackageList()); + int adj = app.curAdj; + if (adj >= CONTENT_PROVIDER_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY; + } else if (adj >= HIDDEN_APP_MIN_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; + currApp.lru = adj - HIDDEN_APP_MIN_ADJ + 1; + } else if (adj >= HOME_APP_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; + currApp.lru = 0; + } else if (adj >= SECONDARY_SERVER_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; + } else if (adj >= VISIBLE_APP_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; + } else { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; + } + //Log.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance + // + " lru=" + currApp.lru); if (runList == null) { runList = new ArrayList<ActivityManager.RunningAppProcessInfo>(); } @@ -7679,6 +7891,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pw.println(" mPausingActivity: " + mPausingActivity); pw.println(" mResumedActivity: " + mResumedActivity); pw.println(" mFocusedActivity: " + mFocusedActivity); + pw.println(" mLastPausedActivity: " + mLastPausedActivity); if (mRecentTasks.size() > 0) { pw.println(" "); @@ -7816,6 +8029,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pw.println(" "); pw.println(" Total persistent processes: " + numPers); + pw.println(" mHomeProcess: " + mHomeProcess); pw.println(" mConfiguration: " + mConfiguration); pw.println(" mStartRunning=" + mStartRunning + " mSystemReady=" + mSystemReady @@ -8176,28 +8390,53 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private static final void dumpApplicationMemoryUsage(FileDescriptor fd, - PrintWriter pw, List list, String prefix) { + PrintWriter pw, List list, String prefix, String[] args) { + final boolean isCheckinRequest = scanArgs(args, "-c"); + long uptime = SystemClock.uptimeMillis(); + long realtime = SystemClock.elapsedRealtime(); + + if (isCheckinRequest) { + // short checkin version + pw.println(uptime + "," + realtime); + pw.flush(); + } else { + pw.println("Applications Memory Usage (kB):"); + pw.println("Uptime: " + uptime + " Realtime: " + realtime); + } for (int i = list.size() - 1 ; i >= 0 ; i--) { ProcessRecord r = (ProcessRecord)list.get(i); if (r.thread != null) { - pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **"); - pw.flush(); - - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); + if (!isCheckinRequest) { + pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **"); + pw.flush(); + } try { - data.writeFileDescriptor(fd); - r.thread.asBinder().transact(IBinder.DUMP_TRANSACTION, data, reply, 0); - + r.thread.asBinder().dump(fd, args); } catch (RemoteException e) { - pw.println("Got RemoteException!"); - pw.flush(); + if (!isCheckinRequest) { + pw.println("Got RemoteException!"); + pw.flush(); + } } + } + } + } - data.recycle(); - reply.recycle(); + /** + * Searches array of arguments for the specified string + * @param args array of argument strings + * @param value value to search for + * @return true if the value is contained in the array + */ + private static boolean scanArgs(String[] args, String value) { + if (args != null) { + for (String arg : args) { + if (value.equals(arg)) { + return true; + } } } + return false; } private final int indexOfTokenLocked(IBinder token, boolean required) { @@ -8295,6 +8534,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (sr.crashCount >= 2) { Log.w(TAG, "Service crashed " + sr.crashCount + " times, stopping: " + sr); + EventLog.writeEvent(LOG_AM_SERVICE_CRASHED_TOO_MUCH, + sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr, true); } else if (!allowRestart) { bringDownServiceLocked(sr, true); @@ -8313,7 +8554,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final void removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr) { - cpr.launchingApp = null; + synchronized (cpr) { + cpr.launchingApp = null; + cpr.notifyAll(); + } mProvidersByClass.remove(cpr.info.name); String names[] = cpr.info.authority.split(";"); @@ -8395,6 +8639,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen break; } } + } else { + i = NL; } if (i >= NL) { @@ -8467,8 +8713,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } mProcessesOnHold.remove(app); + if (app == mHomeProcess) { + mHomeProcess = null; + } + if (restart) { - // We have component that still need to be running in the + // We have components that still need to be running in the // process, so re-launch it. mProcessNames.put(app.processName, app.info.uid, app); startProcessLocked(app, "restart", app.processName); @@ -8476,6 +8726,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Goodbye! synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(app.pid); + mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } app.pid = 0; } @@ -8818,6 +9069,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; Log.w(TAG, "Scheduling restart of crashed service " + r.shortName + " in " + r.restartDelay + "ms"); + EventLog.writeEvent(LOG_AM_SCHEDULE_SERVICE_RESTART, + r.shortName, r.restartDelay); Message msg = Message.obtain(); msg.what = SERVICE_ERROR_MSG; @@ -9120,6 +9373,36 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return 0; } + public IBinder peekService(Intent service, String resolvedType) { + // Refuse possible leaked file descriptors + if (service != null && service.hasFileDescriptors() == true) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + + IBinder ret = null; + + synchronized(this) { + ServiceLookupResult r = findServiceLocked(service, resolvedType); + + if (r != null) { + // r.record is null if findServiceLocked() failed the caller permission check + if (r.record == null) { + throw new SecurityException( + "Permission Denial: Accessing service " + r.record.name + + " from pid=" + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + r.permission); + } + IntentBindRecord ib = r.record.bindings.get(r.record.intent); + if (ib != null) { + ret = ib.binder; + } + } + } + + return ret; + } + public boolean stopServiceToken(ComponentName className, IBinder token, int startId) { synchronized(this) { @@ -9928,6 +10211,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { + 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); + } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0){ + Log.e(TAG, "Attempt to launch receivers of broadcast intent " + intent + + " before boot completion"); + throw new IllegalStateException("Cannot broadcast before boot completed"); + } + } + final ProcessRecord callerApp = getRecordForAppLocked(caller); final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); @@ -10639,6 +10936,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // ========================================================= // CONFIGURATION // ========================================================= + + public ConfigurationInfo getDeviceConfigurationInfo() { + ConfigurationInfo config = new ConfigurationInfo(); + synchronized (this) { + config.reqTouchScreen = mConfiguration.touchscreen; + config.reqKeyboardType = mConfiguration.keyboard; + config.reqNavigation = mConfiguration.navigation; + if (mConfiguration.navigation != Configuration.NAVIGATION_NONAV) { + config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; + } + if (mConfiguration.keyboard != Configuration.KEYBOARD_UNDEFINED) { + config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; + } + } + return config; + } public Configuration getConfiguration() { Configuration ci; @@ -10653,6 +10966,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "updateConfiguration()"); synchronized(this) { + if (values == null && mWindowManager != null) { + // sentinel: fetch the current configuration from the window manager + values = mWindowManager.computeNewConfiguration(); + } + final long origId = Binder.clearCallingIdentity(); updateConfigurationLocked(values, null); Binder.restoreCallingIdentity(origId); @@ -10718,7 +11036,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (starting != null) { - kept = ensureActivityConfigurationLocked(starting); + kept = ensureActivityConfigurationLocked(starting, changes); if (kept) { // If this didn't result in the starting activity being // destroyed, then we need to make sure at this point that all @@ -10775,7 +11093,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * for whatever reason. Ensures the HistoryRecord is updated with the * correct configuration and all other bookkeeping is handled. */ - private final boolean ensureActivityConfigurationLocked(HistoryRecord r) { + private final boolean ensureActivityConfigurationLocked(HistoryRecord r, + int globalChanges) { if (DEBUG_SWITCH) Log.i(TAG, "Ensuring correct configuration: " + r); // Short circuit: if the two configurations are the exact same @@ -10822,7 +11141,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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, 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); destroyActivityLocked(r, true); @@ -10903,9 +11222,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.isForeground = false; - // Right now there are three interesting states: it is - // either the foreground app, background with activities, - // or background without activities. + // Determine the importance of the process, starting with most + // important to least, and assign an appropriate OOM adjustment. int adj; int N; if (app == TOP_APP || app.instrumentationClass != null @@ -10925,6 +11243,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else if (app.foregroundServices || app.forcingToForeground != null) { // The user is aware of this app, so make it visible. adj = VISIBLE_APP_ADJ; + } else if (app == mHomeProcess) { + // This process is hosting what we currently consider to be the + // home app, so we don't want to let it go into the background. + adj = HOME_APP_ADJ; } else if ((N=app.activities.size()) != 0) { // This app is in the background with paused activities. adj = hiddenAdj; @@ -10940,7 +11262,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen adj = EMPTY_APP_ADJ; } - // By default, we use the computed adjusted. It may be changed if + // By default, we use the computed adjustment. It may be changed if // there are applications dependent on our services or providers, but // this gives us a baseline and makes sure we don't get into an // infinite recursion. @@ -11461,7 +11783,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We can finish this one if we have its icicle saved and // it is not persistent. - if ((r.haveState || !r.stateNotNeeded) + if ((r.haveState || !r.stateNotNeeded) && !r.visible && r.stopped && !r.persistent && !r.finishing) { final int origSize = mLRUActivities.size(); destroyActivityLocked(r, true); @@ -11501,6 +11823,56 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public boolean profileControl(String process, boolean start, + String path) throws RemoteException { + + synchronized (this) { + // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to + // its own permission. + if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.SET_ACTIVITY_WATCHER); + } + + ProcessRecord proc = null; + try { + int pid = Integer.parseInt(process); + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(pid); + } + } catch (NumberFormatException e) { + } + + if (proc == null) { + HashMap<String, SparseArray<ProcessRecord>> all + = mProcessNames.getMap(); + SparseArray<ProcessRecord> procs = all.get(process); + if (procs != null && procs.size() > 0) { + proc = procs.valueAt(0); + } + } + + if (proc == null || proc.thread == null) { + throw new IllegalArgumentException("Unknown process: " + process); + } + + boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0")); + if (isSecure) { + if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { + throw new SecurityException("Process not debuggable: " + proc); + } + } + + try { + proc.thread.profilerControl(start, path); + return true; + } catch (RemoteException e) { + throw new IllegalStateException("Process disappeared"); + } + } + } + /** In this method we try to acquire our lock to make sure that we have not deadlocked */ public void monitor() { synchronized (this) { } diff --git a/services/java/com/android/server/am/BaseErrorDialog.java b/services/java/com/android/server/am/BaseErrorDialog.java index 4f62f62..bed2768 100644 --- a/services/java/com/android/server/am/BaseErrorDialog.java +++ b/services/java/com/android/server/am/BaseErrorDialog.java @@ -31,6 +31,8 @@ class BaseErrorDialog extends AlertDialog { super(context, com.android.internal.R.style.Theme_Dialog_AppError); getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); getWindow().setTitle("Error Dialog"); setIcon(R.drawable.ic_dialog_alert); } diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index bf1bc8c..27d0401 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -22,8 +22,10 @@ import com.android.internal.os.BatteryStatsImpl; import android.content.Context; import android.os.Binder; import android.os.IBinder; +import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; +import android.util.PrintWriterPrinter; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -58,14 +60,23 @@ public final class BatteryStatsService extends IBatteryStats.Stub { /** * @return the current statistics object, which may be modified - * to reflect events that affect battery usage. + * to reflect events that affect battery usage. You must lock the + * stats object before doing anything with it. */ public BatteryStatsImpl getActiveStatistics() { return mStats; } - public BatteryStatsImpl getStatistics() { - return mStats; + public byte[] getStatistics() { + mContext.enforceCallingPermission( + android.Manifest.permission.BATTERY_STATS, null); + //Log.i("foo", "SENDING BATTERY INFO:"); + //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo")); + Parcel out = Parcel.obtain(); + mStats.writeToParcel(out, 0); + byte[] data = out.marshall(); + out.recycle(); + return data; } public void noteStartWakelock(int uid, String name, int type) { @@ -95,6 +106,48 @@ public final class BatteryStatsService extends IBatteryStats.Stub { mStats.getUidStatsLocked(uid).noteStopSensor(sensor); } } + + public void noteStartGps(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteStartGps(uid); + } + } + + public void noteStopGps(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteStopGps(uid); + } + } + + public void noteScreenOn() { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteScreenOnLocked(); + } + } + + public void noteScreenOff() { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteScreenOffLocked(); + } + } + + public void notePhoneOn() { + enforceCallingPermission(); + synchronized (mStats) { + mStats.notePhoneOnLocked(); + } + } + + public void notePhoneOff() { + enforceCallingPermission(); + synchronized (mStats) { + mStats.notePhoneOffLocked(); + } + } public boolean isOnBattery() { return mStats.isOnBattery(); @@ -106,10 +159,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } public long getAwakeTimeBattery() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); return mStats.getAwakeTimeBattery(); } public long getAwakeTimePlugged() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); return mStats.getAwakeTimePlugged(); } @@ -117,14 +174,24 @@ public final class BatteryStatsService extends IBatteryStats.Stub { if (Binder.getCallingPid() == Process.myPid()) { return; } - mContext.enforcePermission(android.Manifest.permission.BATTERY_STATS, + mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, Binder.getCallingPid(), Binder.getCallingUid(), null); } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - synchronized (this) { - mStats.dumpLocked(fd, pw, args); + synchronized (mStats) { + boolean isCheckin = false; + if (args != null) { + for (String arg : args) { + if ("-c".equals(arg)) { + isCheckin = true; + break; + } + } + } + if (isCheckin) mStats.dumpCheckinLocked(pw, args); + else mStats.dumpLocked(new PrintWriterPrinter(pw)); } } } diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java index b370b1c..0f62471 100644 --- a/services/java/com/android/server/am/HistoryRecord.java +++ b/services/java/com/android/server/am/HistoryRecord.java @@ -23,6 +23,7 @@ import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.os.Bundle; @@ -54,6 +55,8 @@ class HistoryRecord extends IApplicationToken.Stub { final String taskAffinity; // as per ActivityInfo.taskAffinity final boolean stateNotNeeded; // As per ActivityInfo.flags final boolean fullscreen; // covers the full screen? + final boolean componentSpecified; // did caller specifiy an explicit component? + final boolean isHomeActivity; // do we consider this to be a home activity? final String baseDir; // where activity source (resources etc) located final String resDir; // where public activity source (public resources etc) located final String dataDir; // where activity data should go @@ -101,8 +104,8 @@ class HistoryRecord extends IApplicationToken.Stub { pw.println(prefix + this); pw.println(prefix + "packageName=" + packageName + " processName=" + processName); - pw.println(prefix + "app=" + app); - pw.println(prefix + "launchedFromUid=" + launchedFromUid); + pw.println(prefix + "launchedFromUid=" + launchedFromUid + + " app=" + app); pw.println(prefix + intent); pw.println(prefix + "frontOfTask=" + frontOfTask + " task=" + task); pw.println(prefix + "taskAffinity=" + taskAffinity); @@ -111,6 +114,9 @@ class HistoryRecord extends IApplicationToken.Stub { pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes) + " icon=0x" + Integer.toHexString(icon) + " theme=0x" + Integer.toHexString(theme)); + pw.println(prefix + "stateNotNeeded=" + stateNotNeeded + + " componentSpecified=" + componentSpecified + + " isHomeActivity=" + isHomeActivity); pw.println(prefix + "configuration=" + configuration); pw.println(prefix + "resultTo=" + resultTo + " resultWho=" + resultWho + " resultCode=" + requestCode); @@ -139,13 +145,15 @@ class HistoryRecord extends IApplicationToken.Stub { HistoryRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromUid, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, - HistoryRecord _resultTo, String _resultWho, int _reqCode) { + HistoryRecord _resultTo, String _resultWho, int _reqCode, + boolean _componentSpecified) { service = _service; info = aInfo; launchedFromUid = _launchedFromUid; intent = _intent; shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; + componentSpecified = _componentSpecified; configuration = _configuration; resultTo = _resultTo; resultWho = _resultWho; @@ -184,6 +192,11 @@ class HistoryRecord extends IApplicationToken.Stub { dataDir = aInfo.applicationInfo.dataDir; nonLocalizedLabel = aInfo.nonLocalizedLabel; labelRes = aInfo.labelRes; + if (nonLocalizedLabel == null && labelRes == 0) { + ApplicationInfo app = aInfo.applicationInfo; + nonLocalizedLabel = app.nonLocalizedLabel; + labelRes = app.labelRes; + } icon = aInfo.getIconResource(); theme = aInfo.getThemeResource(); if ((aInfo.flags&ActivityInfo.FLAG_MULTIPROCESS) != 0 @@ -210,6 +223,29 @@ class HistoryRecord extends IApplicationToken.Stub { && !ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsTranslucent, false); + if (!_componentSpecified || _launchedFromUid == Process.myUid() + || _launchedFromUid == 0) { + // If we know the system has determined the component, then + // we can consider this to be a home activity... + if (Intent.ACTION_MAIN.equals(_intent.getAction()) && + _intent.hasCategory(Intent.CATEGORY_HOME) && + _intent.getCategories().size() == 1 && + _intent.getData() == null && + _intent.getType() == null && + (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && + !"android".equals(realActivity.getClassName())) { + // This sure looks like a home activity! + // Note the last check is so we don't count the resolver + // activity as being home... really, we don't care about + // doing anything special with something that comes from + // the core framework package. + isHomeActivity = true; + } else { + isHomeActivity = false; + } + } else { + isHomeActivity = false; + } } else { realActivity = null; taskAffinity = null; @@ -220,6 +256,7 @@ class HistoryRecord extends IApplicationToken.Stub { processName = null; packageName = null; fullscreen = true; + isHomeActivity = false; } } diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java new file mode 100755 index 0000000..3922f39 --- /dev/null +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2006-2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import com.android.internal.app.IUsageStats; +import android.content.ComponentName; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import com.android.internal.os.PkgUsageStats; +import android.os.Parcel; +import android.os.Process; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.util.Log; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This service collects the statistics associated with usage + * of various components, like when a particular package is launched or + * paused and aggregates events like number of time a component is launched + * total duration of a component launch. + */ +public final class UsageStatsService extends IUsageStats.Stub { + public static final String SERVICE_NAME = "usagestats"; + private static final boolean localLOGV = false; + private static final String TAG = "UsageStats"; + static IUsageStats sService; + private Context mContext; + // structure used to maintain statistics since the last checkin. + final private Map<String, PkgUsageStatsExtended> mStats; + // Lock to update package stats. Methods suffixed by SLOCK should invoked with + // this lock held + final Object mStatsLock; + // Lock to write to file. Methods suffixed by FLOCK should invoked with + // this lock held. + final Object mFileLock; + // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks + private String mResumedPkg; + private File mFile; + //private File mBackupFile; + private long mLastWriteRealTime; + private int _FILE_WRITE_INTERVAL = 30*60*1000; //ms + private static final String _PREFIX_DELIMIT="."; + private String mFilePrefix; + private Calendar mCal; + private static final int _MAX_NUM_FILES = 10; + private long mLastTime; + + private class PkgUsageStatsExtended { + int mLaunchCount; + long mUsageTime; + long mPausedTime; + long mResumedTime; + + PkgUsageStatsExtended() { + mLaunchCount = 0; + mUsageTime = 0; + } + void updateResume() { + mLaunchCount ++; + mResumedTime = SystemClock.elapsedRealtime(); + } + void updatePause() { + mPausedTime = SystemClock.elapsedRealtime(); + mUsageTime += (mPausedTime - mResumedTime); + } + void clear() { + mLaunchCount = 0; + mUsageTime = 0; + } + } + + UsageStatsService(String fileName) { + mStats = new HashMap<String, PkgUsageStatsExtended>(); + mStatsLock = new Object(); + mFileLock = new Object(); + mFilePrefix = fileName; + mCal = Calendar.getInstance(); + // Update current stats which are binned by date + String uFileName = getCurrentDateStr(mFilePrefix); + mFile = new File(uFileName); + readStatsFromFile(); + mLastWriteRealTime = SystemClock.elapsedRealtime(); + mLastTime = new Date().getTime(); + } + + /* + * Utility method to convert date into string. + */ + private String getCurrentDateStr(String prefix) { + mCal.setTime(new Date()); + StringBuilder sb = new StringBuilder(); + if (prefix != null) { + sb.append(prefix); + sb.append("."); + } + int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1; + if (mm < 10) { + sb.append("0"); + } + sb.append(mm); + int dd = mCal.get(Calendar.DAY_OF_MONTH); + if (dd < 10) { + sb.append("0"); + } + sb.append(dd); + sb.append(mCal.get(Calendar.YEAR)); + return sb.toString(); + } + + private Parcel getParcelForFile(File file) throws IOException { + FileInputStream stream = new FileInputStream(file); + byte[] raw = readFully(stream); + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, raw.length); + in.setDataPosition(0); + stream.close(); + return in; + } + + private void readStatsFromFile() { + File newFile = mFile; + synchronized (mFileLock) { + try { + if (newFile.exists()) { + readStatsFLOCK(newFile); + } else { + // Check for file limit before creating a new file + checkFileLimitFLOCK(); + newFile.createNewFile(); + } + } catch (IOException e) { + Log.w(TAG,"Error : " + e + " reading data from file:" + newFile); + } + } + } + + private void readStatsFLOCK(File file) throws IOException { + Parcel in = getParcelForFile(file); + while (in.dataAvail() > 0) { + String pkgName = in.readString(); + PkgUsageStatsExtended pus = new PkgUsageStatsExtended(); + pus.mLaunchCount = in.readInt(); + pus.mUsageTime = in.readLong(); + synchronized (mStatsLock) { + mStats.put(pkgName, pus); + } + } + } + + private ArrayList<String> getUsageStatsFileListFLOCK() { + File dir = getUsageFilesDir(); + if (dir == null) { + Log.w(TAG, "Couldnt find writable directory for usage stats file"); + return null; + } + // Check if there are too many files in the system and delete older files + String fList[] = dir.list(); + if (fList == null) { + return null; + } + File pre = new File(mFilePrefix); + String filePrefix = pre.getName(); + // file name followed by dot + int prefixLen = filePrefix.length()+1; + ArrayList<String> fileList = new ArrayList<String>(); + for (String file : fList) { + int index = file.indexOf(filePrefix); + if (index == -1) { + continue; + } + if (file.endsWith(".bak")) { + continue; + } + fileList.add(file); + } + return fileList; + } + + private File getUsageFilesDir() { + if (mFilePrefix == null) { + return null; + } + File pre = new File(mFilePrefix); + return new File(pre.getParent()); + } + + private void checkFileLimitFLOCK() { + File dir = getUsageFilesDir(); + if (dir == null) { + Log.w(TAG, "Couldnt find writable directory for usage stats file"); + return; + } + // Get all usage stats output files + ArrayList<String> fileList = getUsageStatsFileListFLOCK(); + if (fileList == null) { + // Strange but we dont have to delete any thing + return; + } + int count = fileList.size(); + if (count <= _MAX_NUM_FILES) { + return; + } + // Sort files + Collections.sort(fileList); + count -= _MAX_NUM_FILES; + // Delete older files + for (int i = 0; i < count; i++) { + String fileName = fileList.get(i); + File file = new File(dir, fileName); + Log.i(TAG, "Deleting file : "+fileName); + file.delete(); + } + } + + private void writeStatsToFile() { + synchronized (mFileLock) { + long currTime = new Date().getTime(); + boolean dayChanged = ((currTime - mLastTime) >= (24*60*60*1000)); + long currRealTime = SystemClock.elapsedRealtime(); + if (((currRealTime-mLastWriteRealTime) < _FILE_WRITE_INTERVAL) && + (!dayChanged)) { + // wait till the next update + return; + } + // Get the most recent file + String todayStr = getCurrentDateStr(mFilePrefix); + // Copy current file to back up + File backupFile = new File(mFile.getPath() + ".bak"); + mFile.renameTo(backupFile); + try { + checkFileLimitFLOCK(); + mFile.createNewFile(); + // Write mStats to file + writeStatsFLOCK(); + mLastWriteRealTime = currRealTime; + mLastTime = currTime; + if (dayChanged) { + // clear stats + synchronized (mStats) { + mStats.clear(); + } + mFile = new File(todayStr); + } + // Delete the backup file + if (backupFile != null) { + backupFile.delete(); + } + } catch (IOException e) { + Log.w(TAG, "Failed writing stats to file:" + mFile); + if (backupFile != null) { + backupFile.renameTo(mFile); + } + } + } + } + + private void writeStatsFLOCK() throws IOException { + FileOutputStream stream = new FileOutputStream(mFile); + Parcel out = Parcel.obtain(); + writeStatsToParcelFLOCK(out); + stream.write(out.marshall()); + out.recycle(); + stream.flush(); + stream.close(); + } + + private void writeStatsToParcelFLOCK(Parcel out) { + synchronized (mStatsLock) { + Set<String> keys = mStats.keySet(); + for (String key : keys) { + PkgUsageStatsExtended pus = mStats.get(key); + out.writeString(key); + out.writeInt(pus.mLaunchCount); + out.writeLong(pus.mUsageTime); + } + } + } + + public void publish(Context context) { + mContext = context; + ServiceManager.addService(SERVICE_NAME, asBinder()); + } + + public static IUsageStats getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService(SERVICE_NAME); + sService = asInterface(b); + return sService; + } + + public void noteResumeComponent(ComponentName componentName) { + enforceCallingPermission(); + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) { + // Moving across activities in same package. just return + return; + } + if (localLOGV) Log.i(TAG, "started component:"+pkgName); + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + pus = new PkgUsageStatsExtended(); + mStats.put(pkgName, pus); + } + pus.updateResume(); + } + mResumedPkg = pkgName; + } + + public void notePauseComponent(ComponentName componentName) { + enforceCallingPermission(); + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) { + Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused"); + return; + } + if (localLOGV) Log.i(TAG, "paused component:"+pkgName); + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + // Weird some error here + Log.w(TAG, "No package stats for pkg:"+pkgName); + return; + } + pus.updatePause(); + } + // Persist data to file + writeStatsToFile(); + } + + public void enforceCallingPermission() { + if (Binder.getCallingPid() == Process.myPid()) { + return; + } + mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, + Binder.getCallingPid(), Binder.getCallingUid(), null); + } + + public PkgUsageStats getPkgUsageStats(ComponentName componentName) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS, null); + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return null; + } + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + return null; + } + return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime); + } + } + + public PkgUsageStats[] getAllPkgUsageStats() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS, null); + synchronized (mStatsLock) { + Set<String> keys = mStats.keySet(); + int size = keys.size(); + if (size <= 0) { + return null; + } + PkgUsageStats retArr[] = new PkgUsageStats[size]; + int i = 0; + for (String key: keys) { + PkgUsageStatsExtended pus = mStats.get(key); + retArr[i] = new PkgUsageStats(key, pus.mLaunchCount, pus.mUsageTime); + i++; + } + return retArr; + } + } + + static byte[] readFully(FileInputStream stream) throws java.io.IOException { + int pos = 0; + int avail = stream.available(); + byte[] data = new byte[avail]; + while (true) { + int amt = stream.read(data, pos, data.length-pos); + if (amt <= 0) { + return data; + } + pos += amt; + avail = stream.available(); + if (avail > data.length-pos) { + byte[] newData = new byte[pos+avail]; + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } + + private void collectDumpInfoFLOCK(PrintWriter pw, String[] args) { + List<String> fileList = getUsageStatsFileListFLOCK(); + if (fileList == null) { + return; + } + final boolean isCheckinRequest = scanArgs(args, "-c"); + Collections.sort(fileList); + File usageFile = new File(mFilePrefix); + String dirName = usageFile.getParent(); + File dir = new File(dirName); + String filePrefix = usageFile.getName(); + // file name followed by dot + int prefixLen = filePrefix.length()+1; + String todayStr = getCurrentDateStr(null); + for (String file : fileList) { + File dFile = new File(dir, file); + String dateStr = file.substring(prefixLen); + try { + Parcel in = getParcelForFile(dFile); + collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCheckinRequest); + if (isCheckinRequest && !todayStr.equalsIgnoreCase(dateStr)) { + // Delete old file after collecting info only for checkin requests + dFile.delete(); + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file); + return; + } catch (IOException e) { + Log.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file); + } + } + } + + private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw, + String date, boolean isCheckinRequest) { + StringBuilder sb = new StringBuilder(); + sb.append("Date:"); + sb.append(date); + boolean first = true; + while (in.dataAvail() > 0) { + String pkgName = in.readString(); + int launchCount = in.readInt(); + long usageTime = in.readLong(); + if (isCheckinRequest) { + if (!first) { + sb.append(","); + } + sb.append(pkgName); + sb.append(","); + sb.append(launchCount); + sb.append(","); + sb.append(usageTime); + sb.append("ms"); + } else { + if (first) { + sb.append("\n"); + } + sb.append("pkg="); + sb.append(pkgName); + sb.append(", launchCount="); + sb.append(launchCount); + sb.append(", usageTime="); + sb.append(usageTime); + sb.append(" ms\n"); + } + first = false; + } + pw.write(sb.toString()); + } + + /** + * Searches array of arguments for the specified string + * @param args array of argument strings + * @param value value to search for + * @return true if the value is contained in the array + */ + private static boolean scanArgs(String[] args, String value) { + if (args != null) { + for (String arg : args) { + if (value.equals(arg)) { + return true; + } + } + } + return false; + } + + @Override + /* + * The data persisted to file is parsed and the stats are computed. + */ + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (mFileLock) { + collectDumpInfoFLOCK(pw, args); + } + } + +} diff --git a/services/java/com/android/server/status/AnimatedImageView.java b/services/java/com/android/server/status/AnimatedImageView.java index 36492da..cd581c4 100644 --- a/services/java/com/android/server/status/AnimatedImageView.java +++ b/services/java/com/android/server/status/AnimatedImageView.java @@ -42,6 +42,7 @@ public class AnimatedImageView extends ImageView { } @Override + @android.view.RemotableViewMethod public void setImageResource(int resid) { super.setImageResource(resid); updateAnim(); diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index 00ff7be..8433227 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -72,6 +72,11 @@ public class StatusBarPolicy { 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 Context mContext; private StatusBarService mService; private Handler mHandler = new StatusBarHandler(); @@ -88,7 +93,7 @@ public class StatusBarPolicy { private boolean mBatteryPlugged; private int mBatteryLevel; private int mBatteryThreshold = 0; // index into mBatteryThresholds - private int[] mBatteryThresholds = new int[] { 15, -1 }; + private int[] mBatteryThresholds = new int[] { 20, 15, -1 }; private AlertDialog mLowBatteryDialog; private TextView mBatteryLevelTextView; private View mBatteryView; @@ -157,6 +162,7 @@ public class StatusBarPolicy { private IconData mBluetoothData; private int mBluetoothHeadsetState; private int mBluetoothA2dpState; + private boolean mBluetoothEnabled; // wifi private static final int[] sWifiSignalImages = new int[] { @@ -233,7 +239,7 @@ public class StatusBarPolicy { } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) || action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) { - updateVolume(intent); + updateVolume(); } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { updateSimState(intent); @@ -286,7 +292,16 @@ public class StatusBarPolicy { mBluetoothData = IconData.makeIcon("bluetooth", null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0); mBluetoothIcon = service.addIcon(mBluetoothData, null); - updateBluetooth(null); + BluetoothDevice bluetooth = + (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE); + if (bluetooth != null) { + mBluetoothEnabled = bluetooth.isEnabled(); + } else { + mBluetoothEnabled = false; + } + mBluetoothA2dpState = BluetoothA2dp.STATE_DISCONNECTED; + mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED; + mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled); // Gps status mGpsEnabledIconData = IconData.makeIcon("gps", @@ -316,6 +331,7 @@ public class StatusBarPolicy { null, com.android.internal.R.drawable.stat_sys_ringer_silent, 0, 0); mVolumeIcon = service.addIcon(mVolumeData, null); service.setIconVisibility(mVolumeIcon, false); + updateVolume(); IntentFilter filter = new IntentFilter(); @@ -419,9 +435,14 @@ public class StatusBarPolicy { } } */ + if (false) { + Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level + + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold); + } if (!plugged - && ((oldPlugged && level <= mBatteryThresholds[0]) - || (mBatteryThreshold > oldThreshold))) { + && ((oldPlugged && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING]) + || (mBatteryThreshold > oldThreshold + && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) { // Broadcast the low battery warning mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW)); @@ -438,6 +459,13 @@ public class StatusBarPolicy { mBatteryShowLowOnEndCall = true; } } + } else if (mBatteryThreshold == BATTERY_THRESHOLD_CLOSE_WARNING) { + if (SHOW_LOW_BATTERY_WARNING) { + if (mLowBatteryDialog != null) { + mLowBatteryDialog.dismiss(); + mBatteryShowLowOnEndCall = false; + } + } } } @@ -745,9 +773,7 @@ public class StatusBarPolicy { } } - private final void updateVolume(Intent intent) { - // This can be called from two different received intents, so don't use extras. - + private final void updateVolume() { AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); final int ringerMode = audioManager.getRingerMode(); final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT || @@ -767,30 +793,17 @@ public class StatusBarPolicy { } private final void updateBluetooth(Intent intent) { - boolean visible = false; - if (intent == null) { // Initialize - BluetoothDevice bluetooth = - (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE); - if (bluetooth != null) { - visible = bluetooth.isEnabled(); - } - mService.setIconVisibility(mBluetoothIcon, visible); - return; - } - int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth; - String action = intent.getAction(); + String action = intent.getAction(); if (action.equals(BluetoothIntent.DISABLED_ACTION)) { - visible = false; + mBluetoothEnabled = false; } else if (action.equals(BluetoothIntent.ENABLED_ACTION)) { - visible = true; + mBluetoothEnabled = true; } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) { - visible = true; mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, BluetoothHeadset.STATE_ERROR); } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) { - visible = true; mBluetoothA2dpState = intent.getIntExtra(BluetoothA2dp.SINK_STATE, BluetoothA2dp.STATE_DISCONNECTED); } else { @@ -805,7 +818,7 @@ public class StatusBarPolicy { mBluetoothData.iconId = iconId; mService.updateIcon(mBluetoothIcon, mBluetoothData, null); - mService.setIconVisibility(mBluetoothIcon, visible); + mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled); } private final void updateWifi(Intent intent) { diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 3b6e354..5442e1d 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -27,6 +27,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; @@ -175,11 +176,11 @@ public class StatusBarService extends IStatusBar.Stub WindowManager.LayoutParams mExpandedParams; ScrollView mScrollView; View mNotificationLinearLayout; - View mOngoingTitle; + TextView mOngoingTitle; LinearLayout mOngoingItems; - View mLatestTitle; + TextView mLatestTitle; LinearLayout mLatestItems; - View mNoNotificationsTitle; + TextView mNoNotificationsTitle; TextView mSpnLabel; TextView mPlmnLabel; TextView mClearButton; @@ -269,11 +270,11 @@ public class StatusBarService extends IStatusBar.Stub mExpandedDialog = new ExpandedDialog(context); mExpandedView = expanded; - mOngoingTitle = expanded.findViewById(R.id.ongoingTitle); + mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle); mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems); - mLatestTitle = expanded.findViewById(R.id.latestTitle); + mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle); mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems); - mNoNotificationsTitle = expanded.findViewById(R.id.noNotificationsTitle); + mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle); mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button); mClearButton.setOnClickListener(mClearButtonListener); mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel); @@ -322,9 +323,11 @@ public class StatusBarService extends IStatusBar.Stub } public void systemReady() { + final StatusBarView view = mStatusBarView; WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, - 25, + view.getContext().getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height), WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING, @@ -333,7 +336,7 @@ public class StatusBarService extends IStatusBar.Stub lp.setTitle("StatusBar"); lp.windowAnimations = R.style.Animation_StatusBar; - WindowManagerImpl.getDefault().addView(mStatusBarView, lp); + WindowManagerImpl.getDefault().addView(view, lp); } // ================================================================================ @@ -1324,80 +1327,89 @@ public class StatusBarService extends IStatusBar.Stub } protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump StatusBar from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + synchronized (mQueue) { - pw.println("mExpanded=" + mExpanded + pw.println("Current Status Bar state:"); + pw.println(" mExpanded=" + mExpanded + ", mExpandedVisible=" + mExpandedVisible); - pw.println("mTicking=" + mTicking); - pw.println("mTracking=" + mTracking); - pw.println("mAnimating=" + mAnimating + pw.println(" mTicking=" + mTicking); + pw.println(" mTracking=" + mTracking); + pw.println(" mAnimating=" + mAnimating + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel + ", mAnimAccel=" + mAnimAccel); - pw.println("mCurAnimationTime=" + mCurAnimationTime + pw.println(" mCurAnimationTime=" + mCurAnimationTime + " mAnimLastTime=" + mAnimLastTime); - pw.println("mDisplayHeight=" + mDisplayHeight + pw.println(" mDisplayHeight=" + mDisplayHeight + " mAnimatingReveal=" + mAnimatingReveal + " mViewDelta=" + mViewDelta); - pw.println("mDisplayHeight=" + mDisplayHeight); + pw.println(" mDisplayHeight=" + mDisplayHeight); final int N = mQueue.size(); - pw.println("mQueue.size=" + N); + pw.println(" mQueue.size=" + N); for (int i=0; i<N; i++) { PendingOp op = mQueue.get(i); - pw.println(" [" + i + "] key=" + op.key + " code=" + op.code + " visible=" + pw.println(" [" + i + "] key=" + op.key + " code=" + op.code + " visible=" + op.visible); - pw.println(" iconData=" + op.iconData); - pw.println(" notificationData=" + op.notificationData); + pw.println(" iconData=" + op.iconData); + pw.println(" notificationData=" + op.notificationData); } - pw.println("mExpandedParams: " + mExpandedParams); - pw.println("mExpandedView: " + viewInfo(mExpandedView)); - pw.println("mExpandedDialog: " + mExpandedDialog); - pw.println("mTrackingParams: " + mTrackingParams); - pw.println("mTrackingView: " + viewInfo(mTrackingView)); - pw.println("mOngoingTitle: " + viewInfo(mOngoingTitle)); - pw.println("mOngoingItems: " + viewInfo(mOngoingItems)); - pw.println("mLatestTitle: " + viewInfo(mLatestTitle)); - pw.println("mLatestItems: " + viewInfo(mLatestItems)); - pw.println("mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle)); - pw.println("mCloseView: " + viewInfo(mCloseView)); - pw.println("mTickerView: " + viewInfo(mTickerView)); - pw.println("mScrollView: " + viewInfo(mScrollView) + pw.println(" mExpandedParams: " + mExpandedParams); + pw.println(" mExpandedView: " + viewInfo(mExpandedView)); + pw.println(" mExpandedDialog: " + mExpandedDialog); + pw.println(" mTrackingParams: " + mTrackingParams); + pw.println(" mTrackingView: " + viewInfo(mTrackingView)); + pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle)); + pw.println(" mOngoingItems: " + viewInfo(mOngoingItems)); + pw.println(" mLatestTitle: " + viewInfo(mLatestTitle)); + pw.println(" mLatestItems: " + viewInfo(mLatestItems)); + pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle)); + pw.println(" mCloseView: " + viewInfo(mCloseView)); + pw.println(" mTickerView: " + viewInfo(mTickerView)); + pw.println(" mScrollView: " + viewInfo(mScrollView) + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY()); pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout)); } synchronized (mIconMap) { final int N = mIconMap.size(); - pw.println("mIconMap.size=" + N); + pw.println(" mIconMap.size=" + N); Set<IBinder> keys = mIconMap.keySet(); int i=0; for (IBinder key: keys) { StatusBarIcon icon = mIconMap.get(key); - pw.println(" [" + i + "] key=" + key); - pw.println(" data=" + icon.mData); + pw.println(" [" + i + "] key=" + key); + pw.println(" data=" + icon.mData); i++; } } synchronized (mNotificationData) { int N = mNotificationData.ongoingCount(); - pw.println("ongoingCount.size=" + N); + pw.println(" ongoingCount.size=" + N); for (int i=0; i<N; i++) { StatusBarNotification n = mNotificationData.getOngoing(i); - pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); - pw.println(" data=" + n.data); + pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); + pw.println(" data=" + n.data); } N = mNotificationData.latestCount(); - pw.println("ongoingCount.size=" + N); + pw.println(" ongoingCount.size=" + N); for (int i=0; i<N; i++) { StatusBarNotification n = mNotificationData.getLatest(i); - pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); - pw.println(" data=" + n.data); + pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); + pw.println(" data=" + n.data); } } synchronized (mDisableRecords) { final int N = mDisableRecords.size(); - pw.println("mDisableRecords.size=" + N + pw.println(" mDisableRecords.size=" + N + " mDisabled=0x" + Integer.toHexString(mDisabled)); for (int i=0; i<N; i++) { DisableRecord tok = mDisableRecords.get(i); - pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what) + pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what) + " pkg=" + tok.pkg + " token=" + tok.token); } } @@ -1693,6 +1705,9 @@ public class StatusBarService extends IStatusBar.Stub */ void updateResources() { mClearButton.setText(mContext.getText(R.string.status_bar_clear_all_button)); + mOngoingTitle.setText(mContext.getText(R.string.status_bar_ongoing_events_title)); + mLatestTitle.setText(mContext.getText(R.string.status_bar_latest_events_title)); + mNoNotificationsTitle.setText(mContext.getText(R.string.status_bar_no_notifications_title)); Log.d(TAG, "updateResources"); } |