summaryrefslogtreecommitdiffstats
path: root/services/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java')
-rw-r--r--services/java/Android.mk17
-rw-r--r--services/java/com/android/server/AlarmManagerService.java101
-rw-r--r--services/java/com/android/server/AppWidgetService.java1148
-rw-r--r--services/java/com/android/server/BatteryService.java158
-rw-r--r--services/java/com/android/server/ConnectivityService.java29
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java58
-rw-r--r--services/java/com/android/server/FallbackCheckinService.java75
-rw-r--r--services/java/com/android/server/GadgetService.java292
-rwxr-xr-xservices/java/com/android/server/HardwareService.java82
-rw-r--r--services/java/com/android/server/HeadsetObserver.java48
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java763
-rw-r--r--services/java/com/android/server/KeyInputQueue.java1
-rw-r--r--services/java/com/android/server/LocationManagerService.java1604
-rw-r--r--services/java/com/android/server/MountListener.java108
-rw-r--r--services/java/com/android/server/MountService.java433
-rw-r--r--services/java/com/android/server/NetStatService.java31
-rw-r--r--services/java/com/android/server/NotificationManagerService.java15
-rw-r--r--services/java/com/android/server/PackageManagerService.java294
-rw-r--r--services/java/com/android/server/PowerManagerService.java119
-rw-r--r--services/java/com/android/server/SensorService.java17
-rw-r--r--services/java/com/android/server/SystemServer.java100
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java62
-rw-r--r--services/java/com/android/server/WifiService.java130
-rw-r--r--services/java/com/android/server/WindowManagerService.java812
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java542
-rw-r--r--services/java/com/android/server/am/BaseErrorDialog.java2
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java79
-rw-r--r--services/java/com/android/server/am/HistoryRecord.java43
-rwxr-xr-xservices/java/com/android/server/am/UsageStatsService.java532
-rw-r--r--services/java/com/android/server/status/AnimatedImageView.java1
-rw-r--r--services/java/com/android/server/status/StatusBarPolicy.java63
-rw-r--r--services/java/com/android/server/status/StatusBarService.java101
32 files changed, 5952 insertions, 1908 deletions
diff --git a/services/java/Android.mk b/services/java/Android.mk
new file mode 100644
index 0000000..5e912d6
--- /dev/null
+++ b/services/java/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+# the library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(call all-subdir-java-files)
+
+LOCAL_MODULE:= services
+
+LOCAL_JAVA_LIBRARIES := android.policy
+
+include $(BUILD_JAVA_LIBRARY)
+
+include $(BUILD_DROIDDOC)
+
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 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");
}